10afa8e06SEd Maste /* 2*f540a430SEd Maste * Copyright (c) 2018-2021 Yubico AB. All rights reserved. 30afa8e06SEd Maste * Use of this source code is governed by a BSD-style 40afa8e06SEd Maste * license that can be found in the LICENSE file. 50afa8e06SEd Maste */ 60afa8e06SEd Maste 70afa8e06SEd Maste #include <errno.h> 80afa8e06SEd Maste #include <fido.h> 90afa8e06SEd Maste #include <stdbool.h> 100afa8e06SEd Maste #include <stdio.h> 110afa8e06SEd Maste #include <stdlib.h> 120afa8e06SEd Maste #include <string.h> 130afa8e06SEd Maste #ifdef HAVE_UNISTD_H 140afa8e06SEd Maste #include <unistd.h> 150afa8e06SEd Maste #endif 160afa8e06SEd Maste 170afa8e06SEd Maste #include "../openbsd-compat/openbsd-compat.h" 180afa8e06SEd Maste #include "extern.h" 190afa8e06SEd Maste 20*f540a430SEd Maste static const unsigned char cd[32] = { 210afa8e06SEd Maste 0xf9, 0x64, 0x57, 0xe7, 0x2d, 0x97, 0xf6, 0xbb, 220afa8e06SEd Maste 0xdd, 0xd7, 0xfb, 0x06, 0x37, 0x62, 0xea, 0x26, 230afa8e06SEd Maste 0x20, 0x44, 0x8e, 0x69, 0x7c, 0x03, 0xf2, 0x31, 240afa8e06SEd Maste 0x2f, 0x99, 0xdc, 0xaf, 0x3e, 0x8a, 0x91, 0x6b, 250afa8e06SEd Maste }; 260afa8e06SEd Maste 270afa8e06SEd Maste static const unsigned char user_id[32] = { 280afa8e06SEd Maste 0x78, 0x1c, 0x78, 0x60, 0xad, 0x88, 0xd2, 0x63, 290afa8e06SEd Maste 0x32, 0x62, 0x2a, 0xf1, 0x74, 0x5d, 0xed, 0xb2, 300afa8e06SEd Maste 0xe7, 0xa4, 0x2b, 0x44, 0x89, 0x29, 0x39, 0xc5, 310afa8e06SEd Maste 0x56, 0x64, 0x01, 0x27, 0x0d, 0xbb, 0xc4, 0x49, 320afa8e06SEd Maste }; 330afa8e06SEd Maste 340afa8e06SEd Maste static void 350afa8e06SEd Maste usage(void) 360afa8e06SEd Maste { 370afa8e06SEd Maste fprintf(stderr, "usage: cred [-t ecdsa|rsa|eddsa] [-k pubkey] " 380afa8e06SEd Maste "[-ei cred_id] [-P pin] [-T seconds] [-b blobkey] [-hruv] " 390afa8e06SEd Maste "<device>\n"); 400afa8e06SEd Maste exit(EXIT_FAILURE); 410afa8e06SEd Maste } 420afa8e06SEd Maste 430afa8e06SEd Maste static void 440afa8e06SEd Maste verify_cred(int type, const char *fmt, const unsigned char *authdata_ptr, 45*f540a430SEd Maste size_t authdata_len, const unsigned char *attstmt_ptr, size_t attstmt_len, 46*f540a430SEd Maste bool rk, bool uv, int ext, const char *key_out, const char *id_out) 470afa8e06SEd Maste { 480afa8e06SEd Maste fido_cred_t *cred; 490afa8e06SEd Maste int r; 500afa8e06SEd Maste 510afa8e06SEd Maste if ((cred = fido_cred_new()) == NULL) 520afa8e06SEd Maste errx(1, "fido_cred_new"); 530afa8e06SEd Maste 540afa8e06SEd Maste /* type */ 550afa8e06SEd Maste r = fido_cred_set_type(cred, type); 560afa8e06SEd Maste if (r != FIDO_OK) 570afa8e06SEd Maste errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r); 580afa8e06SEd Maste 59*f540a430SEd Maste /* client data */ 60*f540a430SEd Maste r = fido_cred_set_clientdata(cred, cd, sizeof(cd)); 610afa8e06SEd Maste if (r != FIDO_OK) 62*f540a430SEd Maste errx(1, "fido_cred_set_clientdata: %s (0x%x)", fido_strerr(r), r); 630afa8e06SEd Maste 640afa8e06SEd Maste /* relying party */ 650afa8e06SEd Maste r = fido_cred_set_rp(cred, "localhost", "sweet home localhost"); 660afa8e06SEd Maste if (r != FIDO_OK) 670afa8e06SEd Maste errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r); 680afa8e06SEd Maste 690afa8e06SEd Maste /* authdata */ 700afa8e06SEd Maste r = fido_cred_set_authdata(cred, authdata_ptr, authdata_len); 710afa8e06SEd Maste if (r != FIDO_OK) 720afa8e06SEd Maste errx(1, "fido_cred_set_authdata: %s (0x%x)", fido_strerr(r), r); 730afa8e06SEd Maste 740afa8e06SEd Maste /* extensions */ 750afa8e06SEd Maste r = fido_cred_set_extensions(cred, ext); 760afa8e06SEd Maste if (r != FIDO_OK) 770afa8e06SEd Maste errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r); 780afa8e06SEd Maste 790afa8e06SEd Maste /* resident key */ 800afa8e06SEd Maste if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK) 810afa8e06SEd Maste errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r); 820afa8e06SEd Maste 830afa8e06SEd Maste /* user verification */ 840afa8e06SEd Maste if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) 850afa8e06SEd Maste errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r); 860afa8e06SEd Maste 870afa8e06SEd Maste /* fmt */ 880afa8e06SEd Maste r = fido_cred_set_fmt(cred, fmt); 890afa8e06SEd Maste if (r != FIDO_OK) 900afa8e06SEd Maste errx(1, "fido_cred_set_fmt: %s (0x%x)", fido_strerr(r), r); 910afa8e06SEd Maste 920afa8e06SEd Maste if (!strcmp(fido_cred_fmt(cred), "none")) { 930afa8e06SEd Maste warnx("no attestation data, skipping credential verification"); 940afa8e06SEd Maste goto out; 950afa8e06SEd Maste } 960afa8e06SEd Maste 97*f540a430SEd Maste /* attestation statement */ 98*f540a430SEd Maste r = fido_cred_set_attstmt(cred, attstmt_ptr, attstmt_len); 990afa8e06SEd Maste if (r != FIDO_OK) 100*f540a430SEd Maste errx(1, "fido_cred_set_attstmt: %s (0x%x)", fido_strerr(r), r); 1010afa8e06SEd Maste 1020afa8e06SEd Maste r = fido_cred_verify(cred); 1030afa8e06SEd Maste if (r != FIDO_OK) 1040afa8e06SEd Maste errx(1, "fido_cred_verify: %s (0x%x)", fido_strerr(r), r); 1050afa8e06SEd Maste 1060afa8e06SEd Maste out: 1070afa8e06SEd Maste if (key_out != NULL) { 1080afa8e06SEd Maste /* extract the credential pubkey */ 1090afa8e06SEd Maste if (type == COSE_ES256) { 1100afa8e06SEd Maste if (write_ec_pubkey(key_out, fido_cred_pubkey_ptr(cred), 1110afa8e06SEd Maste fido_cred_pubkey_len(cred)) < 0) 1120afa8e06SEd Maste errx(1, "write_ec_pubkey"); 1130afa8e06SEd Maste } else if (type == COSE_RS256) { 1140afa8e06SEd Maste if (write_rsa_pubkey(key_out, fido_cred_pubkey_ptr(cred), 1150afa8e06SEd Maste fido_cred_pubkey_len(cred)) < 0) 1160afa8e06SEd Maste errx(1, "write_rsa_pubkey"); 1170afa8e06SEd Maste } else if (type == COSE_EDDSA) { 1180afa8e06SEd Maste if (write_eddsa_pubkey(key_out, fido_cred_pubkey_ptr(cred), 1190afa8e06SEd Maste fido_cred_pubkey_len(cred)) < 0) 1200afa8e06SEd Maste errx(1, "write_eddsa_pubkey"); 1210afa8e06SEd Maste } 1220afa8e06SEd Maste } 1230afa8e06SEd Maste 1240afa8e06SEd Maste if (id_out != NULL) { 1250afa8e06SEd Maste /* extract the credential id */ 1260afa8e06SEd Maste if (write_blob(id_out, fido_cred_id_ptr(cred), 1270afa8e06SEd Maste fido_cred_id_len(cred)) < 0) 1280afa8e06SEd Maste errx(1, "write_blob"); 1290afa8e06SEd Maste } 1300afa8e06SEd Maste 1310afa8e06SEd Maste fido_cred_free(&cred); 1320afa8e06SEd Maste } 1330afa8e06SEd Maste 1340afa8e06SEd Maste int 1350afa8e06SEd Maste main(int argc, char **argv) 1360afa8e06SEd Maste { 1370afa8e06SEd Maste bool rk = false; 1380afa8e06SEd Maste bool uv = false; 1390afa8e06SEd Maste bool u2f = false; 1400afa8e06SEd Maste fido_dev_t *dev; 1410afa8e06SEd Maste fido_cred_t *cred = NULL; 1420afa8e06SEd Maste const char *pin = NULL; 1430afa8e06SEd Maste const char *blobkey_out = NULL; 1440afa8e06SEd Maste const char *key_out = NULL; 1450afa8e06SEd Maste const char *id_out = NULL; 1460afa8e06SEd Maste unsigned char *body = NULL; 147*f540a430SEd Maste long long ms = 0; 1480afa8e06SEd Maste size_t len; 1490afa8e06SEd Maste int type = COSE_ES256; 1500afa8e06SEd Maste int ext = 0; 1510afa8e06SEd Maste int ch; 1520afa8e06SEd Maste int r; 1530afa8e06SEd Maste 1540afa8e06SEd Maste if ((cred = fido_cred_new()) == NULL) 1550afa8e06SEd Maste errx(1, "fido_cred_new"); 1560afa8e06SEd Maste 1570afa8e06SEd Maste while ((ch = getopt(argc, argv, "P:T:b:e:hi:k:rt:uv")) != -1) { 1580afa8e06SEd Maste switch (ch) { 1590afa8e06SEd Maste case 'P': 1600afa8e06SEd Maste pin = optarg; 1610afa8e06SEd Maste break; 1620afa8e06SEd Maste case 'T': 163*f540a430SEd Maste if (base10(optarg, &ms) < 0) 1640afa8e06SEd Maste errx(1, "base10: %s", optarg); 165*f540a430SEd Maste if (ms <= 0 || ms > 30) 1660afa8e06SEd Maste errx(1, "-T: %s must be in (0,30]", optarg); 167*f540a430SEd Maste ms *= 1000; /* seconds to milliseconds */ 1680afa8e06SEd Maste break; 1690afa8e06SEd Maste case 'b': 1700afa8e06SEd Maste ext |= FIDO_EXT_LARGEBLOB_KEY; 1710afa8e06SEd Maste blobkey_out = optarg; 1720afa8e06SEd Maste break; 1730afa8e06SEd Maste case 'e': 1740afa8e06SEd Maste if (read_blob(optarg, &body, &len) < 0) 1750afa8e06SEd Maste errx(1, "read_blob: %s", optarg); 1760afa8e06SEd Maste r = fido_cred_exclude(cred, body, len); 1770afa8e06SEd Maste if (r != FIDO_OK) 1780afa8e06SEd Maste errx(1, "fido_cred_exclude: %s (0x%x)", 1790afa8e06SEd Maste fido_strerr(r), r); 1800afa8e06SEd Maste free(body); 1810afa8e06SEd Maste body = NULL; 1820afa8e06SEd Maste break; 1830afa8e06SEd Maste case 'h': 1840afa8e06SEd Maste ext |= FIDO_EXT_HMAC_SECRET; 1850afa8e06SEd Maste break; 1860afa8e06SEd Maste case 'i': 1870afa8e06SEd Maste id_out = optarg; 1880afa8e06SEd Maste break; 1890afa8e06SEd Maste case 'k': 1900afa8e06SEd Maste key_out = optarg; 1910afa8e06SEd Maste break; 1920afa8e06SEd Maste case 'r': 1930afa8e06SEd Maste rk = true; 1940afa8e06SEd Maste break; 1950afa8e06SEd Maste case 't': 1960afa8e06SEd Maste if (strcmp(optarg, "ecdsa") == 0) 1970afa8e06SEd Maste type = COSE_ES256; 1980afa8e06SEd Maste else if (strcmp(optarg, "rsa") == 0) 1990afa8e06SEd Maste type = COSE_RS256; 2000afa8e06SEd Maste else if (strcmp(optarg, "eddsa") == 0) 2010afa8e06SEd Maste type = COSE_EDDSA; 2020afa8e06SEd Maste else 2030afa8e06SEd Maste errx(1, "unknown type %s", optarg); 2040afa8e06SEd Maste break; 2050afa8e06SEd Maste case 'u': 2060afa8e06SEd Maste u2f = true; 2070afa8e06SEd Maste break; 2080afa8e06SEd Maste case 'v': 2090afa8e06SEd Maste uv = true; 2100afa8e06SEd Maste break; 2110afa8e06SEd Maste default: 2120afa8e06SEd Maste usage(); 2130afa8e06SEd Maste } 2140afa8e06SEd Maste } 2150afa8e06SEd Maste 2160afa8e06SEd Maste argc -= optind; 2170afa8e06SEd Maste argv += optind; 2180afa8e06SEd Maste 219*f540a430SEd Maste if (argc != 1) 2200afa8e06SEd Maste usage(); 2210afa8e06SEd Maste 222*f540a430SEd Maste fido_init(0); 2230afa8e06SEd Maste 224*f540a430SEd Maste if ((dev = fido_dev_new()) == NULL) 225*f540a430SEd Maste errx(1, "fido_dev_new"); 226*f540a430SEd Maste 227*f540a430SEd Maste r = fido_dev_open(dev, argv[0]); 228*f540a430SEd Maste if (r != FIDO_OK) 229*f540a430SEd Maste errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r); 2300afa8e06SEd Maste if (u2f) 2310afa8e06SEd Maste fido_dev_force_u2f(dev); 2320afa8e06SEd Maste 2330afa8e06SEd Maste /* type */ 2340afa8e06SEd Maste r = fido_cred_set_type(cred, type); 2350afa8e06SEd Maste if (r != FIDO_OK) 2360afa8e06SEd Maste errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r); 2370afa8e06SEd Maste 238*f540a430SEd Maste /* client data */ 239*f540a430SEd Maste r = fido_cred_set_clientdata(cred, cd, sizeof(cd)); 2400afa8e06SEd Maste if (r != FIDO_OK) 241*f540a430SEd Maste errx(1, "fido_cred_set_clientdata: %s (0x%x)", fido_strerr(r), r); 2420afa8e06SEd Maste 2430afa8e06SEd Maste /* relying party */ 2440afa8e06SEd Maste r = fido_cred_set_rp(cred, "localhost", "sweet home localhost"); 2450afa8e06SEd Maste if (r != FIDO_OK) 2460afa8e06SEd Maste errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r); 2470afa8e06SEd Maste 2480afa8e06SEd Maste /* user */ 2490afa8e06SEd Maste r = fido_cred_set_user(cred, user_id, sizeof(user_id), "john smith", 2500afa8e06SEd Maste "jsmith", NULL); 2510afa8e06SEd Maste if (r != FIDO_OK) 2520afa8e06SEd Maste errx(1, "fido_cred_set_user: %s (0x%x)", fido_strerr(r), r); 2530afa8e06SEd Maste 2540afa8e06SEd Maste /* extensions */ 2550afa8e06SEd Maste r = fido_cred_set_extensions(cred, ext); 2560afa8e06SEd Maste if (r != FIDO_OK) 2570afa8e06SEd Maste errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r); 2580afa8e06SEd Maste 2590afa8e06SEd Maste /* resident key */ 2600afa8e06SEd Maste if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK) 2610afa8e06SEd Maste errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r); 2620afa8e06SEd Maste 2630afa8e06SEd Maste /* user verification */ 2640afa8e06SEd Maste if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) 2650afa8e06SEd Maste errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r); 2660afa8e06SEd Maste 267*f540a430SEd Maste /* timeout */ 268*f540a430SEd Maste if (ms != 0 && (r = fido_dev_set_timeout(dev, (int)ms)) != FIDO_OK) 269*f540a430SEd Maste errx(1, "fido_dev_set_timeout: %s (0x%x)", fido_strerr(r), r); 2700afa8e06SEd Maste 271*f540a430SEd Maste if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) { 2720afa8e06SEd Maste fido_dev_cancel(dev); 2730afa8e06SEd Maste errx(1, "fido_makecred: %s (0x%x)", fido_strerr(r), r); 2740afa8e06SEd Maste } 2750afa8e06SEd Maste 2760afa8e06SEd Maste r = fido_dev_close(dev); 2770afa8e06SEd Maste if (r != FIDO_OK) 2780afa8e06SEd Maste errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r); 2790afa8e06SEd Maste 2800afa8e06SEd Maste fido_dev_free(&dev); 2810afa8e06SEd Maste 2820afa8e06SEd Maste /* when verifying, pin implies uv */ 2830afa8e06SEd Maste if (pin) 2840afa8e06SEd Maste uv = true; 2850afa8e06SEd Maste 2860afa8e06SEd Maste verify_cred(type, fido_cred_fmt(cred), fido_cred_authdata_ptr(cred), 287*f540a430SEd Maste fido_cred_authdata_len(cred), fido_cred_attstmt_ptr(cred), 288*f540a430SEd Maste fido_cred_attstmt_len(cred), rk, uv, ext, key_out, id_out); 2890afa8e06SEd Maste 2900afa8e06SEd Maste if (blobkey_out != NULL) { 2910afa8e06SEd Maste /* extract the "largeBlob" key */ 2920afa8e06SEd Maste if (write_blob(blobkey_out, fido_cred_largeblob_key_ptr(cred), 2930afa8e06SEd Maste fido_cred_largeblob_key_len(cred)) < 0) 2940afa8e06SEd Maste errx(1, "write_blob"); 2950afa8e06SEd Maste } 2960afa8e06SEd Maste 2970afa8e06SEd Maste fido_cred_free(&cred); 2980afa8e06SEd Maste 2990afa8e06SEd Maste exit(0); 3000afa8e06SEd Maste } 301