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