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