blob: cf783e205f69b14e59db83dffaa56a727bbed1c9 [file] [log] [blame]
djm@openbsd.org6bff9522019-11-14 21:27:29 +00001/*
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
naddy@openbsd.org723a5362019-11-18 16:08:57 +000028#ifdef WITH_OPENSSL
djm@openbsd.org6bff9522019-11-14 21:27:29 +000029#include <openssl/opensslv.h>
30#include <openssl/crypto.h>
31#include <openssl/bn.h>
32#include <openssl/ec.h>
33#include <openssl/ecdsa.h>
naddy@openbsd.org723a5362019-11-18 16:08:57 +000034#endif /* WITH_OPENSSL */
djm@openbsd.org6bff9522019-11-14 21:27:29 +000035
36#include <fido.h>
djm@openbsd.org14cea362019-12-30 09:21:16 +000037#include <fido/credman.h>
djm@openbsd.org6bff9522019-11-14 21:27:29 +000038
39#ifndef SK_STANDALONE
40#include "log.h"
41#include "xmalloc.h"
42#endif
43
44/* #define SK_DEBUG 1 */
45
46#define MAX_FIDO_DEVICES 256
47
48/* Compatibility with OpenSSH 1.0.x */
49#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
50#define ECDSA_SIG_get0(sig, pr, ps) \
51 do { \
52 (*pr) = sig->r; \
53 (*ps) = sig->s; \
54 } while (0)
55#endif
56
djm@openbsd.orgc312ca02020-01-06 02:00:46 +000057#define SK_VERSION_MAJOR 0x00040000 /* current API version */
djm@openbsd.org6bff9522019-11-14 21:27:29 +000058
59/* Flags */
djm@openbsd.org4532bd02019-12-30 09:19:52 +000060#define SK_USER_PRESENCE_REQD 0x01
61#define SK_USER_VERIFICATION_REQD 0x04
62#define SK_RESIDENT_KEY 0x20
djm@openbsd.org6bff9522019-11-14 21:27:29 +000063
64/* Algs */
65#define SK_ECDSA 0x00
66#define SK_ED25519 0x01
67
djm@openbsd.org43ce9642019-12-30 09:24:45 +000068/* Error codes */
69#define SSH_SK_ERR_GENERAL -1
70#define SSH_SK_ERR_UNSUPPORTED -2
71#define SSH_SK_ERR_PIN_REQUIRED -3
72
djm@openbsd.org6bff9522019-11-14 21:27:29 +000073struct sk_enroll_response {
74 uint8_t *public_key;
75 size_t public_key_len;
76 uint8_t *key_handle;
77 size_t key_handle_len;
78 uint8_t *signature;
79 size_t signature_len;
80 uint8_t *attestation_cert;
81 size_t attestation_cert_len;
82};
83
84struct sk_sign_response {
85 uint8_t flags;
86 uint32_t counter;
87 uint8_t *sig_r;
88 size_t sig_r_len;
89 uint8_t *sig_s;
90 size_t sig_s_len;
91};
92
djm@openbsd.org14cea362019-12-30 09:21:16 +000093struct sk_resident_key {
djm@openbsd.orgc312ca02020-01-06 02:00:46 +000094 uint32_t alg;
djm@openbsd.org14cea362019-12-30 09:21:16 +000095 size_t slot;
96 char *application;
97 struct sk_enroll_response key;
98};
99
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000100struct sk_option {
101 char *name;
102 char *value;
103 uint8_t required;
104};
105
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000106/* If building as part of OpenSSH, then rename exported functions */
107#if !defined(SK_STANDALONE)
djm@openbsd.org14cea362019-12-30 09:21:16 +0000108#define sk_api_version ssh_sk_api_version
109#define sk_enroll ssh_sk_enroll
110#define sk_sign ssh_sk_sign
111#define sk_load_resident_keys ssh_sk_load_resident_keys
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000112#endif
113
114/* Return the version of the middleware API */
115uint32_t sk_api_version(void);
116
117/* Enroll a U2F key (private key generation) */
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000118int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
djm@openbsd.orgc54cd182019-12-30 09:23:28 +0000119 const char *application, uint8_t flags, const char *pin,
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000120 struct sk_option **options, struct sk_enroll_response **enroll_response);
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000121
122/* Sign a challenge */
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000123int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len,
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000124 const char *application, const uint8_t *key_handle, size_t key_handle_len,
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000125 uint8_t flags, const char *pin, struct sk_option **options,
126 struct sk_sign_response **sign_response);
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000127
djm@openbsd.org14cea362019-12-30 09:21:16 +0000128/* Load resident keys */
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000129int sk_load_resident_keys(const char *pin, struct sk_option **options,
djm@openbsd.org14cea362019-12-30 09:21:16 +0000130 struct sk_resident_key ***rks, size_t *nrks);
131
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000132static void skdebug(const char *func, const char *fmt, ...)
133 __attribute__((__format__ (printf, 2, 3)));
134
135static void
136skdebug(const char *func, const char *fmt, ...)
137{
138#if !defined(SK_STANDALONE)
139 char *msg;
140 va_list ap;
141
142 va_start(ap, fmt);
143 xvasprintf(&msg, fmt, ap);
144 va_end(ap);
djm@openbsd.org22a82712019-11-15 02:20:06 +0000145 debug("%s: %s", func, msg);
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000146 free(msg);
147#elif defined(SK_DEBUG)
148 va_list ap;
149
150 va_start(ap, fmt);
151 fprintf(stderr, "%s: ", func);
152 vfprintf(stderr, fmt, ap);
153 fputc('\n', stderr);
154 va_end(ap);
155#else
156 (void)func; /* XXX */
157 (void)fmt; /* XXX */
158#endif
159}
160
161uint32_t
162sk_api_version(void)
163{
164 return SK_VERSION_MAJOR;
165}
166
167/* Select the first identified FIDO device attached to the system */
168static char *
169pick_first_device(void)
170{
171 char *ret = NULL;
172 fido_dev_info_t *devlist = NULL;
173 size_t olen = 0;
174 int r;
175 const fido_dev_info_t *di;
176
177 if ((devlist = fido_dev_info_new(1)) == NULL) {
178 skdebug(__func__, "fido_dev_info_new failed");
179 goto out;
180 }
181 if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) {
182 skdebug(__func__, "fido_dev_info_manifest failed: %s",
183 fido_strerr(r));
184 goto out;
185 }
186 if (olen != 1) {
187 skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen);
188 goto out;
189 }
190 di = fido_dev_info_ptr(devlist, 0);
191 if ((ret = strdup(fido_dev_info_path(di))) == NULL) {
192 skdebug(__func__, "fido_dev_info_path failed");
193 goto out;
194 }
195 out:
196 fido_dev_info_free(&devlist, 1);
197 return ret;
198}
199
200/* Check if the specified key handle exists on a given device. */
201static int
202try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len,
203 const char *application, const uint8_t *key_handle, size_t key_handle_len)
204{
205 fido_assert_t *assert = NULL;
206 int r = FIDO_ERR_INTERNAL;
207
208 if ((assert = fido_assert_new()) == NULL) {
209 skdebug(__func__, "fido_assert_new failed");
210 goto out;
211 }
212 if ((r = fido_assert_set_clientdata_hash(assert, message,
213 message_len)) != FIDO_OK) {
214 skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
215 fido_strerr(r));
216 goto out;
217 }
218 if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
219 skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
220 goto out;
221 }
222 if ((r = fido_assert_allow_cred(assert, key_handle,
223 key_handle_len)) != FIDO_OK) {
224 skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
225 goto out;
226 }
227 if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
228 skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
229 goto out;
230 }
231 r = fido_dev_get_assert(dev, assert, NULL);
232 skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
djm@openbsd.org01362cf2019-11-15 03:41:57 +0000233 if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
234 /* U2F tokens may return this */
235 r = FIDO_OK;
236 }
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000237 out:
238 fido_assert_free(&assert);
239
240 return r != FIDO_OK ? -1 : 0;
241}
242
243/* Iterate over configured devices looking for a specific key handle */
244static fido_dev_t *
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000245find_device(const char *path, const uint8_t *message, size_t message_len,
246 const char *application, const uint8_t *key_handle, size_t key_handle_len)
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000247{
248 fido_dev_info_t *devlist = NULL;
249 fido_dev_t *dev = NULL;
deraadt@openbsd.orgd165bb52019-11-15 05:26:56 +0000250 size_t devlist_len = 0, i;
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000251 int r;
252
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000253 if (path != NULL) {
254 if ((dev = fido_dev_new()) == NULL) {
255 skdebug(__func__, "fido_dev_new failed");
256 return NULL;
257 }
258 if ((r = fido_dev_open(dev, path)) != FIDO_OK) {
259 skdebug(__func__, "fido_dev_open failed");
260 fido_dev_free(&dev);
261 return NULL;
262 }
263 return dev;
264 }
265
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000266 if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
267 skdebug(__func__, "fido_dev_info_new failed");
268 goto out;
269 }
270 if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
271 &devlist_len)) != FIDO_OK) {
272 skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r));
273 goto out;
274 }
275
276 skdebug(__func__, "found %zu device(s)", devlist_len);
277
deraadt@openbsd.orgd165bb52019-11-15 05:26:56 +0000278 for (i = 0; i < devlist_len; i++) {
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000279 const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
280
281 if (di == NULL) {
282 skdebug(__func__, "fido_dev_info_ptr %zu failed", i);
283 continue;
284 }
285 if ((path = fido_dev_info_path(di)) == NULL) {
286 skdebug(__func__, "fido_dev_info_path %zu failed", i);
287 continue;
288 }
289 skdebug(__func__, "trying device %zu: %s", i, path);
290 if ((dev = fido_dev_new()) == NULL) {
291 skdebug(__func__, "fido_dev_new failed");
292 continue;
293 }
294 if ((r = fido_dev_open(dev, path)) != FIDO_OK) {
295 skdebug(__func__, "fido_dev_open failed");
296 fido_dev_free(&dev);
297 continue;
298 }
299 if (try_device(dev, message, message_len, application,
300 key_handle, key_handle_len) == 0) {
301 skdebug(__func__, "found key");
302 break;
303 }
304 fido_dev_close(dev);
305 fido_dev_free(&dev);
306 }
307
308 out:
309 if (devlist != NULL)
310 fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
311
312 return dev;
313}
314
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000315#ifdef WITH_OPENSSL
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000316/*
317 * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
318 * but the API expects a SEC1 octet string.
319 */
320static int
djm@openbsd.org14cea362019-12-30 09:21:16 +0000321pack_public_key_ecdsa(const fido_cred_t *cred,
322 struct sk_enroll_response *response)
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000323{
324 const uint8_t *ptr;
325 BIGNUM *x = NULL, *y = NULL;
326 EC_POINT *q = NULL;
327 EC_GROUP *g = NULL;
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000328 int ret = -1;
329
330 response->public_key = NULL;
331 response->public_key_len = 0;
332
djm@openbsd.orgfd1a9642019-11-15 06:00:20 +0000333 if ((x = BN_new()) == NULL ||
334 (y = BN_new()) == NULL ||
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000335 (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
336 (q = EC_POINT_new(g)) == NULL) {
337 skdebug(__func__, "libcrypto setup failed");
338 goto out;
339 }
340 if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
341 skdebug(__func__, "fido_cred_pubkey_ptr failed");
342 goto out;
343 }
344 if (fido_cred_pubkey_len(cred) != 64) {
345 skdebug(__func__, "bad fido_cred_pubkey_len %zu",
346 fido_cred_pubkey_len(cred));
347 goto out;
348 }
349
350 if (BN_bin2bn(ptr, 32, x) == NULL ||
351 BN_bin2bn(ptr + 32, 32, y) == NULL) {
352 skdebug(__func__, "BN_bin2bn failed");
353 goto out;
354 }
djm@openbsd.orgfd1a9642019-11-15 06:00:20 +0000355 if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) {
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000356 skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
357 goto out;
358 }
359 response->public_key_len = EC_POINT_point2oct(g, q,
djm@openbsd.orgfd1a9642019-11-15 06:00:20 +0000360 POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000361 if (response->public_key_len == 0 || response->public_key_len > 2048) {
362 skdebug(__func__, "bad pubkey length %zu",
363 response->public_key_len);
364 goto out;
365 }
366 if ((response->public_key = malloc(response->public_key_len)) == NULL) {
367 skdebug(__func__, "malloc pubkey failed");
368 goto out;
369 }
370 if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
djm@openbsd.orgfd1a9642019-11-15 06:00:20 +0000371 response->public_key, response->public_key_len, NULL) == 0) {
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000372 skdebug(__func__, "EC_POINT_point2oct failed");
373 goto out;
374 }
375 /* success */
376 ret = 0;
377 out:
378 if (ret != 0 && response->public_key != NULL) {
379 memset(response->public_key, 0, response->public_key_len);
380 free(response->public_key);
381 response->public_key = NULL;
382 }
383 EC_POINT_free(q);
384 EC_GROUP_free(g);
djm@openbsd.orgfd1a9642019-11-15 06:00:20 +0000385 BN_clear_free(x);
386 BN_clear_free(y);
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000387 return ret;
388}
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000389#endif /* WITH_OPENSSL */
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000390
391static int
djm@openbsd.org14cea362019-12-30 09:21:16 +0000392pack_public_key_ed25519(const fido_cred_t *cred,
393 struct sk_enroll_response *response)
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000394{
395 const uint8_t *ptr;
396 size_t len;
397 int ret = -1;
398
399 response->public_key = NULL;
400 response->public_key_len = 0;
401
402 if ((len = fido_cred_pubkey_len(cred)) != 32) {
403 skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
404 goto out;
405 }
406 if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
407 skdebug(__func__, "fido_cred_pubkey_ptr failed");
408 goto out;
409 }
410 response->public_key_len = len;
411 if ((response->public_key = malloc(response->public_key_len)) == NULL) {
412 skdebug(__func__, "malloc pubkey failed");
413 goto out;
414 }
415 memcpy(response->public_key, ptr, len);
416 ret = 0;
417 out:
418 if (ret != 0)
419 free(response->public_key);
420 return ret;
421}
422
423static int
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000424pack_public_key(uint32_t alg, const fido_cred_t *cred,
djm@openbsd.org14cea362019-12-30 09:21:16 +0000425 struct sk_enroll_response *response)
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000426{
427 switch(alg) {
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000428#ifdef WITH_OPENSSL
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000429 case SK_ECDSA:
430 return pack_public_key_ecdsa(cred, response);
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000431#endif /* WITH_OPENSSL */
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000432 case SK_ED25519:
433 return pack_public_key_ed25519(cred, response);
434 default:
435 return -1;
436 }
437}
438
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000439static int
440fidoerr_to_skerr(int fidoerr)
441{
442 switch (fidoerr) {
443 case FIDO_ERR_UNSUPPORTED_OPTION:
444 return SSH_SK_ERR_UNSUPPORTED;
445 case FIDO_ERR_PIN_REQUIRED:
446 case FIDO_ERR_PIN_INVALID:
447 return SSH_SK_ERR_PIN_REQUIRED;
448 default:
449 return -1;
450 }
451}
452
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000453static int
454check_enroll_options(struct sk_option **options, char **devicep,
455 uint8_t *user_id, size_t user_id_len)
456{
457 size_t i;
458
459 if (options == NULL)
460 return 0;
461 for (i = 0; options[i] != NULL; i++) {
462 if (strcmp(options[i]->name, "device") == 0) {
463 if ((*devicep = strdup(options[i]->value)) == NULL) {
464 skdebug(__func__, "strdup device failed");
465 return -1;
466 }
467 skdebug(__func__, "requested device %s", *devicep);
djm@openbsd.org3cc60c82020-01-06 02:39:30 +0000468 } else if (strcmp(options[i]->name, "user") == 0) {
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000469 if (strlcpy(user_id, options[i]->value, user_id_len) >=
470 user_id_len) {
471 skdebug(__func__, "user too long");
472 return -1;
473 }
474 skdebug(__func__, "requested user %s",
475 (char *)user_id);
476 } else {
477 skdebug(__func__, "requested unsupported option %s",
478 options[i]->name);
479 if (options[i]->required) {
480 skdebug(__func__, "unknown required option");
481 return -1;
482 }
483 }
484 }
485 return 0;
486}
487
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000488int
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000489sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
djm@openbsd.orgc54cd182019-12-30 09:23:28 +0000490 const char *application, uint8_t flags, const char *pin,
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000491 struct sk_option **options, struct sk_enroll_response **enroll_response)
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000492{
493 fido_cred_t *cred = NULL;
494 fido_dev_t *dev = NULL;
495 const uint8_t *ptr;
496 uint8_t user_id[32];
497 struct sk_enroll_response *response = NULL;
498 size_t len;
499 int cose_alg;
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000500 int ret = SSH_SK_ERR_GENERAL;
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000501 int r;
502 char *device = NULL;
503
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000504#ifdef SK_DEBUG
505 fido_init(FIDO_DEBUG);
506#endif
markus@openbsd.orgd4317782019-11-15 15:41:01 +0000507 if (enroll_response == NULL) {
508 skdebug(__func__, "enroll_response == NULL");
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000509 goto out;
510 }
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000511 memset(user_id, 0, sizeof(user_id));
512 if (check_enroll_options(options, &device,
513 user_id, sizeof(user_id)) != 0)
514 goto out; /* error already logged */
515
markus@openbsd.orgd4317782019-11-15 15:41:01 +0000516 *enroll_response = NULL;
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000517 switch(alg) {
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000518#ifdef WITH_OPENSSL
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000519 case SK_ECDSA:
520 cose_alg = COSE_ES256;
521 break;
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000522#endif /* WITH_OPENSSL */
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000523 case SK_ED25519:
524 cose_alg = COSE_EDDSA;
525 break;
526 default:
527 skdebug(__func__, "unsupported key type %d", alg);
528 goto out;
529 }
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000530 if (device == NULL && (device = pick_first_device()) == NULL) {
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000531 skdebug(__func__, "pick_first_device failed");
532 goto out;
533 }
534 skdebug(__func__, "using device %s", device);
535 if ((cred = fido_cred_new()) == NULL) {
536 skdebug(__func__, "fido_cred_new failed");
537 goto out;
538 }
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000539 if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
540 skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
541 goto out;
542 }
543 if ((r = fido_cred_set_clientdata_hash(cred, challenge,
544 challenge_len)) != FIDO_OK) {
545 skdebug(__func__, "fido_cred_set_clientdata_hash: %s",
546 fido_strerr(r));
547 goto out;
548 }
djm@openbsd.org4532bd02019-12-30 09:19:52 +0000549 if ((r = fido_cred_set_rk(cred, (flags & SK_RESIDENT_KEY) != 0 ?
550 FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
551 skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
552 goto out;
553 }
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000554 if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
555 "openssh", "openssh", NULL)) != FIDO_OK) {
556 skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
557 goto out;
558 }
559 if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
560 skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
561 goto out;
562 }
563 if ((dev = fido_dev_new()) == NULL) {
564 skdebug(__func__, "fido_dev_new failed");
565 goto out;
566 }
567 if ((r = fido_dev_open(dev, device)) != FIDO_OK) {
568 skdebug(__func__, "fido_dev_open: %s", fido_strerr(r));
569 goto out;
570 }
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000571 if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) {
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000572 skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000573 ret = fidoerr_to_skerr(r);
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000574 goto out;
575 }
576 if (fido_cred_x5c_ptr(cred) != NULL) {
577 if ((r = fido_cred_verify(cred)) != FIDO_OK) {
578 skdebug(__func__, "fido_cred_verify: %s",
579 fido_strerr(r));
580 goto out;
581 }
582 } else {
583 skdebug(__func__, "self-attested credential");
584 if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
585 skdebug(__func__, "fido_cred_verify_self: %s",
586 fido_strerr(r));
587 goto out;
588 }
589 }
590 if ((response = calloc(1, sizeof(*response))) == NULL) {
591 skdebug(__func__, "calloc response failed");
592 goto out;
593 }
594 if (pack_public_key(alg, cred, response) != 0) {
595 skdebug(__func__, "pack_public_key failed");
596 goto out;
597 }
598 if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
599 len = fido_cred_id_len(cred);
600 if ((response->key_handle = calloc(1, len)) == NULL) {
601 skdebug(__func__, "calloc key handle failed");
602 goto out;
603 }
604 memcpy(response->key_handle, ptr, len);
605 response->key_handle_len = len;
606 }
607 if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
608 len = fido_cred_sig_len(cred);
609 if ((response->signature = calloc(1, len)) == NULL) {
610 skdebug(__func__, "calloc signature failed");
611 goto out;
612 }
613 memcpy(response->signature, ptr, len);
614 response->signature_len = len;
615 }
616 if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
617 len = fido_cred_x5c_len(cred);
618 if ((response->attestation_cert = calloc(1, len)) == NULL) {
619 skdebug(__func__, "calloc attestation cert failed");
620 goto out;
621 }
622 memcpy(response->attestation_cert, ptr, len);
623 response->attestation_cert_len = len;
624 }
markus@openbsd.orgd4317782019-11-15 15:41:01 +0000625 *enroll_response = response;
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000626 response = NULL;
627 ret = 0;
628 out:
629 free(device);
630 if (response != NULL) {
631 free(response->public_key);
632 free(response->key_handle);
633 free(response->signature);
634 free(response->attestation_cert);
635 free(response);
636 }
637 if (dev != NULL) {
638 fido_dev_close(dev);
639 fido_dev_free(&dev);
640 }
641 if (cred != NULL) {
642 fido_cred_free(&cred);
643 }
644 return ret;
645}
646
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000647#ifdef WITH_OPENSSL
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000648static int
649pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
650{
651 ECDSA_SIG *sig = NULL;
652 const BIGNUM *sig_r, *sig_s;
653 const unsigned char *cp;
654 size_t sig_len;
655 int ret = -1;
656
657 cp = fido_assert_sig_ptr(assert, 0);
658 sig_len = fido_assert_sig_len(assert, 0);
659 if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
660 skdebug(__func__, "d2i_ECDSA_SIG failed");
661 goto out;
662 }
663 ECDSA_SIG_get0(sig, &sig_r, &sig_s);
664 response->sig_r_len = BN_num_bytes(sig_r);
665 response->sig_s_len = BN_num_bytes(sig_s);
666 if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
667 (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
668 skdebug(__func__, "calloc signature failed");
669 goto out;
670 }
671 BN_bn2bin(sig_r, response->sig_r);
672 BN_bn2bin(sig_s, response->sig_s);
673 ret = 0;
674 out:
675 ECDSA_SIG_free(sig);
676 if (ret != 0) {
677 free(response->sig_r);
678 free(response->sig_s);
679 response->sig_r = NULL;
680 response->sig_s = NULL;
681 }
682 return ret;
683}
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000684#endif /* WITH_OPENSSL */
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000685
686static int
687pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
688{
689 const unsigned char *ptr;
690 size_t len;
691 int ret = -1;
692
693 ptr = fido_assert_sig_ptr(assert, 0);
694 len = fido_assert_sig_len(assert, 0);
695 if (len != 64) {
696 skdebug(__func__, "bad length %zu", len);
697 goto out;
698 }
699 response->sig_r_len = len;
700 if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
701 skdebug(__func__, "calloc signature failed");
702 goto out;
703 }
704 memcpy(response->sig_r, ptr, len);
705 ret = 0;
706 out:
707 if (ret != 0) {
708 free(response->sig_r);
709 response->sig_r = NULL;
710 }
711 return ret;
712}
713
714static int
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000715pack_sig(uint32_t alg, fido_assert_t *assert,
716 struct sk_sign_response *response)
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000717{
718 switch(alg) {
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000719#ifdef WITH_OPENSSL
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000720 case SK_ECDSA:
721 return pack_sig_ecdsa(assert, response);
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000722#endif /* WITH_OPENSSL */
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000723 case SK_ED25519:
724 return pack_sig_ed25519(assert, response);
725 default:
726 return -1;
727 }
728}
729
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000730/* Checks sk_options for sk_sign() and sk_load_resident_keys() */
731static int
732check_sign_load_resident_options(struct sk_option **options, char **devicep)
733{
734 size_t i;
735
736 if (options == NULL)
737 return 0;
738 for (i = 0; options[i] != NULL; i++) {
739 if (strcmp(options[i]->name, "device") == 0) {
740 if ((*devicep = strdup(options[i]->value)) == NULL) {
741 skdebug(__func__, "strdup device failed");
742 return -1;
743 }
744 skdebug(__func__, "requested device %s", *devicep);
745 } else {
746 skdebug(__func__, "requested unsupported option %s",
747 options[i]->name);
748 if (options[i]->required) {
749 skdebug(__func__, "unknown required option");
750 return -1;
751 }
752 }
753 }
754 return 0;
755}
756
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000757int
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000758sk_sign(uint32_t alg, const uint8_t *message, size_t message_len,
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000759 const char *application,
760 const uint8_t *key_handle, size_t key_handle_len,
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000761 uint8_t flags, const char *pin, struct sk_option **options,
762 struct sk_sign_response **sign_response)
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000763{
764 fido_assert_t *assert = NULL;
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000765 char *device = NULL;
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000766 fido_dev_t *dev = NULL;
767 struct sk_sign_response *response = NULL;
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000768 int ret = SSH_SK_ERR_GENERAL;
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000769 int r;
770
771#ifdef SK_DEBUG
772 fido_init(FIDO_DEBUG);
773#endif
774
775 if (sign_response == NULL) {
776 skdebug(__func__, "sign_response == NULL");
777 goto out;
778 }
779 *sign_response = NULL;
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000780 if (check_sign_load_resident_options(options, &device) != 0)
781 goto out; /* error already logged */
782 if ((dev = find_device(device, message, message_len,
783 application, key_handle, key_handle_len)) == NULL) {
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000784 skdebug(__func__, "couldn't find device for key handle");
785 goto out;
786 }
787 if ((assert = fido_assert_new()) == NULL) {
788 skdebug(__func__, "fido_assert_new failed");
789 goto out;
790 }
791 if ((r = fido_assert_set_clientdata_hash(assert, message,
792 message_len)) != FIDO_OK) {
793 skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
794 fido_strerr(r));
795 goto out;
796 }
797 if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
798 skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
799 goto out;
800 }
801 if ((r = fido_assert_allow_cred(assert, key_handle,
802 key_handle_len)) != FIDO_OK) {
803 skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
804 goto out;
805 }
806 if ((r = fido_assert_set_up(assert,
807 (flags & SK_USER_PRESENCE_REQD) ?
808 FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
809 skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
810 goto out;
811 }
812 if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) {
813 skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
814 goto out;
815 }
816 if ((response = calloc(1, sizeof(*response))) == NULL) {
817 skdebug(__func__, "calloc response failed");
818 goto out;
819 }
820 response->flags = fido_assert_flags(assert, 0);
821 response->counter = fido_assert_sigcount(assert, 0);
822 if (pack_sig(alg, assert, response) != 0) {
823 skdebug(__func__, "pack_sig failed");
824 goto out;
825 }
826 *sign_response = response;
827 response = NULL;
828 ret = 0;
829 out:
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000830 free(device);
djm@openbsd.org6bff9522019-11-14 21:27:29 +0000831 if (response != NULL) {
832 free(response->sig_r);
833 free(response->sig_s);
834 free(response);
835 }
836 if (dev != NULL) {
837 fido_dev_close(dev);
838 fido_dev_free(&dev);
839 }
840 if (assert != NULL) {
841 fido_assert_free(&assert);
842 }
843 return ret;
844}
djm@openbsd.org14cea362019-12-30 09:21:16 +0000845
846static int
847read_rks(const char *devpath, const char *pin,
848 struct sk_resident_key ***rksp, size_t *nrksp)
849{
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000850 int ret = SSH_SK_ERR_GENERAL, r = -1;
djm@openbsd.org14cea362019-12-30 09:21:16 +0000851 fido_dev_t *dev = NULL;
852 fido_credman_metadata_t *metadata = NULL;
853 fido_credman_rp_t *rp = NULL;
854 fido_credman_rk_t *rk = NULL;
855 size_t i, j, nrp, nrk;
856 const fido_cred_t *cred;
857 struct sk_resident_key *srk = NULL, **tmp;
858
859 if ((dev = fido_dev_new()) == NULL) {
860 skdebug(__func__, "fido_dev_new failed");
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000861 return ret;
djm@openbsd.org14cea362019-12-30 09:21:16 +0000862 }
863 if ((r = fido_dev_open(dev, devpath)) != FIDO_OK) {
864 skdebug(__func__, "fido_dev_open %s failed: %s",
865 devpath, fido_strerr(r));
866 fido_dev_free(&dev);
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000867 return ret;
djm@openbsd.org14cea362019-12-30 09:21:16 +0000868 }
869 if ((metadata = fido_credman_metadata_new()) == NULL) {
djm@openbsd.org14cea362019-12-30 09:21:16 +0000870 skdebug(__func__, "alloc failed");
871 goto out;
872 }
873
874 if ((r = fido_credman_get_dev_metadata(dev, metadata, pin)) != 0) {
875 if (r == FIDO_ERR_INVALID_COMMAND) {
876 skdebug(__func__, "device %s does not support "
877 "resident keys", devpath);
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000878 ret = 0;
djm@openbsd.org14cea362019-12-30 09:21:16 +0000879 goto out;
880 }
881 skdebug(__func__, "get metadata for %s failed: %s",
882 devpath, fido_strerr(r));
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000883 ret = fidoerr_to_skerr(r);
djm@openbsd.org14cea362019-12-30 09:21:16 +0000884 goto out;
885 }
886 skdebug(__func__, "existing %llu, remaining %llu",
887 (unsigned long long)fido_credman_rk_existing(metadata),
888 (unsigned long long)fido_credman_rk_remaining(metadata));
889 if ((rp = fido_credman_rp_new()) == NULL) {
djm@openbsd.org14cea362019-12-30 09:21:16 +0000890 skdebug(__func__, "alloc rp failed");
891 goto out;
892 }
893 if ((r = fido_credman_get_dev_rp(dev, rp, pin)) != 0) {
894 skdebug(__func__, "get RPs for %s failed: %s",
895 devpath, fido_strerr(r));
896 goto out;
897 }
898 nrp = fido_credman_rp_count(rp);
899 skdebug(__func__, "Device %s has resident keys for %zu RPs",
900 devpath, nrp);
901
902 /* Iterate over RP IDs that have resident keys */
903 for (i = 0; i < nrp; i++) {
904 skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
905 i, fido_credman_rp_name(rp, i), fido_credman_rp_id(rp, i),
906 fido_credman_rp_id_hash_len(rp, i));
907
908 /* Skip non-SSH RP IDs */
909 if (strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
910 continue;
911
912 fido_credman_rk_free(&rk);
913 if ((rk = fido_credman_rk_new()) == NULL) {
djm@openbsd.org14cea362019-12-30 09:21:16 +0000914 skdebug(__func__, "alloc rk failed");
915 goto out;
916 }
917 if ((r = fido_credman_get_dev_rk(dev, fido_credman_rp_id(rp, i),
918 rk, pin)) != 0) {
919 skdebug(__func__, "get RKs for %s slot %zu failed: %s",
920 devpath, i, fido_strerr(r));
921 goto out;
922 }
923 nrk = fido_credman_rk_count(rk);
924 skdebug(__func__, "RP \"%s\" has %zu resident keys",
925 fido_credman_rp_id(rp, i), nrk);
926
927 /* Iterate over resident keys for this RP ID */
928 for (j = 0; j < nrk; j++) {
929 if ((cred = fido_credman_rk(rk, j)) == NULL) {
930 skdebug(__func__, "no RK in slot %zu", j);
931 continue;
932 }
933 skdebug(__func__, "Device %s RP \"%s\" slot %zu: "
934 "type %d", devpath, fido_credman_rp_id(rp, i), j,
935 fido_cred_type(cred));
936
937 /* build response entry */
938 if ((srk = calloc(1, sizeof(*srk))) == NULL ||
939 (srk->key.key_handle = calloc(1,
940 fido_cred_id_len(cred))) == NULL ||
941 (srk->application = strdup(fido_credman_rp_id(rp,
942 i))) == NULL) {
djm@openbsd.org14cea362019-12-30 09:21:16 +0000943 skdebug(__func__, "alloc sk_resident_key");
944 goto out;
945 }
946
947 srk->key.key_handle_len = fido_cred_id_len(cred);
948 memcpy(srk->key.key_handle,
949 fido_cred_id_ptr(cred),
950 srk->key.key_handle_len);
951
952 switch (fido_cred_type(cred)) {
953 case COSE_ES256:
954 srk->alg = SK_ECDSA;
955 break;
956 case COSE_EDDSA:
957 srk->alg = SK_ED25519;
958 break;
959 default:
960 skdebug(__func__, "unsupported key type %d",
961 fido_cred_type(cred));
962 goto out;
963 }
964
965 if ((r = pack_public_key(srk->alg, cred,
966 &srk->key)) != 0) {
967 skdebug(__func__, "pack public key failed");
968 goto out;
969 }
970 /* append */
971 if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
972 sizeof(**rksp))) == NULL) {
djm@openbsd.org14cea362019-12-30 09:21:16 +0000973 skdebug(__func__, "alloc rksp");
974 goto out;
975 }
976 *rksp = tmp;
977 (*rksp)[(*nrksp)++] = srk;
978 srk = NULL;
979 }
980 }
981 /* Success */
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000982 ret = 0;
djm@openbsd.org14cea362019-12-30 09:21:16 +0000983 out:
984 if (srk != NULL) {
985 free(srk->application);
986 freezero(srk->key.public_key, srk->key.public_key_len);
987 freezero(srk->key.key_handle, srk->key.key_handle_len);
988 freezero(srk, sizeof(*srk));
989 }
990 fido_credman_rp_free(&rp);
991 fido_credman_rk_free(&rk);
992 fido_dev_close(dev);
993 fido_dev_free(&dev);
994 fido_credman_metadata_free(&metadata);
djm@openbsd.org43ce9642019-12-30 09:24:45 +0000995 return ret;
djm@openbsd.org14cea362019-12-30 09:21:16 +0000996}
997
998int
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000999sk_load_resident_keys(const char *pin, struct sk_option **options,
djm@openbsd.org14cea362019-12-30 09:21:16 +00001000 struct sk_resident_key ***rksp, size_t *nrksp)
1001{
djm@openbsd.org43ce9642019-12-30 09:24:45 +00001002 int ret = SSH_SK_ERR_GENERAL, r = -1;
djm@openbsd.org14cea362019-12-30 09:21:16 +00001003 fido_dev_info_t *devlist = NULL;
1004 size_t i, ndev = 0, nrks = 0;
1005 const fido_dev_info_t *di;
1006 struct sk_resident_key **rks = NULL;
djm@openbsd.orgc312ca02020-01-06 02:00:46 +00001007 char *device = NULL;
djm@openbsd.org14cea362019-12-30 09:21:16 +00001008 *rksp = NULL;
1009 *nrksp = 0;
1010
djm@openbsd.orgc312ca02020-01-06 02:00:46 +00001011 if (check_sign_load_resident_options(options, &device) != 0)
1012 goto out; /* error already logged */
1013 if (device != NULL) {
1014 skdebug(__func__, "trying %s", device);
1015 if ((r = read_rks(device, pin, &rks, &nrks)) != 0) {
djm@openbsd.orgff5784e2020-01-06 02:24:28 +00001016 skdebug(__func__, "read_rks failed for %s", device);
djm@openbsd.orgc312ca02020-01-06 02:00:46 +00001017 ret = r;
1018 goto out;
1019 }
1020 } else {
1021 /* Try all devices */
1022 if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
1023 skdebug(__func__, "fido_dev_info_new failed");
1024 goto out;
1025 }
1026 if ((r = fido_dev_info_manifest(devlist,
1027 MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) {
1028 skdebug(__func__, "fido_dev_info_manifest failed: %s",
1029 fido_strerr(r));
1030 goto out;
1031 }
1032 for (i = 0; i < ndev; i++) {
1033 if ((di = fido_dev_info_ptr(devlist, i)) == NULL) {
1034 skdebug(__func__, "no dev info at %zu", i);
1035 continue;
1036 }
1037 skdebug(__func__, "trying %s", fido_dev_info_path(di));
1038 if ((r = read_rks(fido_dev_info_path(di), pin,
1039 &rks, &nrks)) != 0) {
1040 skdebug(__func__, "read_rks failed for %s",
1041 fido_dev_info_path(di));
1042 /* remember last error */
1043 ret = r;
1044 continue;
1045 }
djm@openbsd.org14cea362019-12-30 09:21:16 +00001046 }
1047 }
djm@openbsd.orgc312ca02020-01-06 02:00:46 +00001048 /* success, unless we have no keys but a specific error */
1049 if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
1050 ret = 0;
djm@openbsd.org14cea362019-12-30 09:21:16 +00001051 *rksp = rks;
1052 *nrksp = nrks;
1053 rks = NULL;
1054 nrks = 0;
1055 out:
djm@openbsd.orgc312ca02020-01-06 02:00:46 +00001056 free(device);
djm@openbsd.org14cea362019-12-30 09:21:16 +00001057 for (i = 0; i < nrks; i++) {
1058 free(rks[i]->application);
1059 freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
1060 freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
1061 freezero(rks[i], sizeof(*rks[i]));
1062 }
1063 free(rks);
1064 fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
djm@openbsd.org43ce9642019-12-30 09:24:45 +00001065 return ret;
djm@openbsd.org14cea362019-12-30 09:21:16 +00001066}
1067
djm@openbsd.org6bff9522019-11-14 21:27:29 +00001068#endif /* ENABLE_SK_INTERNAL */