Annotation of src/usr.bin/ssh/sk-usbhid.c, Revision 1.9
1.1 djm 1: /*
2: * Copyright (c) 2019 Markus Friedl
3: *
4: * Permission to use, copy, modify, and distribute this software for any
5: * purpose with or without fee is hereby granted, provided that the above
6: * copyright notice and this permission notice appear in all copies.
7: *
8: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15: */
16:
17: #include <stdint.h>
18: #include <stdlib.h>
19: #include <string.h>
20: #include <stdio.h>
21: #include <stddef.h>
22: #include <stdarg.h>
23:
1.7 naddy 24: #ifdef WITH_OPENSSL
1.1 djm 25: #include <openssl/opensslv.h>
26: #include <openssl/crypto.h>
27: #include <openssl/bn.h>
28: #include <openssl/ec.h>
29: #include <openssl/ecdsa.h>
1.7 naddy 30: #endif /* WITH_OPENSSL */
1.1 djm 31:
32: #include <fido.h>
1.9 ! djm 33: #include <fido/credman.h>
1.1 djm 34:
35: #ifndef SK_STANDALONE
36: #include "log.h"
37: #include "xmalloc.h"
38: #endif
39:
40: /* #define SK_DEBUG 1 */
41:
42: #define MAX_FIDO_DEVICES 256
43:
44: /* Compatibility with OpenSSH 1.0.x */
45: #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
46: #define ECDSA_SIG_get0(sig, pr, ps) \
47: do { \
48: (*pr) = sig->r; \
49: (*ps) = sig->s; \
50: } while (0)
51: #endif
52:
53: #define SK_VERSION_MAJOR 0x00020000 /* current API version */
54:
55: /* Flags */
1.8 djm 56: #define SK_USER_PRESENCE_REQD 0x01
57: #define SK_USER_VERIFICATION_REQD 0x04
58: #define SK_RESIDENT_KEY 0x20
1.1 djm 59:
60: /* Algs */
61: #define SK_ECDSA 0x00
62: #define SK_ED25519 0x01
63:
64: struct sk_enroll_response {
65: uint8_t *public_key;
66: size_t public_key_len;
67: uint8_t *key_handle;
68: size_t key_handle_len;
69: uint8_t *signature;
70: size_t signature_len;
71: uint8_t *attestation_cert;
72: size_t attestation_cert_len;
73: };
74:
75: struct sk_sign_response {
76: uint8_t flags;
77: uint32_t counter;
78: uint8_t *sig_r;
79: size_t sig_r_len;
80: uint8_t *sig_s;
81: size_t sig_s_len;
82: };
83:
1.9 ! djm 84: struct sk_resident_key {
! 85: uint8_t alg;
! 86: size_t slot;
! 87: char *application;
! 88: struct sk_enroll_response key;
! 89: };
! 90:
1.1 djm 91: /* If building as part of OpenSSH, then rename exported functions */
92: #if !defined(SK_STANDALONE)
1.9 ! djm 93: #define sk_api_version ssh_sk_api_version
! 94: #define sk_enroll ssh_sk_enroll
! 95: #define sk_sign ssh_sk_sign
! 96: #define sk_load_resident_keys ssh_sk_load_resident_keys
1.1 djm 97: #endif
98:
99: /* Return the version of the middleware API */
100: uint32_t sk_api_version(void);
101:
102: /* Enroll a U2F key (private key generation) */
103: int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
104: const char *application, uint8_t flags,
105: struct sk_enroll_response **enroll_response);
106:
107: /* Sign a challenge */
108: int sk_sign(int alg, const uint8_t *message, size_t message_len,
109: const char *application, const uint8_t *key_handle, size_t key_handle_len,
110: uint8_t flags, struct sk_sign_response **sign_response);
111:
1.9 ! djm 112: /* Load resident keys */
! 113: int sk_load_resident_keys(const char *pin,
! 114: struct sk_resident_key ***rks, size_t *nrks);
! 115:
1.1 djm 116: static void skdebug(const char *func, const char *fmt, ...)
117: __attribute__((__format__ (printf, 2, 3)));
118:
119: static void
120: skdebug(const char *func, const char *fmt, ...)
121: {
122: #if !defined(SK_STANDALONE)
123: char *msg;
124: va_list ap;
125:
126: va_start(ap, fmt);
127: xvasprintf(&msg, fmt, ap);
128: va_end(ap);
1.2 djm 129: debug("%s: %s", func, msg);
1.1 djm 130: free(msg);
131: #elif defined(SK_DEBUG)
132: va_list ap;
133:
134: va_start(ap, fmt);
135: fprintf(stderr, "%s: ", func);
136: vfprintf(stderr, fmt, ap);
137: fputc('\n', stderr);
138: va_end(ap);
139: #else
140: (void)func; /* XXX */
141: (void)fmt; /* XXX */
142: #endif
143: }
144:
145: uint32_t
146: sk_api_version(void)
147: {
148: return SK_VERSION_MAJOR;
149: }
150:
151: /* Select the first identified FIDO device attached to the system */
152: static char *
153: pick_first_device(void)
154: {
155: char *ret = NULL;
156: fido_dev_info_t *devlist = NULL;
157: size_t olen = 0;
158: int r;
159: const fido_dev_info_t *di;
160:
161: if ((devlist = fido_dev_info_new(1)) == NULL) {
162: skdebug(__func__, "fido_dev_info_new failed");
163: goto out;
164: }
165: if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) {
166: skdebug(__func__, "fido_dev_info_manifest failed: %s",
167: fido_strerr(r));
168: goto out;
169: }
170: if (olen != 1) {
171: skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen);
172: goto out;
173: }
174: di = fido_dev_info_ptr(devlist, 0);
175: if ((ret = strdup(fido_dev_info_path(di))) == NULL) {
176: skdebug(__func__, "fido_dev_info_path failed");
177: goto out;
178: }
179: out:
180: fido_dev_info_free(&devlist, 1);
181: return ret;
182: }
183:
184: /* Check if the specified key handle exists on a given device. */
185: static int
186: try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len,
187: const char *application, const uint8_t *key_handle, size_t key_handle_len)
188: {
189: fido_assert_t *assert = NULL;
190: int r = FIDO_ERR_INTERNAL;
191:
192: if ((assert = fido_assert_new()) == NULL) {
193: skdebug(__func__, "fido_assert_new failed");
194: goto out;
195: }
196: if ((r = fido_assert_set_clientdata_hash(assert, message,
197: message_len)) != FIDO_OK) {
198: skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
199: fido_strerr(r));
200: goto out;
201: }
202: if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
203: skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
204: goto out;
205: }
206: if ((r = fido_assert_allow_cred(assert, key_handle,
207: key_handle_len)) != FIDO_OK) {
208: skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
209: goto out;
210: }
211: if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
212: skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
213: goto out;
214: }
215: r = fido_dev_get_assert(dev, assert, NULL);
216: skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
1.3 djm 217: if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
218: /* U2F tokens may return this */
219: r = FIDO_OK;
220: }
1.1 djm 221: out:
222: fido_assert_free(&assert);
223:
224: return r != FIDO_OK ? -1 : 0;
225: }
226:
227: /* Iterate over configured devices looking for a specific key handle */
228: static fido_dev_t *
229: find_device(const uint8_t *message, size_t message_len, const char *application,
230: const uint8_t *key_handle, size_t key_handle_len)
231: {
232: fido_dev_info_t *devlist = NULL;
233: fido_dev_t *dev = NULL;
1.4 deraadt 234: size_t devlist_len = 0, i;
1.1 djm 235: const char *path;
236: int r;
237:
238: if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
239: skdebug(__func__, "fido_dev_info_new failed");
240: goto out;
241: }
242: if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
243: &devlist_len)) != FIDO_OK) {
244: skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r));
245: goto out;
246: }
247:
248: skdebug(__func__, "found %zu device(s)", devlist_len);
249:
1.4 deraadt 250: for (i = 0; i < devlist_len; i++) {
1.1 djm 251: const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
252:
253: if (di == NULL) {
254: skdebug(__func__, "fido_dev_info_ptr %zu failed", i);
255: continue;
256: }
257: if ((path = fido_dev_info_path(di)) == NULL) {
258: skdebug(__func__, "fido_dev_info_path %zu failed", i);
259: continue;
260: }
261: skdebug(__func__, "trying device %zu: %s", i, path);
262: if ((dev = fido_dev_new()) == NULL) {
263: skdebug(__func__, "fido_dev_new failed");
264: continue;
265: }
266: if ((r = fido_dev_open(dev, path)) != FIDO_OK) {
267: skdebug(__func__, "fido_dev_open failed");
268: fido_dev_free(&dev);
269: continue;
270: }
271: if (try_device(dev, message, message_len, application,
272: key_handle, key_handle_len) == 0) {
273: skdebug(__func__, "found key");
274: break;
275: }
276: fido_dev_close(dev);
277: fido_dev_free(&dev);
278: }
279:
280: out:
281: if (devlist != NULL)
282: fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
283:
284: return dev;
285: }
286:
1.7 naddy 287: #ifdef WITH_OPENSSL
1.1 djm 288: /*
289: * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
290: * but the API expects a SEC1 octet string.
291: */
292: static int
1.9 ! djm 293: pack_public_key_ecdsa(const fido_cred_t *cred,
! 294: struct sk_enroll_response *response)
1.1 djm 295: {
296: const uint8_t *ptr;
297: BIGNUM *x = NULL, *y = NULL;
298: EC_POINT *q = NULL;
299: EC_GROUP *g = NULL;
300: int ret = -1;
301:
302: response->public_key = NULL;
303: response->public_key_len = 0;
304:
1.5 djm 305: if ((x = BN_new()) == NULL ||
306: (y = BN_new()) == NULL ||
1.1 djm 307: (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
308: (q = EC_POINT_new(g)) == NULL) {
309: skdebug(__func__, "libcrypto setup failed");
310: goto out;
311: }
312: if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
313: skdebug(__func__, "fido_cred_pubkey_ptr failed");
314: goto out;
315: }
316: if (fido_cred_pubkey_len(cred) != 64) {
317: skdebug(__func__, "bad fido_cred_pubkey_len %zu",
318: fido_cred_pubkey_len(cred));
319: goto out;
320: }
321:
322: if (BN_bin2bn(ptr, 32, x) == NULL ||
323: BN_bin2bn(ptr + 32, 32, y) == NULL) {
324: skdebug(__func__, "BN_bin2bn failed");
325: goto out;
326: }
1.5 djm 327: if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) {
1.1 djm 328: skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
329: goto out;
330: }
331: response->public_key_len = EC_POINT_point2oct(g, q,
1.5 djm 332: POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
1.1 djm 333: if (response->public_key_len == 0 || response->public_key_len > 2048) {
334: skdebug(__func__, "bad pubkey length %zu",
335: response->public_key_len);
336: goto out;
337: }
338: if ((response->public_key = malloc(response->public_key_len)) == NULL) {
339: skdebug(__func__, "malloc pubkey failed");
340: goto out;
341: }
342: if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
1.5 djm 343: response->public_key, response->public_key_len, NULL) == 0) {
1.1 djm 344: skdebug(__func__, "EC_POINT_point2oct failed");
345: goto out;
346: }
347: /* success */
348: ret = 0;
349: out:
350: if (ret != 0 && response->public_key != NULL) {
351: memset(response->public_key, 0, response->public_key_len);
352: free(response->public_key);
353: response->public_key = NULL;
354: }
355: EC_POINT_free(q);
356: EC_GROUP_free(g);
1.5 djm 357: BN_clear_free(x);
358: BN_clear_free(y);
1.1 djm 359: return ret;
360: }
1.7 naddy 361: #endif /* WITH_OPENSSL */
1.1 djm 362:
363: static int
1.9 ! djm 364: pack_public_key_ed25519(const fido_cred_t *cred,
! 365: struct sk_enroll_response *response)
1.1 djm 366: {
367: const uint8_t *ptr;
368: size_t len;
369: int ret = -1;
370:
371: response->public_key = NULL;
372: response->public_key_len = 0;
373:
374: if ((len = fido_cred_pubkey_len(cred)) != 32) {
375: skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
376: goto out;
377: }
378: if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
379: skdebug(__func__, "fido_cred_pubkey_ptr failed");
380: goto out;
381: }
382: response->public_key_len = len;
383: if ((response->public_key = malloc(response->public_key_len)) == NULL) {
384: skdebug(__func__, "malloc pubkey failed");
385: goto out;
386: }
387: memcpy(response->public_key, ptr, len);
388: ret = 0;
389: out:
390: if (ret != 0)
391: free(response->public_key);
392: return ret;
393: }
394:
395: static int
1.9 ! djm 396: pack_public_key(int alg, const fido_cred_t *cred,
! 397: struct sk_enroll_response *response)
1.1 djm 398: {
399: switch(alg) {
1.7 naddy 400: #ifdef WITH_OPENSSL
1.1 djm 401: case SK_ECDSA:
402: return pack_public_key_ecdsa(cred, response);
1.7 naddy 403: #endif /* WITH_OPENSSL */
1.1 djm 404: case SK_ED25519:
405: return pack_public_key_ed25519(cred, response);
406: default:
407: return -1;
408: }
409: }
410:
411: int
412: sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
413: const char *application, uint8_t flags,
1.6 markus 414: struct sk_enroll_response **enroll_response)
1.1 djm 415: {
416: fido_cred_t *cred = NULL;
417: fido_dev_t *dev = NULL;
418: const uint8_t *ptr;
419: uint8_t user_id[32];
420: struct sk_enroll_response *response = NULL;
421: size_t len;
422: int cose_alg;
423: int ret = -1;
424: int r;
425: char *device = NULL;
426:
427: #ifdef SK_DEBUG
428: fido_init(FIDO_DEBUG);
429: #endif
1.6 markus 430: if (enroll_response == NULL) {
431: skdebug(__func__, "enroll_response == NULL");
1.1 djm 432: goto out;
433: }
1.6 markus 434: *enroll_response = NULL;
1.1 djm 435: switch(alg) {
1.7 naddy 436: #ifdef WITH_OPENSSL
1.1 djm 437: case SK_ECDSA:
438: cose_alg = COSE_ES256;
439: break;
1.7 naddy 440: #endif /* WITH_OPENSSL */
1.1 djm 441: case SK_ED25519:
442: cose_alg = COSE_EDDSA;
443: break;
444: default:
445: skdebug(__func__, "unsupported key type %d", alg);
446: goto out;
447: }
448: if ((device = pick_first_device()) == NULL) {
449: skdebug(__func__, "pick_first_device failed");
450: goto out;
451: }
452: skdebug(__func__, "using device %s", device);
453: if ((cred = fido_cred_new()) == NULL) {
454: skdebug(__func__, "fido_cred_new failed");
455: goto out;
456: }
457: memset(user_id, 0, sizeof(user_id));
458: if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
459: skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
460: goto out;
461: }
462: if ((r = fido_cred_set_clientdata_hash(cred, challenge,
463: challenge_len)) != FIDO_OK) {
464: skdebug(__func__, "fido_cred_set_clientdata_hash: %s",
465: fido_strerr(r));
1.8 djm 466: goto out;
467: }
468: if ((r = fido_cred_set_rk(cred, (flags & SK_RESIDENT_KEY) != 0 ?
469: FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
470: skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
1.1 djm 471: goto out;
472: }
473: if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
474: "openssh", "openssh", NULL)) != FIDO_OK) {
475: skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
476: goto out;
477: }
478: if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
479: skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
480: goto out;
481: }
482: if ((dev = fido_dev_new()) == NULL) {
483: skdebug(__func__, "fido_dev_new failed");
484: goto out;
485: }
486: if ((r = fido_dev_open(dev, device)) != FIDO_OK) {
487: skdebug(__func__, "fido_dev_open: %s", fido_strerr(r));
488: goto out;
489: }
490: if ((r = fido_dev_make_cred(dev, cred, NULL)) != FIDO_OK) {
491: skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
492: goto out;
493: }
494: if (fido_cred_x5c_ptr(cred) != NULL) {
495: if ((r = fido_cred_verify(cred)) != FIDO_OK) {
496: skdebug(__func__, "fido_cred_verify: %s",
497: fido_strerr(r));
498: goto out;
499: }
500: } else {
501: skdebug(__func__, "self-attested credential");
502: if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
503: skdebug(__func__, "fido_cred_verify_self: %s",
504: fido_strerr(r));
505: goto out;
506: }
507: }
508: if ((response = calloc(1, sizeof(*response))) == NULL) {
509: skdebug(__func__, "calloc response failed");
510: goto out;
511: }
512: if (pack_public_key(alg, cred, response) != 0) {
513: skdebug(__func__, "pack_public_key failed");
514: goto out;
515: }
516: if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
517: len = fido_cred_id_len(cred);
518: if ((response->key_handle = calloc(1, len)) == NULL) {
519: skdebug(__func__, "calloc key handle failed");
520: goto out;
521: }
522: memcpy(response->key_handle, ptr, len);
523: response->key_handle_len = len;
524: }
525: if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
526: len = fido_cred_sig_len(cred);
527: if ((response->signature = calloc(1, len)) == NULL) {
528: skdebug(__func__, "calloc signature failed");
529: goto out;
530: }
531: memcpy(response->signature, ptr, len);
532: response->signature_len = len;
533: }
534: if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
535: len = fido_cred_x5c_len(cred);
536: if ((response->attestation_cert = calloc(1, len)) == NULL) {
537: skdebug(__func__, "calloc attestation cert failed");
538: goto out;
539: }
540: memcpy(response->attestation_cert, ptr, len);
541: response->attestation_cert_len = len;
542: }
1.6 markus 543: *enroll_response = response;
1.1 djm 544: response = NULL;
545: ret = 0;
546: out:
547: free(device);
548: if (response != NULL) {
549: free(response->public_key);
550: free(response->key_handle);
551: free(response->signature);
552: free(response->attestation_cert);
553: free(response);
554: }
555: if (dev != NULL) {
556: fido_dev_close(dev);
557: fido_dev_free(&dev);
558: }
559: if (cred != NULL) {
560: fido_cred_free(&cred);
561: }
562: return ret;
563: }
564:
1.7 naddy 565: #ifdef WITH_OPENSSL
1.1 djm 566: static int
567: pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
568: {
569: ECDSA_SIG *sig = NULL;
570: const BIGNUM *sig_r, *sig_s;
571: const unsigned char *cp;
572: size_t sig_len;
573: int ret = -1;
574:
575: cp = fido_assert_sig_ptr(assert, 0);
576: sig_len = fido_assert_sig_len(assert, 0);
577: if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
578: skdebug(__func__, "d2i_ECDSA_SIG failed");
579: goto out;
580: }
581: ECDSA_SIG_get0(sig, &sig_r, &sig_s);
582: response->sig_r_len = BN_num_bytes(sig_r);
583: response->sig_s_len = BN_num_bytes(sig_s);
584: if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
585: (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
586: skdebug(__func__, "calloc signature failed");
587: goto out;
588: }
589: BN_bn2bin(sig_r, response->sig_r);
590: BN_bn2bin(sig_s, response->sig_s);
591: ret = 0;
592: out:
593: ECDSA_SIG_free(sig);
594: if (ret != 0) {
595: free(response->sig_r);
596: free(response->sig_s);
597: response->sig_r = NULL;
598: response->sig_s = NULL;
599: }
600: return ret;
601: }
1.7 naddy 602: #endif /* WITH_OPENSSL */
1.1 djm 603:
604: static int
605: pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
606: {
607: const unsigned char *ptr;
608: size_t len;
609: int ret = -1;
610:
611: ptr = fido_assert_sig_ptr(assert, 0);
612: len = fido_assert_sig_len(assert, 0);
613: if (len != 64) {
614: skdebug(__func__, "bad length %zu", len);
615: goto out;
616: }
617: response->sig_r_len = len;
618: if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
619: skdebug(__func__, "calloc signature failed");
620: goto out;
621: }
622: memcpy(response->sig_r, ptr, len);
623: ret = 0;
624: out:
625: if (ret != 0) {
626: free(response->sig_r);
627: response->sig_r = NULL;
628: }
629: return ret;
630: }
631:
632: static int
633: pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response)
634: {
635: switch(alg) {
1.7 naddy 636: #ifdef WITH_OPENSSL
1.1 djm 637: case SK_ECDSA:
638: return pack_sig_ecdsa(assert, response);
1.7 naddy 639: #endif /* WITH_OPENSSL */
1.1 djm 640: case SK_ED25519:
641: return pack_sig_ed25519(assert, response);
642: default:
643: return -1;
644: }
645: }
646:
647: int
648: sk_sign(int alg, const uint8_t *message, size_t message_len,
649: const char *application,
650: const uint8_t *key_handle, size_t key_handle_len,
651: uint8_t flags, struct sk_sign_response **sign_response)
652: {
653: fido_assert_t *assert = NULL;
654: fido_dev_t *dev = NULL;
655: struct sk_sign_response *response = NULL;
656: int ret = -1;
657: int r;
658:
659: #ifdef SK_DEBUG
660: fido_init(FIDO_DEBUG);
661: #endif
662:
663: if (sign_response == NULL) {
664: skdebug(__func__, "sign_response == NULL");
665: goto out;
666: }
667: *sign_response = NULL;
668: if ((dev = find_device(message, message_len, application, key_handle,
669: key_handle_len)) == NULL) {
670: skdebug(__func__, "couldn't find device for key handle");
671: goto out;
672: }
673: if ((assert = fido_assert_new()) == NULL) {
674: skdebug(__func__, "fido_assert_new failed");
675: goto out;
676: }
677: if ((r = fido_assert_set_clientdata_hash(assert, message,
678: message_len)) != FIDO_OK) {
679: skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
680: fido_strerr(r));
681: goto out;
682: }
683: if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
684: skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
685: goto out;
686: }
687: if ((r = fido_assert_allow_cred(assert, key_handle,
688: key_handle_len)) != FIDO_OK) {
689: skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
690: goto out;
691: }
692: if ((r = fido_assert_set_up(assert,
693: (flags & SK_USER_PRESENCE_REQD) ?
694: FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
695: skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
696: goto out;
697: }
698: if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) {
699: skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
700: goto out;
701: }
702: if ((response = calloc(1, sizeof(*response))) == NULL) {
703: skdebug(__func__, "calloc response failed");
704: goto out;
705: }
706: response->flags = fido_assert_flags(assert, 0);
707: response->counter = fido_assert_sigcount(assert, 0);
708: if (pack_sig(alg, assert, response) != 0) {
709: skdebug(__func__, "pack_sig failed");
710: goto out;
711: }
712: *sign_response = response;
713: response = NULL;
714: ret = 0;
715: out:
716: if (response != NULL) {
717: free(response->sig_r);
718: free(response->sig_s);
719: free(response);
720: }
721: if (dev != NULL) {
722: fido_dev_close(dev);
723: fido_dev_free(&dev);
724: }
725: if (assert != NULL) {
726: fido_assert_free(&assert);
727: }
728: return ret;
729: }
1.9 ! djm 730:
! 731: static int
! 732: read_rks(const char *devpath, const char *pin,
! 733: struct sk_resident_key ***rksp, size_t *nrksp)
! 734: {
! 735: int r = -1;
! 736: fido_dev_t *dev = NULL;
! 737: fido_credman_metadata_t *metadata = NULL;
! 738: fido_credman_rp_t *rp = NULL;
! 739: fido_credman_rk_t *rk = NULL;
! 740: size_t i, j, nrp, nrk;
! 741: const fido_cred_t *cred;
! 742: struct sk_resident_key *srk = NULL, **tmp;
! 743:
! 744: if ((dev = fido_dev_new()) == NULL) {
! 745: skdebug(__func__, "fido_dev_new failed");
! 746: return -1;
! 747: }
! 748: if ((r = fido_dev_open(dev, devpath)) != FIDO_OK) {
! 749: skdebug(__func__, "fido_dev_open %s failed: %s",
! 750: devpath, fido_strerr(r));
! 751: fido_dev_free(&dev);
! 752: return -1;
! 753: }
! 754: if ((metadata = fido_credman_metadata_new()) == NULL) {
! 755: r = -1;
! 756: skdebug(__func__, "alloc failed");
! 757: goto out;
! 758: }
! 759:
! 760: if ((r = fido_credman_get_dev_metadata(dev, metadata, pin)) != 0) {
! 761: if (r == FIDO_ERR_INVALID_COMMAND) {
! 762: skdebug(__func__, "device %s does not support "
! 763: "resident keys", devpath);
! 764: r = 0;
! 765: goto out;
! 766: }
! 767: skdebug(__func__, "get metadata for %s failed: %s",
! 768: devpath, fido_strerr(r));
! 769: goto out;
! 770: }
! 771: skdebug(__func__, "existing %llu, remaining %llu",
! 772: (unsigned long long)fido_credman_rk_existing(metadata),
! 773: (unsigned long long)fido_credman_rk_remaining(metadata));
! 774: if ((rp = fido_credman_rp_new()) == NULL) {
! 775: r = -1;
! 776: skdebug(__func__, "alloc rp failed");
! 777: goto out;
! 778: }
! 779: if ((r = fido_credman_get_dev_rp(dev, rp, pin)) != 0) {
! 780: skdebug(__func__, "get RPs for %s failed: %s",
! 781: devpath, fido_strerr(r));
! 782: goto out;
! 783: }
! 784: nrp = fido_credman_rp_count(rp);
! 785: skdebug(__func__, "Device %s has resident keys for %zu RPs",
! 786: devpath, nrp);
! 787:
! 788: /* Iterate over RP IDs that have resident keys */
! 789: for (i = 0; i < nrp; i++) {
! 790: skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
! 791: i, fido_credman_rp_name(rp, i), fido_credman_rp_id(rp, i),
! 792: fido_credman_rp_id_hash_len(rp, i));
! 793:
! 794: /* Skip non-SSH RP IDs */
! 795: if (strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
! 796: continue;
! 797:
! 798: fido_credman_rk_free(&rk);
! 799: if ((rk = fido_credman_rk_new()) == NULL) {
! 800: r = -1;
! 801: skdebug(__func__, "alloc rk failed");
! 802: goto out;
! 803: }
! 804: if ((r = fido_credman_get_dev_rk(dev, fido_credman_rp_id(rp, i),
! 805: rk, pin)) != 0) {
! 806: skdebug(__func__, "get RKs for %s slot %zu failed: %s",
! 807: devpath, i, fido_strerr(r));
! 808: goto out;
! 809: }
! 810: nrk = fido_credman_rk_count(rk);
! 811: skdebug(__func__, "RP \"%s\" has %zu resident keys",
! 812: fido_credman_rp_id(rp, i), nrk);
! 813:
! 814: /* Iterate over resident keys for this RP ID */
! 815: for (j = 0; j < nrk; j++) {
! 816: if ((cred = fido_credman_rk(rk, j)) == NULL) {
! 817: skdebug(__func__, "no RK in slot %zu", j);
! 818: continue;
! 819: }
! 820: skdebug(__func__, "Device %s RP \"%s\" slot %zu: "
! 821: "type %d", devpath, fido_credman_rp_id(rp, i), j,
! 822: fido_cred_type(cred));
! 823:
! 824: /* build response entry */
! 825: if ((srk = calloc(1, sizeof(*srk))) == NULL ||
! 826: (srk->key.key_handle = calloc(1,
! 827: fido_cred_id_len(cred))) == NULL ||
! 828: (srk->application = strdup(fido_credman_rp_id(rp,
! 829: i))) == NULL) {
! 830: r = -1;
! 831: skdebug(__func__, "alloc sk_resident_key");
! 832: goto out;
! 833: }
! 834:
! 835: srk->key.key_handle_len = fido_cred_id_len(cred);
! 836: memcpy(srk->key.key_handle,
! 837: fido_cred_id_ptr(cred),
! 838: srk->key.key_handle_len);
! 839:
! 840: switch (fido_cred_type(cred)) {
! 841: case COSE_ES256:
! 842: srk->alg = SK_ECDSA;
! 843: break;
! 844: case COSE_EDDSA:
! 845: srk->alg = SK_ED25519;
! 846: break;
! 847: default:
! 848: skdebug(__func__, "unsupported key type %d",
! 849: fido_cred_type(cred));
! 850: goto out;
! 851: }
! 852:
! 853: if ((r = pack_public_key(srk->alg, cred,
! 854: &srk->key)) != 0) {
! 855: skdebug(__func__, "pack public key failed");
! 856: goto out;
! 857: }
! 858: /* append */
! 859: if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
! 860: sizeof(**rksp))) == NULL) {
! 861: r = -1;
! 862: skdebug(__func__, "alloc rksp");
! 863: goto out;
! 864: }
! 865: *rksp = tmp;
! 866: (*rksp)[(*nrksp)++] = srk;
! 867: srk = NULL;
! 868: }
! 869: }
! 870: /* Success */
! 871: r = 0;
! 872: out:
! 873: if (srk != NULL) {
! 874: free(srk->application);
! 875: freezero(srk->key.public_key, srk->key.public_key_len);
! 876: freezero(srk->key.key_handle, srk->key.key_handle_len);
! 877: freezero(srk, sizeof(*srk));
! 878: }
! 879: fido_credman_rp_free(&rp);
! 880: fido_credman_rk_free(&rk);
! 881: fido_dev_close(dev);
! 882: fido_dev_free(&dev);
! 883: fido_credman_metadata_free(&metadata);
! 884: return r;
! 885: }
! 886:
! 887: int
! 888: sk_load_resident_keys(const char *pin,
! 889: struct sk_resident_key ***rksp, size_t *nrksp)
! 890: {
! 891: int r = -1;
! 892: fido_dev_info_t *devlist = NULL;
! 893: size_t i, ndev = 0, nrks = 0;
! 894: const fido_dev_info_t *di;
! 895: struct sk_resident_key **rks = NULL;
! 896: *rksp = NULL;
! 897: *nrksp = 0;
! 898:
! 899: if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
! 900: skdebug(__func__, "fido_dev_info_new failed");
! 901: goto out;
! 902: }
! 903: if ((r = fido_dev_info_manifest(devlist,
! 904: MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) {
! 905: skdebug(__func__, "fido_dev_info_manifest failed: %s",
! 906: fido_strerr(r));
! 907: goto out;
! 908: }
! 909: for (i = 0; i < ndev; i++) {
! 910: if ((di = fido_dev_info_ptr(devlist, i)) == NULL) {
! 911: skdebug(__func__, "no dev info at %zu", i);
! 912: continue;
! 913: }
! 914: skdebug(__func__, "trying %s", fido_dev_info_path(di));
! 915: if ((r = read_rks(fido_dev_info_path(di), pin,
! 916: &rks, &nrks)) != 0) {
! 917: skdebug(__func__, "read_rks failed for %s",
! 918: fido_dev_info_path(di));
! 919: continue;
! 920: }
! 921: }
! 922: /* success */
! 923: r = 0;
! 924: *rksp = rks;
! 925: *nrksp = nrks;
! 926: rks = NULL;
! 927: nrks = 0;
! 928: out:
! 929: for (i = 0; i < nrks; i++) {
! 930: free(rks[i]->application);
! 931: freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
! 932: freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
! 933: freezero(rks[i], sizeof(*rks[i]));
! 934: }
! 935: free(rks);
! 936: fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
! 937: return r;
! 938: }
! 939: