0afa8e065e
git-subtree-dir: contrib/libfido2 git-subtree-mainline:d586c978b9
git-subtree-split:a58dee945a
343 lines
8.2 KiB
C
343 lines
8.2 KiB
C
/*
|
|
* Copyright (c) 2018 Yubico AB. All rights reserved.
|
|
* Use of this source code is governed by a BSD-style
|
|
* license that can be found in the LICENSE file.
|
|
*/
|
|
|
|
#include <fido.h>
|
|
#include <fido/es256.h>
|
|
#include <fido/rs256.h>
|
|
#include <fido/eddsa.h>
|
|
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "../openbsd-compat/openbsd-compat.h"
|
|
#include "extern.h"
|
|
|
|
static const unsigned char cdh[32] = {
|
|
0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7,
|
|
0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56,
|
|
0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52,
|
|
0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76,
|
|
};
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
fprintf(stderr, "usage: assert [-t ecdsa|rsa|eddsa] [-a cred_id] "
|
|
"[-h hmac_secret] [-s hmac_salt] [-P pin] [-T seconds] "
|
|
"[-b blobkey] [-puv] <pubkey> <device>\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
static void
|
|
verify_assert(int type, const unsigned char *authdata_ptr, size_t authdata_len,
|
|
const unsigned char *sig_ptr, size_t sig_len, bool up, bool uv, int ext,
|
|
const char *key)
|
|
{
|
|
fido_assert_t *assert = NULL;
|
|
EC_KEY *ec = NULL;
|
|
RSA *rsa = NULL;
|
|
EVP_PKEY *eddsa = NULL;
|
|
es256_pk_t *es256_pk = NULL;
|
|
rs256_pk_t *rs256_pk = NULL;
|
|
eddsa_pk_t *eddsa_pk = NULL;
|
|
void *pk;
|
|
int r;
|
|
|
|
/* credential pubkey */
|
|
switch (type) {
|
|
case COSE_ES256:
|
|
if ((ec = read_ec_pubkey(key)) == NULL)
|
|
errx(1, "read_ec_pubkey");
|
|
|
|
if ((es256_pk = es256_pk_new()) == NULL)
|
|
errx(1, "es256_pk_new");
|
|
|
|
if (es256_pk_from_EC_KEY(es256_pk, ec) != FIDO_OK)
|
|
errx(1, "es256_pk_from_EC_KEY");
|
|
|
|
pk = es256_pk;
|
|
EC_KEY_free(ec);
|
|
ec = NULL;
|
|
|
|
break;
|
|
case COSE_RS256:
|
|
if ((rsa = read_rsa_pubkey(key)) == NULL)
|
|
errx(1, "read_rsa_pubkey");
|
|
|
|
if ((rs256_pk = rs256_pk_new()) == NULL)
|
|
errx(1, "rs256_pk_new");
|
|
|
|
if (rs256_pk_from_RSA(rs256_pk, rsa) != FIDO_OK)
|
|
errx(1, "rs256_pk_from_RSA");
|
|
|
|
pk = rs256_pk;
|
|
RSA_free(rsa);
|
|
rsa = NULL;
|
|
|
|
break;
|
|
case COSE_EDDSA:
|
|
if ((eddsa = read_eddsa_pubkey(key)) == NULL)
|
|
errx(1, "read_eddsa_pubkey");
|
|
|
|
if ((eddsa_pk = eddsa_pk_new()) == NULL)
|
|
errx(1, "eddsa_pk_new");
|
|
|
|
if (eddsa_pk_from_EVP_PKEY(eddsa_pk, eddsa) != FIDO_OK)
|
|
errx(1, "eddsa_pk_from_EVP_PKEY");
|
|
|
|
pk = eddsa_pk;
|
|
EVP_PKEY_free(eddsa);
|
|
eddsa = NULL;
|
|
|
|
break;
|
|
default:
|
|
errx(1, "unknown credential type %d", type);
|
|
}
|
|
|
|
if ((assert = fido_assert_new()) == NULL)
|
|
errx(1, "fido_assert_new");
|
|
|
|
/* client data hash */
|
|
r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh));
|
|
if (r != FIDO_OK)
|
|
errx(1, "fido_assert_set_clientdata_hash: %s (0x%x)",
|
|
fido_strerr(r), r);
|
|
|
|
/* relying party */
|
|
r = fido_assert_set_rp(assert, "localhost");
|
|
if (r != FIDO_OK)
|
|
errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r);
|
|
|
|
/* authdata */
|
|
r = fido_assert_set_count(assert, 1);
|
|
if (r != FIDO_OK)
|
|
errx(1, "fido_assert_set_count: %s (0x%x)", fido_strerr(r), r);
|
|
r = fido_assert_set_authdata(assert, 0, authdata_ptr, authdata_len);
|
|
if (r != FIDO_OK)
|
|
errx(1, "fido_assert_set_authdata: %s (0x%x)", fido_strerr(r), r);
|
|
|
|
/* extension */
|
|
r = fido_assert_set_extensions(assert, ext);
|
|
if (r != FIDO_OK)
|
|
errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r),
|
|
r);
|
|
|
|
/* user presence */
|
|
if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
|
|
errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r);
|
|
|
|
/* user verification */
|
|
if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
|
|
errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r);
|
|
|
|
/* sig */
|
|
r = fido_assert_set_sig(assert, 0, sig_ptr, sig_len);
|
|
if (r != FIDO_OK)
|
|
errx(1, "fido_assert_set_sig: %s (0x%x)", fido_strerr(r), r);
|
|
|
|
r = fido_assert_verify(assert, 0, type, pk);
|
|
if (r != FIDO_OK)
|
|
errx(1, "fido_assert_verify: %s (0x%x)", fido_strerr(r), r);
|
|
|
|
es256_pk_free(&es256_pk);
|
|
rs256_pk_free(&rs256_pk);
|
|
eddsa_pk_free(&eddsa_pk);
|
|
|
|
fido_assert_free(&assert);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
bool up = false;
|
|
bool uv = false;
|
|
bool u2f = false;
|
|
fido_dev_t *dev = NULL;
|
|
fido_assert_t *assert = NULL;
|
|
const char *pin = NULL;
|
|
const char *blobkey_out = NULL;
|
|
const char *hmac_out = NULL;
|
|
unsigned char *body = NULL;
|
|
long long seconds = 0;
|
|
size_t len;
|
|
int type = COSE_ES256;
|
|
int ext = 0;
|
|
int ch;
|
|
int r;
|
|
|
|
if ((assert = fido_assert_new()) == NULL)
|
|
errx(1, "fido_assert_new");
|
|
|
|
while ((ch = getopt(argc, argv, "P:T:a:b:h:ps:t:uv")) != -1) {
|
|
switch (ch) {
|
|
case 'P':
|
|
pin = optarg;
|
|
break;
|
|
case 'T':
|
|
#ifndef SIGNAL_EXAMPLE
|
|
(void)seconds;
|
|
errx(1, "-T not supported");
|
|
#else
|
|
if (base10(optarg, &seconds) < 0)
|
|
errx(1, "base10: %s", optarg);
|
|
if (seconds <= 0 || seconds > 30)
|
|
errx(1, "-T: %s must be in (0,30]", optarg);
|
|
break;
|
|
#endif
|
|
case 'a':
|
|
if (read_blob(optarg, &body, &len) < 0)
|
|
errx(1, "read_blob: %s", optarg);
|
|
if ((r = fido_assert_allow_cred(assert, body,
|
|
len)) != FIDO_OK)
|
|
errx(1, "fido_assert_allow_cred: %s (0x%x)",
|
|
fido_strerr(r), r);
|
|
free(body);
|
|
body = NULL;
|
|
break;
|
|
case 'b':
|
|
ext |= FIDO_EXT_LARGEBLOB_KEY;
|
|
blobkey_out = optarg;
|
|
break;
|
|
case 'h':
|
|
hmac_out = optarg;
|
|
break;
|
|
case 'p':
|
|
up = true;
|
|
break;
|
|
case 's':
|
|
ext |= FIDO_EXT_HMAC_SECRET;
|
|
if (read_blob(optarg, &body, &len) < 0)
|
|
errx(1, "read_blob: %s", optarg);
|
|
if ((r = fido_assert_set_hmac_salt(assert, body,
|
|
len)) != FIDO_OK)
|
|
errx(1, "fido_assert_set_hmac_salt: %s (0x%x)",
|
|
fido_strerr(r), r);
|
|
free(body);
|
|
body = NULL;
|
|
break;
|
|
case 't':
|
|
if (strcmp(optarg, "ecdsa") == 0)
|
|
type = COSE_ES256;
|
|
else if (strcmp(optarg, "rsa") == 0)
|
|
type = COSE_RS256;
|
|
else if (strcmp(optarg, "eddsa") == 0)
|
|
type = COSE_EDDSA;
|
|
else
|
|
errx(1, "unknown type %s", optarg);
|
|
break;
|
|
case 'u':
|
|
u2f = true;
|
|
break;
|
|
case 'v':
|
|
uv = true;
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc != 2)
|
|
usage();
|
|
|
|
fido_init(0);
|
|
|
|
if ((dev = fido_dev_new()) == NULL)
|
|
errx(1, "fido_dev_new");
|
|
|
|
r = fido_dev_open(dev, argv[1]);
|
|
if (r != FIDO_OK)
|
|
errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
|
|
if (u2f)
|
|
fido_dev_force_u2f(dev);
|
|
|
|
/* client data hash */
|
|
r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh));
|
|
if (r != FIDO_OK)
|
|
errx(1, "fido_assert_set_clientdata_hash: %s (0x%x)",
|
|
fido_strerr(r), r);
|
|
|
|
/* relying party */
|
|
r = fido_assert_set_rp(assert, "localhost");
|
|
if (r != FIDO_OK)
|
|
errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r);
|
|
|
|
/* extensions */
|
|
r = fido_assert_set_extensions(assert, ext);
|
|
if (r != FIDO_OK)
|
|
errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r),
|
|
r);
|
|
|
|
/* user presence */
|
|
if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
|
|
errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r);
|
|
|
|
/* user verification */
|
|
if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
|
|
errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r);
|
|
|
|
#ifdef SIGNAL_EXAMPLE
|
|
prepare_signal_handler(SIGINT);
|
|
if (seconds) {
|
|
prepare_signal_handler(SIGALRM);
|
|
alarm((unsigned)seconds);
|
|
}
|
|
#endif
|
|
|
|
r = fido_dev_get_assert(dev, assert, pin);
|
|
if (r != FIDO_OK) {
|
|
#ifdef SIGNAL_EXAMPLE
|
|
if (got_signal)
|
|
fido_dev_cancel(dev);
|
|
#endif
|
|
errx(1, "fido_dev_get_assert: %s (0x%x)", fido_strerr(r), r);
|
|
}
|
|
|
|
r = fido_dev_close(dev);
|
|
if (r != FIDO_OK)
|
|
errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
|
|
|
|
fido_dev_free(&dev);
|
|
|
|
if (fido_assert_count(assert) != 1)
|
|
errx(1, "fido_assert_count: %d signatures returned",
|
|
(int)fido_assert_count(assert));
|
|
|
|
/* when verifying, pin implies uv */
|
|
if (pin)
|
|
uv = true;
|
|
|
|
verify_assert(type, fido_assert_authdata_ptr(assert, 0),
|
|
fido_assert_authdata_len(assert, 0), fido_assert_sig_ptr(assert, 0),
|
|
fido_assert_sig_len(assert, 0), up, uv, ext, argv[0]);
|
|
|
|
if (hmac_out != NULL) {
|
|
/* extract the hmac secret */
|
|
if (write_blob(hmac_out, fido_assert_hmac_secret_ptr(assert, 0),
|
|
fido_assert_hmac_secret_len(assert, 0)) < 0)
|
|
errx(1, "write_blob");
|
|
}
|
|
|
|
if (blobkey_out != NULL) {
|
|
/* extract the hmac secret */
|
|
if (write_blob(blobkey_out,
|
|
fido_assert_largeblob_key_ptr(assert, 0),
|
|
fido_assert_largeblob_key_len(assert, 0)) < 0)
|
|
errx(1, "write_blob");
|
|
}
|
|
|
|
fido_assert_free(&assert);
|
|
|
|
exit(0);
|
|
}
|