diff options
Diffstat (limited to 'tools/sk-libfido2.c')
-rw-r--r-- | tools/sk-libfido2.c | 784 |
1 files changed, 0 insertions, 784 deletions
diff --git a/tools/sk-libfido2.c b/tools/sk-libfido2.c deleted file mode 100644 index 15aa813..0000000 --- a/tools/sk-libfido2.c +++ /dev/null | |||
@@ -1,784 +0,0 @@ | |||
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 <fcntl.h> | ||
18 | #include <stdint.h> | ||
19 | #include <stdlib.h> | ||
20 | #include <string.h> | ||
21 | #include <stdio.h> | ||
22 | #include <stddef.h> | ||
23 | #include <stdarg.h> | ||
24 | #ifdef HAVE_UNISTD_H | ||
25 | #include <unistd.h> | ||
26 | #endif | ||
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 | |||
38 | #ifndef SK_STANDALONE | ||
39 | #include "log.h" | ||
40 | #include "xmalloc.h" | ||
41 | #endif | ||
42 | |||
43 | /* #define SK_DEBUG 1 */ | ||
44 | |||
45 | #if defined(_WIN32) | ||
46 | #include <windows.h> | ||
47 | #include <winternl.h> | ||
48 | #include <winerror.h> | ||
49 | #include <bcrypt.h> | ||
50 | #include <sal.h> | ||
51 | #endif | ||
52 | |||
53 | #define MAX_FIDO_DEVICES 256 | ||
54 | |||
55 | /* Compatibility with OpenSSL 1.0.x */ | ||
56 | #if (OPENSSL_VERSION_NUMBER < 0x10100000L) | ||
57 | #define ECDSA_SIG_get0(sig, pr, ps) \ | ||
58 | do { \ | ||
59 | (*pr) = sig->r; \ | ||
60 | (*ps) = sig->s; \ | ||
61 | } while (0) | ||
62 | #endif | ||
63 | |||
64 | #define SK_VERSION_MAJOR 0x00020000 /* current API version */ | ||
65 | |||
66 | /* Flags */ | ||
67 | #define SK_USER_PRESENCE_REQD 0x01 | ||
68 | |||
69 | /* Algs */ | ||
70 | #define SK_ECDSA 0x00 | ||
71 | #define SK_ED25519 0x01 | ||
72 | |||
73 | struct 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 | |||
84 | struct 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 | |||
93 | /* If building as part of OpenSSH, then rename exported functions */ | ||
94 | #if !defined(SK_STANDALONE) | ||
95 | #define sk_api_version ssh_sk_api_version | ||
96 | #define sk_enroll ssh_sk_enroll | ||
97 | #define sk_sign ssh_sk_sign | ||
98 | #endif | ||
99 | |||
100 | /* Return the version of the middleware API */ | ||
101 | uint32_t sk_api_version(void); | ||
102 | |||
103 | /* Enroll a U2F key (private key generation) */ | ||
104 | int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, | ||
105 | const char *application, uint8_t flags, | ||
106 | struct sk_enroll_response **enroll_response); | ||
107 | |||
108 | /* Sign a challenge */ | ||
109 | int sk_sign(int alg, const uint8_t *message, size_t message_len, | ||
110 | const char *application, const uint8_t *key_handle, size_t key_handle_len, | ||
111 | uint8_t flags, struct sk_sign_response **sign_response); | ||
112 | |||
113 | #ifdef SK_DEBUG | ||
114 | static void skdebug(const char *func, const char *fmt, ...) | ||
115 | __attribute__((__format__ (printf, 2, 3))); | ||
116 | |||
117 | static void | ||
118 | skdebug(const char *func, const char *fmt, ...) | ||
119 | { | ||
120 | #if !defined(SK_STANDALONE) | ||
121 | char *msg; | ||
122 | va_list ap; | ||
123 | |||
124 | va_start(ap, fmt); | ||
125 | xvasprintf(&msg, fmt, ap); | ||
126 | va_end(ap); | ||
127 | debug("%s: %s", func, msg); | ||
128 | free(msg); | ||
129 | #else | ||
130 | va_list ap; | ||
131 | |||
132 | va_start(ap, fmt); | ||
133 | fprintf(stderr, "%s: ", func); | ||
134 | vfprintf(stderr, fmt, ap); | ||
135 | fputc('\n', stderr); | ||
136 | va_end(ap); | ||
137 | #endif /* !SK_STANDALONE */ | ||
138 | } | ||
139 | #else | ||
140 | #define skdebug(...) do { /* nothing */ } while (0) | ||
141 | #endif /* SK_DEBUG */ | ||
142 | |||
143 | uint32_t | ||
144 | sk_api_version(void) | ||
145 | { | ||
146 | return SK_VERSION_MAJOR; | ||
147 | } | ||
148 | |||
149 | /* Select the first identified FIDO device attached to the system */ | ||
150 | static char * | ||
151 | pick_first_device(void) | ||
152 | { | ||
153 | char *ret = NULL; | ||
154 | fido_dev_info_t *devlist = NULL; | ||
155 | size_t olen = 0; | ||
156 | int r; | ||
157 | const fido_dev_info_t *di; | ||
158 | |||
159 | if ((devlist = fido_dev_info_new(1)) == NULL) { | ||
160 | skdebug(__func__, "fido_dev_info_new failed"); | ||
161 | goto out; | ||
162 | } | ||
163 | if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) { | ||
164 | skdebug(__func__, "fido_dev_info_manifest failed: %s", | ||
165 | fido_strerr(r)); | ||
166 | goto out; | ||
167 | } | ||
168 | if (olen != 1) { | ||
169 | skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen); | ||
170 | goto out; | ||
171 | } | ||
172 | di = fido_dev_info_ptr(devlist, 0); | ||
173 | if ((ret = strdup(fido_dev_info_path(di))) == NULL) { | ||
174 | skdebug(__func__, "fido_dev_info_path failed"); | ||
175 | goto out; | ||
176 | } | ||
177 | out: | ||
178 | fido_dev_info_free(&devlist, 1); | ||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | #if defined(HAVE_ARC4RANDOM_BUF) | ||
183 | static int | ||
184 | get_random_challenge(uint8_t *ptr, size_t len) | ||
185 | { | ||
186 | arc4random_buf(ptr, len); | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | #elif defined(HAVE_GETENTROPY) | ||
191 | static int | ||
192 | get_random_challenge(uint8_t *ptr, size_t len) | ||
193 | { | ||
194 | if (getentropy(ptr, len) == -1) { | ||
195 | skdebug(__func__, "getentropy failed"); | ||
196 | return -1; | ||
197 | } | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | #elif defined(HAS_DEV_URANDOM) | ||
202 | static int | ||
203 | get_random_challenge(uint8_t *ptr, size_t len) | ||
204 | { | ||
205 | int fd; | ||
206 | ssize_t n; | ||
207 | |||
208 | if ((fd = open(FIDO_RANDOM_DEV, O_RDONLY)) < 0) { | ||
209 | skdebug(__func__, "open %s failed", FIDO_RANDOM_DEV); | ||
210 | return -1; | ||
211 | } | ||
212 | |||
213 | n = read(fd, ptr, len); | ||
214 | close(fd); | ||
215 | |||
216 | if (n < 0 || (size_t)n != len) { | ||
217 | skdebug(__func__, "read from %s failed", FIDO_RANDOM_DEV); | ||
218 | return -1; | ||
219 | } | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | #elif defined(_WIN32) | ||
224 | static int | ||
225 | get_random_challenge(uint8_t *ptr, size_t len) | ||
226 | { | ||
227 | NTSTATUS status; | ||
228 | |||
229 | status = BCryptGenRandom(NULL, ptr, len, | ||
230 | BCRYPT_USE_SYSTEM_PREFERRED_RNG); | ||
231 | if (!NT_SUCCESS(status)) | ||
232 | return -1; | ||
233 | |||
234 | return 0; | ||
235 | } | ||
236 | #else | ||
237 | #error "please provide an implementation of get_random_challenge() for your platform" | ||
238 | #endif | ||
239 | |||
240 | /* Check if the specified key handle exists on a given device. */ | ||
241 | static int | ||
242 | try_device(fido_dev_t *dev, const char *application, | ||
243 | const uint8_t *key_handle, size_t key_handle_len) | ||
244 | { | ||
245 | fido_assert_t *assert = NULL; | ||
246 | uint8_t challenge[32]; | ||
247 | int r = FIDO_ERR_INTERNAL; | ||
248 | |||
249 | if (get_random_challenge(challenge, sizeof(challenge)) == -1) { | ||
250 | skdebug(__func__, "get_random_challenge failed"); | ||
251 | goto out; | ||
252 | } | ||
253 | |||
254 | if ((assert = fido_assert_new()) == NULL) { | ||
255 | skdebug(__func__, "fido_assert_new failed"); | ||
256 | goto out; | ||
257 | } | ||
258 | if ((r = fido_assert_set_clientdata_hash(assert, challenge, | ||
259 | sizeof(challenge))) != FIDO_OK) { | ||
260 | skdebug(__func__, "fido_assert_set_clientdata_hash: %s", | ||
261 | fido_strerr(r)); | ||
262 | goto out; | ||
263 | } | ||
264 | if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { | ||
265 | skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); | ||
266 | goto out; | ||
267 | } | ||
268 | if ((r = fido_assert_allow_cred(assert, key_handle, | ||
269 | key_handle_len)) != FIDO_OK) { | ||
270 | skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); | ||
271 | goto out; | ||
272 | } | ||
273 | if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) { | ||
274 | skdebug(__func__, "fido_assert_up: %s", fido_strerr(r)); | ||
275 | goto out; | ||
276 | } | ||
277 | r = fido_dev_get_assert(dev, assert, NULL); | ||
278 | skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); | ||
279 | if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) { | ||
280 | /* U2F tokens may return this */ | ||
281 | r = FIDO_OK; | ||
282 | } | ||
283 | out: | ||
284 | fido_assert_free(&assert); | ||
285 | |||
286 | return r != FIDO_OK ? -1 : 0; | ||
287 | } | ||
288 | |||
289 | /* Iterate over configured devices looking for a specific key handle */ | ||
290 | static fido_dev_t * | ||
291 | find_device(const char *application, const uint8_t *key_handle, | ||
292 | size_t key_handle_len) | ||
293 | { | ||
294 | fido_dev_info_t *devlist = NULL; | ||
295 | fido_dev_t *dev = NULL; | ||
296 | size_t devlist_len = 0, i; | ||
297 | const char *path; | ||
298 | int r; | ||
299 | |||
300 | if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { | ||
301 | skdebug(__func__, "fido_dev_info_new failed"); | ||
302 | goto out; | ||
303 | } | ||
304 | if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES, | ||
305 | &devlist_len)) != FIDO_OK) { | ||
306 | skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r)); | ||
307 | goto out; | ||
308 | } | ||
309 | |||
310 | skdebug(__func__, "found %zu device(s)", devlist_len); | ||
311 | |||
312 | for (i = 0; i < devlist_len; i++) { | ||
313 | const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); | ||
314 | |||
315 | if (di == NULL) { | ||
316 | skdebug(__func__, "fido_dev_info_ptr %zu failed", i); | ||
317 | continue; | ||
318 | } | ||
319 | if ((path = fido_dev_info_path(di)) == NULL) { | ||
320 | skdebug(__func__, "fido_dev_info_path %zu failed", i); | ||
321 | continue; | ||
322 | } | ||
323 | skdebug(__func__, "trying device %zu: %s", i, path); | ||
324 | if ((dev = fido_dev_new()) == NULL) { | ||
325 | skdebug(__func__, "fido_dev_new failed"); | ||
326 | continue; | ||
327 | } | ||
328 | if ((r = fido_dev_open(dev, path)) != FIDO_OK) { | ||
329 | skdebug(__func__, "fido_dev_open failed"); | ||
330 | fido_dev_free(&dev); | ||
331 | continue; | ||
332 | } | ||
333 | if (try_device(dev, application, key_handle, | ||
334 | key_handle_len) == 0) { | ||
335 | skdebug(__func__, "found key"); | ||
336 | break; | ||
337 | } | ||
338 | fido_dev_close(dev); | ||
339 | fido_dev_free(&dev); | ||
340 | } | ||
341 | |||
342 | out: | ||
343 | if (devlist != NULL) | ||
344 | fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); | ||
345 | |||
346 | return dev; | ||
347 | } | ||
348 | |||
349 | #ifdef WITH_OPENSSL | ||
350 | /* | ||
351 | * The key returned via fido_cred_pubkey_ptr() is in affine coordinates, | ||
352 | * but the API expects a SEC1 octet string. | ||
353 | */ | ||
354 | static int | ||
355 | pack_public_key_ecdsa(fido_cred_t *cred, struct sk_enroll_response *response) | ||
356 | { | ||
357 | const uint8_t *ptr; | ||
358 | BIGNUM *x = NULL, *y = NULL; | ||
359 | EC_POINT *q = NULL; | ||
360 | EC_GROUP *g = NULL; | ||
361 | int ret = -1; | ||
362 | |||
363 | response->public_key = NULL; | ||
364 | response->public_key_len = 0; | ||
365 | |||
366 | if ((x = BN_new()) == NULL || | ||
367 | (y = BN_new()) == NULL || | ||
368 | (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || | ||
369 | (q = EC_POINT_new(g)) == NULL) { | ||
370 | skdebug(__func__, "libcrypto setup failed"); | ||
371 | goto out; | ||
372 | } | ||
373 | if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { | ||
374 | skdebug(__func__, "fido_cred_pubkey_ptr failed"); | ||
375 | goto out; | ||
376 | } | ||
377 | if (fido_cred_pubkey_len(cred) != 64) { | ||
378 | skdebug(__func__, "bad fido_cred_pubkey_len %zu", | ||
379 | fido_cred_pubkey_len(cred)); | ||
380 | goto out; | ||
381 | } | ||
382 | |||
383 | if (BN_bin2bn(ptr, 32, x) == NULL || | ||
384 | BN_bin2bn(ptr + 32, 32, y) == NULL) { | ||
385 | skdebug(__func__, "BN_bin2bn failed"); | ||
386 | goto out; | ||
387 | } | ||
388 | if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) { | ||
389 | skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed"); | ||
390 | goto out; | ||
391 | } | ||
392 | response->public_key_len = EC_POINT_point2oct(g, q, | ||
393 | POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); | ||
394 | if (response->public_key_len == 0 || response->public_key_len > 2048) { | ||
395 | skdebug(__func__, "bad pubkey length %zu", | ||
396 | response->public_key_len); | ||
397 | goto out; | ||
398 | } | ||
399 | if ((response->public_key = malloc(response->public_key_len)) == NULL) { | ||
400 | skdebug(__func__, "malloc pubkey failed"); | ||
401 | goto out; | ||
402 | } | ||
403 | if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED, | ||
404 | response->public_key, response->public_key_len, NULL) == 0) { | ||
405 | skdebug(__func__, "EC_POINT_point2oct failed"); | ||
406 | goto out; | ||
407 | } | ||
408 | /* success */ | ||
409 | ret = 0; | ||
410 | out: | ||
411 | if (ret != 0 && response->public_key != NULL) { | ||
412 | memset(response->public_key, 0, response->public_key_len); | ||
413 | free(response->public_key); | ||
414 | response->public_key = NULL; | ||
415 | } | ||
416 | EC_POINT_free(q); | ||
417 | EC_GROUP_free(g); | ||
418 | BN_clear_free(x); | ||
419 | BN_clear_free(y); | ||
420 | return ret; | ||
421 | } | ||
422 | #endif /* WITH_OPENSSL */ | ||
423 | |||
424 | static int | ||
425 | pack_public_key_ed25519(fido_cred_t *cred, struct sk_enroll_response *response) | ||
426 | { | ||
427 | const uint8_t *ptr; | ||
428 | size_t len; | ||
429 | int ret = -1; | ||
430 | |||
431 | response->public_key = NULL; | ||
432 | response->public_key_len = 0; | ||
433 | |||
434 | if ((len = fido_cred_pubkey_len(cred)) != 32) { | ||
435 | skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len); | ||
436 | goto out; | ||
437 | } | ||
438 | if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { | ||
439 | skdebug(__func__, "fido_cred_pubkey_ptr failed"); | ||
440 | goto out; | ||
441 | } | ||
442 | response->public_key_len = len; | ||
443 | if ((response->public_key = malloc(response->public_key_len)) == NULL) { | ||
444 | skdebug(__func__, "malloc pubkey failed"); | ||
445 | goto out; | ||
446 | } | ||
447 | memcpy(response->public_key, ptr, len); | ||
448 | ret = 0; | ||
449 | out: | ||
450 | if (ret != 0) | ||
451 | free(response->public_key); | ||
452 | return ret; | ||
453 | } | ||
454 | |||
455 | static int | ||
456 | pack_public_key(int alg, fido_cred_t *cred, struct sk_enroll_response *response) | ||
457 | { | ||
458 | switch(alg) { | ||
459 | #ifdef WITH_OPENSSL | ||
460 | case SK_ECDSA: | ||
461 | return pack_public_key_ecdsa(cred, response); | ||
462 | #endif /* WITH_OPENSSL */ | ||
463 | case SK_ED25519: | ||
464 | return pack_public_key_ed25519(cred, response); | ||
465 | default: | ||
466 | return -1; | ||
467 | } | ||
468 | } | ||
469 | |||
470 | int | ||
471 | sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, | ||
472 | const char *application, uint8_t flags, | ||
473 | struct sk_enroll_response **enroll_response) | ||
474 | { | ||
475 | fido_cred_t *cred = NULL; | ||
476 | fido_dev_t *dev = NULL; | ||
477 | const uint8_t *ptr; | ||
478 | uint8_t user_id[32]; | ||
479 | struct sk_enroll_response *response = NULL; | ||
480 | size_t len; | ||
481 | int cose_alg; | ||
482 | int ret = -1; | ||
483 | int r; | ||
484 | char *device = NULL; | ||
485 | |||
486 | (void)flags; /* XXX; unused */ | ||
487 | #ifdef SK_DEBUG | ||
488 | fido_init(FIDO_DEBUG); | ||
489 | #endif | ||
490 | if (enroll_response == NULL) { | ||
491 | skdebug(__func__, "enroll_response == NULL"); | ||
492 | goto out; | ||
493 | } | ||
494 | *enroll_response = NULL; | ||
495 | switch(alg) { | ||
496 | #ifdef WITH_OPENSSL | ||
497 | case SK_ECDSA: | ||
498 | cose_alg = COSE_ES256; | ||
499 | break; | ||
500 | #endif /* WITH_OPENSSL */ | ||
501 | case SK_ED25519: | ||
502 | cose_alg = COSE_EDDSA; | ||
503 | break; | ||
504 | default: | ||
505 | skdebug(__func__, "unsupported key type %d", alg); | ||
506 | goto out; | ||
507 | } | ||
508 | if ((device = pick_first_device()) == NULL) { | ||
509 | skdebug(__func__, "pick_first_device failed"); | ||
510 | goto out; | ||
511 | } | ||
512 | skdebug(__func__, "using device %s", device); | ||
513 | if ((cred = fido_cred_new()) == NULL) { | ||
514 | skdebug(__func__, "fido_cred_new failed"); | ||
515 | goto out; | ||
516 | } | ||
517 | memset(user_id, 0, sizeof(user_id)); | ||
518 | if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) { | ||
519 | skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r)); | ||
520 | goto out; | ||
521 | } | ||
522 | if ((r = fido_cred_set_clientdata_hash(cred, challenge, | ||
523 | challenge_len)) != FIDO_OK) { | ||
524 | skdebug(__func__, "fido_cred_set_clientdata_hash: %s", | ||
525 | fido_strerr(r)); | ||
526 | goto out; | ||
527 | } | ||
528 | if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id), | ||
529 | "openssh", "openssh", NULL)) != FIDO_OK) { | ||
530 | skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r)); | ||
531 | goto out; | ||
532 | } | ||
533 | if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) { | ||
534 | skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r)); | ||
535 | goto out; | ||
536 | } | ||
537 | if ((dev = fido_dev_new()) == NULL) { | ||
538 | skdebug(__func__, "fido_dev_new failed"); | ||
539 | goto out; | ||
540 | } | ||
541 | if ((r = fido_dev_open(dev, device)) != FIDO_OK) { | ||
542 | skdebug(__func__, "fido_dev_open: %s", fido_strerr(r)); | ||
543 | goto out; | ||
544 | } | ||
545 | if ((r = fido_dev_make_cred(dev, cred, NULL)) != FIDO_OK) { | ||
546 | skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r)); | ||
547 | goto out; | ||
548 | } | ||
549 | if (fido_cred_x5c_ptr(cred) != NULL) { | ||
550 | if ((r = fido_cred_verify(cred)) != FIDO_OK) { | ||
551 | skdebug(__func__, "fido_cred_verify: %s", | ||
552 | fido_strerr(r)); | ||
553 | goto out; | ||
554 | } | ||
555 | } else { | ||
556 | skdebug(__func__, "self-attested credential"); | ||
557 | if ((r = fido_cred_verify_self(cred)) != FIDO_OK) { | ||
558 | skdebug(__func__, "fido_cred_verify_self: %s", | ||
559 | fido_strerr(r)); | ||
560 | goto out; | ||
561 | } | ||
562 | } | ||
563 | if ((response = calloc(1, sizeof(*response))) == NULL) { | ||
564 | skdebug(__func__, "calloc response failed"); | ||
565 | goto out; | ||
566 | } | ||
567 | if (pack_public_key(alg, cred, response) != 0) { | ||
568 | skdebug(__func__, "pack_public_key failed"); | ||
569 | goto out; | ||
570 | } | ||
571 | if ((ptr = fido_cred_id_ptr(cred)) != NULL) { | ||
572 | len = fido_cred_id_len(cred); | ||
573 | if ((response->key_handle = calloc(1, len)) == NULL) { | ||
574 | skdebug(__func__, "calloc key handle failed"); | ||
575 | goto out; | ||
576 | } | ||
577 | memcpy(response->key_handle, ptr, len); | ||
578 | response->key_handle_len = len; | ||
579 | } | ||
580 | if ((ptr = fido_cred_sig_ptr(cred)) != NULL) { | ||
581 | len = fido_cred_sig_len(cred); | ||
582 | if ((response->signature = calloc(1, len)) == NULL) { | ||
583 | skdebug(__func__, "calloc signature failed"); | ||
584 | goto out; | ||
585 | } | ||
586 | memcpy(response->signature, ptr, len); | ||
587 | response->signature_len = len; | ||
588 | } | ||
589 | if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) { | ||
590 | len = fido_cred_x5c_len(cred); | ||
591 | if ((response->attestation_cert = calloc(1, len)) == NULL) { | ||
592 | skdebug(__func__, "calloc attestation cert failed"); | ||
593 | goto out; | ||
594 | } | ||
595 | memcpy(response->attestation_cert, ptr, len); | ||
596 | response->attestation_cert_len = len; | ||
597 | } | ||
598 | *enroll_response = response; | ||
599 | response = NULL; | ||
600 | ret = 0; | ||
601 | out: | ||
602 | free(device); | ||
603 | if (response != NULL) { | ||
604 | free(response->public_key); | ||
605 | free(response->key_handle); | ||
606 | free(response->signature); | ||
607 | free(response->attestation_cert); | ||
608 | free(response); | ||
609 | } | ||
610 | if (dev != NULL) { | ||
611 | fido_dev_close(dev); | ||
612 | fido_dev_free(&dev); | ||
613 | } | ||
614 | if (cred != NULL) { | ||
615 | fido_cred_free(&cred); | ||
616 | } | ||
617 | return ret; | ||
618 | } | ||
619 | |||
620 | #ifdef WITH_OPENSSL | ||
621 | static int | ||
622 | pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response) | ||
623 | { | ||
624 | ECDSA_SIG *sig = NULL; | ||
625 | const BIGNUM *sig_r, *sig_s; | ||
626 | const unsigned char *cp; | ||
627 | size_t sig_len; | ||
628 | int ret = -1; | ||
629 | |||
630 | cp = fido_assert_sig_ptr(assert, 0); | ||
631 | sig_len = fido_assert_sig_len(assert, 0); | ||
632 | if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) { | ||
633 | skdebug(__func__, "d2i_ECDSA_SIG failed"); | ||
634 | goto out; | ||
635 | } | ||
636 | ECDSA_SIG_get0(sig, &sig_r, &sig_s); | ||
637 | response->sig_r_len = BN_num_bytes(sig_r); | ||
638 | response->sig_s_len = BN_num_bytes(sig_s); | ||
639 | if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL || | ||
640 | (response->sig_s = calloc(1, response->sig_s_len)) == NULL) { | ||
641 | skdebug(__func__, "calloc signature failed"); | ||
642 | goto out; | ||
643 | } | ||
644 | BN_bn2bin(sig_r, response->sig_r); | ||
645 | BN_bn2bin(sig_s, response->sig_s); | ||
646 | ret = 0; | ||
647 | out: | ||
648 | ECDSA_SIG_free(sig); | ||
649 | if (ret != 0) { | ||
650 | free(response->sig_r); | ||
651 | free(response->sig_s); | ||
652 | response->sig_r = NULL; | ||
653 | response->sig_s = NULL; | ||
654 | } | ||
655 | return ret; | ||
656 | } | ||
657 | #endif /* WITH_OPENSSL */ | ||
658 | |||
659 | static int | ||
660 | pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response) | ||
661 | { | ||
662 | const unsigned char *ptr; | ||
663 | size_t len; | ||
664 | int ret = -1; | ||
665 | |||
666 | ptr = fido_assert_sig_ptr(assert, 0); | ||
667 | len = fido_assert_sig_len(assert, 0); | ||
668 | if (len != 64) { | ||
669 | skdebug(__func__, "bad length %zu", len); | ||
670 | goto out; | ||
671 | } | ||
672 | response->sig_r_len = len; | ||
673 | if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) { | ||
674 | skdebug(__func__, "calloc signature failed"); | ||
675 | goto out; | ||
676 | } | ||
677 | memcpy(response->sig_r, ptr, len); | ||
678 | ret = 0; | ||
679 | out: | ||
680 | if (ret != 0) { | ||
681 | free(response->sig_r); | ||
682 | response->sig_r = NULL; | ||
683 | } | ||
684 | return ret; | ||
685 | } | ||
686 | |||
687 | static int | ||
688 | pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response) | ||
689 | { | ||
690 | switch(alg) { | ||
691 | #ifdef WITH_OPENSSL | ||
692 | case SK_ECDSA: | ||
693 | return pack_sig_ecdsa(assert, response); | ||
694 | #endif /* WITH_OPENSSL */ | ||
695 | case SK_ED25519: | ||
696 | return pack_sig_ed25519(assert, response); | ||
697 | default: | ||
698 | return -1; | ||
699 | } | ||
700 | } | ||
701 | |||
702 | int | ||
703 | sk_sign(int alg, const uint8_t *message, size_t message_len, | ||
704 | const char *application, | ||
705 | const uint8_t *key_handle, size_t key_handle_len, | ||
706 | uint8_t flags, struct sk_sign_response **sign_response) | ||
707 | { | ||
708 | fido_assert_t *assert = NULL; | ||
709 | fido_dev_t *dev = NULL; | ||
710 | struct sk_sign_response *response = NULL; | ||
711 | int ret = -1; | ||
712 | int r; | ||
713 | |||
714 | #ifdef SK_DEBUG | ||
715 | fido_init(FIDO_DEBUG); | ||
716 | #endif | ||
717 | |||
718 | if (sign_response == NULL) { | ||
719 | skdebug(__func__, "sign_response == NULL"); | ||
720 | goto out; | ||
721 | } | ||
722 | *sign_response = NULL; | ||
723 | if ((dev = find_device(application, key_handle, | ||
724 | key_handle_len)) == NULL) { | ||
725 | skdebug(__func__, "couldn't find device for key handle"); | ||
726 | goto out; | ||
727 | } | ||
728 | if ((assert = fido_assert_new()) == NULL) { | ||
729 | skdebug(__func__, "fido_assert_new failed"); | ||
730 | goto out; | ||
731 | } | ||
732 | if ((r = fido_assert_set_clientdata_hash(assert, message, | ||
733 | message_len)) != FIDO_OK) { | ||
734 | skdebug(__func__, "fido_assert_set_clientdata_hash: %s", | ||
735 | fido_strerr(r)); | ||
736 | goto out; | ||
737 | } | ||
738 | if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { | ||
739 | skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); | ||
740 | goto out; | ||
741 | } | ||
742 | if ((r = fido_assert_allow_cred(assert, key_handle, | ||
743 | key_handle_len)) != FIDO_OK) { | ||
744 | skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); | ||
745 | goto out; | ||
746 | } | ||
747 | if ((r = fido_assert_set_up(assert, | ||
748 | (flags & SK_USER_PRESENCE_REQD) ? | ||
749 | FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) { | ||
750 | skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r)); | ||
751 | goto out; | ||
752 | } | ||
753 | if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) { | ||
754 | skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); | ||
755 | goto out; | ||
756 | } | ||
757 | if ((response = calloc(1, sizeof(*response))) == NULL) { | ||
758 | skdebug(__func__, "calloc response failed"); | ||
759 | goto out; | ||
760 | } | ||
761 | response->flags = fido_assert_flags(assert, 0); | ||
762 | response->counter = fido_assert_sigcount(assert, 0); | ||
763 | if (pack_sig(alg, assert, response) != 0) { | ||
764 | skdebug(__func__, "pack_sig failed"); | ||
765 | goto out; | ||
766 | } | ||
767 | *sign_response = response; | ||
768 | response = NULL; | ||
769 | ret = 0; | ||
770 | out: | ||
771 | if (response != NULL) { | ||
772 | free(response->sig_r); | ||
773 | free(response->sig_s); | ||
774 | free(response); | ||
775 | } | ||
776 | if (dev != NULL) { | ||
777 | fido_dev_close(dev); | ||
778 | fido_dev_free(&dev); | ||
779 | } | ||
780 | if (assert != NULL) { | ||
781 | fido_assert_free(&assert); | ||
782 | } | ||
783 | return ret; | ||
784 | } | ||