1 /*
2 * Copyright (c) 2018-2021 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7 #include <openssl/ecdsa.h>
8 #include <openssl/sha.h>
9
10 #include "fido.h"
11 #include "fido/es256.h"
12 #include "fido/rs256.h"
13 #include "fido/eddsa.h"
14
15 static int
adjust_assert_count(const cbor_item_t * key,const cbor_item_t * val,void * arg)16 adjust_assert_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
17 {
18 fido_assert_t *assert = arg;
19 uint64_t n;
20
21 /* numberOfCredentials; see section 6.2 */
22 if (cbor_isa_uint(key) == false ||
23 cbor_int_get_width(key) != CBOR_INT_8 ||
24 cbor_get_uint8(key) != 5) {
25 fido_log_debug("%s: cbor_type", __func__);
26 return (0); /* ignore */
27 }
28
29 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
30 fido_log_debug("%s: cbor_decode_uint64", __func__);
31 return (-1);
32 }
33
34 if (assert->stmt_len != 0 || assert->stmt_cnt != 1 ||
35 (size_t)n < assert->stmt_cnt) {
36 fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu, n=%zu",
37 __func__, assert->stmt_len, assert->stmt_cnt, (size_t)n);
38 return (-1);
39 }
40
41 if (fido_assert_set_count(assert, (size_t)n) != FIDO_OK) {
42 fido_log_debug("%s: fido_assert_set_count", __func__);
43 return (-1);
44 }
45
46 assert->stmt_len = 0; /* XXX */
47
48 return (0);
49 }
50
51 static int
parse_assert_reply(const cbor_item_t * key,const cbor_item_t * val,void * arg)52 parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
53 {
54 fido_assert_stmt *stmt = arg;
55
56 if (cbor_isa_uint(key) == false ||
57 cbor_int_get_width(key) != CBOR_INT_8) {
58 fido_log_debug("%s: cbor type", __func__);
59 return (0); /* ignore */
60 }
61
62 switch (cbor_get_uint8(key)) {
63 case 1: /* credential id */
64 return (cbor_decode_cred_id(val, &stmt->id));
65 case 2: /* authdata */
66 return (cbor_decode_assert_authdata(val, &stmt->authdata_cbor,
67 &stmt->authdata, &stmt->authdata_ext));
68 case 3: /* signature */
69 return (fido_blob_decode(val, &stmt->sig));
70 case 4: /* user attributes */
71 return (cbor_decode_user(val, &stmt->user));
72 case 7: /* large blob key */
73 return (fido_blob_decode(val, &stmt->largeblob_key));
74 default: /* ignore */
75 fido_log_debug("%s: cbor type", __func__);
76 return (0);
77 }
78 }
79
80 static int
fido_dev_get_assert_tx(fido_dev_t * dev,fido_assert_t * assert,const es256_pk_t * pk,const fido_blob_t * ecdh,const char * pin)81 fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
82 const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin)
83 {
84 fido_blob_t f;
85 fido_opt_t uv = assert->uv;
86 cbor_item_t *argv[7];
87 const uint8_t cmd = CTAP_CBOR_ASSERT;
88 int r;
89
90 memset(argv, 0, sizeof(argv));
91 memset(&f, 0, sizeof(f));
92
93 /* do we have everything we need? */
94 if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
95 fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
96 (void *)assert->rp_id, (void *)assert->cdh.ptr);
97 r = FIDO_ERR_INVALID_ARGUMENT;
98 goto fail;
99 }
100
101 if ((argv[0] = cbor_build_string(assert->rp_id)) == NULL ||
102 (argv[1] = fido_blob_encode(&assert->cdh)) == NULL) {
103 fido_log_debug("%s: cbor encode", __func__);
104 r = FIDO_ERR_INTERNAL;
105 goto fail;
106 }
107
108 /* allowed credentials */
109 if (assert->allow_list.len) {
110 const fido_blob_array_t *cl = &assert->allow_list;
111 if ((argv[2] = cbor_encode_pubkey_list(cl)) == NULL) {
112 fido_log_debug("%s: cbor_encode_pubkey_list", __func__);
113 r = FIDO_ERR_INTERNAL;
114 goto fail;
115 }
116 }
117
118 if (assert->ext.mask)
119 if ((argv[3] = cbor_encode_assert_ext(dev, &assert->ext, ecdh,
120 pk)) == NULL) {
121 fido_log_debug("%s: cbor_encode_assert_ext", __func__);
122 r = FIDO_ERR_INTERNAL;
123 goto fail;
124 }
125
126 /* user verification */
127 if (pin != NULL || (uv == FIDO_OPT_TRUE &&
128 fido_dev_supports_permissions(dev))) {
129 if ((r = cbor_add_uv_params(dev, cmd, &assert->cdh, pk, ecdh,
130 pin, assert->rp_id, &argv[5], &argv[6])) != FIDO_OK) {
131 fido_log_debug("%s: cbor_add_uv_params", __func__);
132 goto fail;
133 }
134 uv = FIDO_OPT_OMIT;
135 }
136
137 /* options */
138 if (assert->up != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT)
139 if ((argv[4] = cbor_encode_assert_opt(assert->up, uv)) == NULL) {
140 fido_log_debug("%s: cbor_encode_assert_opt", __func__);
141 r = FIDO_ERR_INTERNAL;
142 goto fail;
143 }
144
145 /* frame and transmit */
146 if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
147 fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
148 fido_log_debug("%s: fido_tx", __func__);
149 r = FIDO_ERR_TX;
150 goto fail;
151 }
152
153 r = FIDO_OK;
154 fail:
155 cbor_vector_free(argv, nitems(argv));
156 free(f.ptr);
157
158 return (r);
159 }
160
161 static int
fido_dev_get_assert_rx(fido_dev_t * dev,fido_assert_t * assert,int ms)162 fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms)
163 {
164 unsigned char reply[FIDO_MAXMSG];
165 int reply_len;
166 int r;
167
168 fido_assert_reset_rx(assert);
169
170 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
171 ms)) < 0) {
172 fido_log_debug("%s: fido_rx", __func__);
173 return (FIDO_ERR_RX);
174 }
175
176 /* start with room for a single assertion */
177 if ((assert->stmt = calloc(1, sizeof(fido_assert_stmt))) == NULL)
178 return (FIDO_ERR_INTERNAL);
179
180 assert->stmt_len = 0;
181 assert->stmt_cnt = 1;
182
183 /* adjust as needed */
184 if ((r = cbor_parse_reply(reply, (size_t)reply_len, assert,
185 adjust_assert_count)) != FIDO_OK) {
186 fido_log_debug("%s: adjust_assert_count", __func__);
187 return (r);
188 }
189
190 /* parse the first assertion */
191 if ((r = cbor_parse_reply(reply, (size_t)reply_len,
192 &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
193 fido_log_debug("%s: parse_assert_reply", __func__);
194 return (r);
195 }
196
197 assert->stmt_len++;
198
199 return (FIDO_OK);
200 }
201
202 static int
fido_get_next_assert_tx(fido_dev_t * dev)203 fido_get_next_assert_tx(fido_dev_t *dev)
204 {
205 const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT };
206
207 if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor)) < 0) {
208 fido_log_debug("%s: fido_tx", __func__);
209 return (FIDO_ERR_TX);
210 }
211
212 return (FIDO_OK);
213 }
214
215 static int
fido_get_next_assert_rx(fido_dev_t * dev,fido_assert_t * assert,int ms)216 fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms)
217 {
218 unsigned char reply[FIDO_MAXMSG];
219 int reply_len;
220 int r;
221
222 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
223 ms)) < 0) {
224 fido_log_debug("%s: fido_rx", __func__);
225 return (FIDO_ERR_RX);
226 }
227
228 /* sanity check */
229 if (assert->stmt_len >= assert->stmt_cnt) {
230 fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu", __func__,
231 assert->stmt_len, assert->stmt_cnt);
232 return (FIDO_ERR_INTERNAL);
233 }
234
235 if ((r = cbor_parse_reply(reply, (size_t)reply_len,
236 &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
237 fido_log_debug("%s: parse_assert_reply", __func__);
238 return (r);
239 }
240
241 return (FIDO_OK);
242 }
243
244 static int
fido_dev_get_assert_wait(fido_dev_t * dev,fido_assert_t * assert,const es256_pk_t * pk,const fido_blob_t * ecdh,const char * pin,int ms)245 fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert,
246 const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int ms)
247 {
248 int r;
249
250 if ((r = fido_dev_get_assert_tx(dev, assert, pk, ecdh, pin)) != FIDO_OK ||
251 (r = fido_dev_get_assert_rx(dev, assert, ms)) != FIDO_OK)
252 return (r);
253
254 while (assert->stmt_len < assert->stmt_cnt) {
255 if ((r = fido_get_next_assert_tx(dev)) != FIDO_OK ||
256 (r = fido_get_next_assert_rx(dev, assert, ms)) != FIDO_OK)
257 return (r);
258 assert->stmt_len++;
259 }
260
261 return (FIDO_OK);
262 }
263
264 static int
decrypt_hmac_secrets(const fido_dev_t * dev,fido_assert_t * assert,const fido_blob_t * key)265 decrypt_hmac_secrets(const fido_dev_t *dev, fido_assert_t *assert,
266 const fido_blob_t *key)
267 {
268 for (size_t i = 0; i < assert->stmt_cnt; i++) {
269 fido_assert_stmt *stmt = &assert->stmt[i];
270 if (stmt->authdata_ext.hmac_secret_enc.ptr != NULL) {
271 if (aes256_cbc_dec(dev, key,
272 &stmt->authdata_ext.hmac_secret_enc,
273 &stmt->hmac_secret) < 0) {
274 fido_log_debug("%s: aes256_cbc_dec %zu",
275 __func__, i);
276 return (-1);
277 }
278 }
279 }
280
281 return (0);
282 }
283
284 int
fido_dev_get_assert(fido_dev_t * dev,fido_assert_t * assert,const char * pin)285 fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
286 {
287 fido_blob_t *ecdh = NULL;
288 es256_pk_t *pk = NULL;
289 int r;
290
291 #ifdef USE_WINHELLO
292 if (dev->flags & FIDO_DEV_WINHELLO)
293 return (fido_winhello_get_assert(dev, assert, pin));
294 #endif
295
296 if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
297 fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
298 (void *)assert->rp_id, (void *)assert->cdh.ptr);
299 return (FIDO_ERR_INVALID_ARGUMENT);
300 }
301
302 if (fido_dev_is_fido2(dev) == false) {
303 if (pin != NULL || assert->ext.mask != 0)
304 return (FIDO_ERR_UNSUPPORTED_OPTION);
305 return (u2f_authenticate(dev, assert, -1));
306 }
307
308 if (pin != NULL || (assert->uv == FIDO_OPT_TRUE &&
309 fido_dev_supports_permissions(dev)) ||
310 (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) {
311 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
312 fido_log_debug("%s: fido_do_ecdh", __func__);
313 goto fail;
314 }
315 }
316
317 r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, -1);
318 if (r == FIDO_OK && (assert->ext.mask & FIDO_EXT_HMAC_SECRET))
319 if (decrypt_hmac_secrets(dev, assert, ecdh) < 0) {
320 fido_log_debug("%s: decrypt_hmac_secrets", __func__);
321 r = FIDO_ERR_INTERNAL;
322 goto fail;
323 }
324
325 fail:
326 es256_pk_free(&pk);
327 fido_blob_free(&ecdh);
328
329 return (r);
330 }
331
332 int
fido_check_flags(uint8_t flags,fido_opt_t up,fido_opt_t uv)333 fido_check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv)
334 {
335 fido_log_debug("%s: flags=%02x", __func__, flags);
336 fido_log_debug("%s: up=%d, uv=%d", __func__, up, uv);
337
338 if (up == FIDO_OPT_TRUE &&
339 (flags & CTAP_AUTHDATA_USER_PRESENT) == 0) {
340 fido_log_debug("%s: CTAP_AUTHDATA_USER_PRESENT", __func__);
341 return (-1); /* user not present */
342 }
343
344 if (uv == FIDO_OPT_TRUE &&
345 (flags & CTAP_AUTHDATA_USER_VERIFIED) == 0) {
346 fido_log_debug("%s: CTAP_AUTHDATA_USER_VERIFIED", __func__);
347 return (-1); /* user not verified */
348 }
349
350 return (0);
351 }
352
353 static int
check_extensions(int authdata_ext,int ext)354 check_extensions(int authdata_ext, int ext)
355 {
356 /* XXX: largeBlobKey is not part of extensions map */
357 ext &= ~FIDO_EXT_LARGEBLOB_KEY;
358 if (authdata_ext != ext) {
359 fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__,
360 authdata_ext, ext);
361 return (-1);
362 }
363
364 return (0);
365 }
366
367 int
fido_get_signed_hash(int cose_alg,fido_blob_t * dgst,const fido_blob_t * clientdata,const fido_blob_t * authdata_cbor)368 fido_get_signed_hash(int cose_alg, fido_blob_t *dgst,
369 const fido_blob_t *clientdata, const fido_blob_t *authdata_cbor)
370 {
371 cbor_item_t *item = NULL;
372 unsigned char *authdata_ptr = NULL;
373 size_t authdata_len;
374 struct cbor_load_result cbor;
375 SHA256_CTX ctx;
376 int ok = -1;
377
378 if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len,
379 &cbor)) == NULL || cbor_isa_bytestring(item) == false ||
380 cbor_bytestring_is_definite(item) == false) {
381 fido_log_debug("%s: authdata", __func__);
382 goto fail;
383 }
384
385 authdata_ptr = cbor_bytestring_handle(item);
386 authdata_len = cbor_bytestring_length(item);
387
388 if (cose_alg != COSE_EDDSA) {
389 if (dgst->len < SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 ||
390 SHA256_Update(&ctx, authdata_ptr, authdata_len) == 0 ||
391 SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 ||
392 SHA256_Final(dgst->ptr, &ctx) == 0) {
393 fido_log_debug("%s: sha256", __func__);
394 goto fail;
395 }
396 dgst->len = SHA256_DIGEST_LENGTH;
397 } else {
398 if (SIZE_MAX - authdata_len < clientdata->len ||
399 dgst->len < authdata_len + clientdata->len) {
400 fido_log_debug("%s: memcpy", __func__);
401 goto fail;
402 }
403 memcpy(dgst->ptr, authdata_ptr, authdata_len);
404 memcpy(dgst->ptr + authdata_len, clientdata->ptr,
405 clientdata->len);
406 dgst->len = authdata_len + clientdata->len;
407 }
408
409 ok = 0;
410 fail:
411 if (item != NULL)
412 cbor_decref(&item);
413
414 return (ok);
415 }
416
417 int
fido_verify_sig_es256(const fido_blob_t * dgst,const es256_pk_t * pk,const fido_blob_t * sig)418 fido_verify_sig_es256(const fido_blob_t *dgst, const es256_pk_t *pk,
419 const fido_blob_t *sig)
420 {
421 EVP_PKEY *pkey = NULL;
422 EC_KEY *ec = NULL;
423 int ok = -1;
424
425 /* ECDSA_verify needs ints */
426 if (dgst->len > INT_MAX || sig->len > INT_MAX) {
427 fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
428 dgst->len, sig->len);
429 return (-1);
430 }
431
432 if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL ||
433 (ec = __UNCONST(EVP_PKEY_get0_EC_KEY(pkey))) == NULL) {
434 fido_log_debug("%s: pk -> ec", __func__);
435 goto fail;
436 }
437
438 if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr,
439 (int)sig->len, ec) != 1) {
440 fido_log_debug("%s: ECDSA_verify", __func__);
441 goto fail;
442 }
443
444 ok = 0;
445 fail:
446 if (pkey != NULL)
447 EVP_PKEY_free(pkey);
448
449 return (ok);
450 }
451
452 int
fido_verify_sig_rs256(const fido_blob_t * dgst,const rs256_pk_t * pk,const fido_blob_t * sig)453 fido_verify_sig_rs256(const fido_blob_t *dgst, const rs256_pk_t *pk,
454 const fido_blob_t *sig)
455 {
456 EVP_PKEY *pkey = NULL;
457 RSA *rsa = NULL;
458 int ok = -1;
459
460 /* RSA_verify needs unsigned ints */
461 if (dgst->len > UINT_MAX || sig->len > UINT_MAX) {
462 fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
463 dgst->len, sig->len);
464 return (-1);
465 }
466
467 if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL ||
468 (rsa = __UNCONST(EVP_PKEY_get0_RSA(pkey))) == NULL) {
469 fido_log_debug("%s: pk -> ec", __func__);
470 goto fail;
471 }
472
473 if (RSA_verify(NID_sha256, dgst->ptr, (unsigned int)dgst->len, sig->ptr,
474 (unsigned int)sig->len, rsa) != 1) {
475 fido_log_debug("%s: RSA_verify", __func__);
476 goto fail;
477 }
478
479 ok = 0;
480 fail:
481 if (pkey != NULL)
482 EVP_PKEY_free(pkey);
483
484 return (ok);
485 }
486
487 int
fido_verify_sig_eddsa(const fido_blob_t * dgst,const eddsa_pk_t * pk,const fido_blob_t * sig)488 fido_verify_sig_eddsa(const fido_blob_t *dgst, const eddsa_pk_t *pk,
489 const fido_blob_t *sig)
490 {
491 EVP_PKEY *pkey = NULL;
492 EVP_MD_CTX *mdctx = NULL;
493 int ok = -1;
494
495 /* EVP_DigestVerify needs ints */
496 if (dgst->len > INT_MAX || sig->len > INT_MAX) {
497 fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
498 dgst->len, sig->len);
499 return (-1);
500 }
501
502 if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
503 fido_log_debug("%s: pk -> pkey", __func__);
504 goto fail;
505 }
506
507 if ((mdctx = EVP_MD_CTX_new()) == NULL) {
508 fido_log_debug("%s: EVP_MD_CTX_new", __func__);
509 goto fail;
510 }
511
512 if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) != 1) {
513 fido_log_debug("%s: EVP_DigestVerifyInit", __func__);
514 goto fail;
515 }
516
517 if (EVP_DigestVerify(mdctx, sig->ptr, sig->len, dgst->ptr,
518 dgst->len) != 1) {
519 fido_log_debug("%s: EVP_DigestVerify", __func__);
520 goto fail;
521 }
522
523 ok = 0;
524 fail:
525 if (mdctx != NULL)
526 EVP_MD_CTX_free(mdctx);
527
528 if (pkey != NULL)
529 EVP_PKEY_free(pkey);
530
531 return (ok);
532 }
533
534 int
fido_assert_verify(const fido_assert_t * assert,size_t idx,int cose_alg,const void * pk)535 fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg,
536 const void *pk)
537 {
538 unsigned char buf[1024]; /* XXX */
539 fido_blob_t dgst;
540 const fido_assert_stmt *stmt = NULL;
541 int ok = -1;
542 int r;
543
544 dgst.ptr = buf;
545 dgst.len = sizeof(buf);
546
547 if (idx >= assert->stmt_len || pk == NULL) {
548 r = FIDO_ERR_INVALID_ARGUMENT;
549 goto out;
550 }
551
552 stmt = &assert->stmt[idx];
553
554 /* do we have everything we need? */
555 if (assert->cdh.ptr == NULL || assert->rp_id == NULL ||
556 stmt->authdata_cbor.ptr == NULL || stmt->sig.ptr == NULL) {
557 fido_log_debug("%s: cdh=%p, rp_id=%s, authdata=%p, sig=%p",
558 __func__, (void *)assert->cdh.ptr, assert->rp_id,
559 (void *)stmt->authdata_cbor.ptr, (void *)stmt->sig.ptr);
560 r = FIDO_ERR_INVALID_ARGUMENT;
561 goto out;
562 }
563
564 if (fido_check_flags(stmt->authdata.flags, assert->up,
565 assert->uv) < 0) {
566 fido_log_debug("%s: fido_check_flags", __func__);
567 r = FIDO_ERR_INVALID_PARAM;
568 goto out;
569 }
570
571 if (check_extensions(stmt->authdata_ext.mask, assert->ext.mask) < 0) {
572 fido_log_debug("%s: check_extensions", __func__);
573 r = FIDO_ERR_INVALID_PARAM;
574 goto out;
575 }
576
577 if (fido_check_rp_id(assert->rp_id, stmt->authdata.rp_id_hash) != 0) {
578 fido_log_debug("%s: fido_check_rp_id", __func__);
579 r = FIDO_ERR_INVALID_PARAM;
580 goto out;
581 }
582
583 if (fido_get_signed_hash(cose_alg, &dgst, &assert->cdh,
584 &stmt->authdata_cbor) < 0) {
585 fido_log_debug("%s: fido_get_signed_hash", __func__);
586 r = FIDO_ERR_INTERNAL;
587 goto out;
588 }
589
590 switch (cose_alg) {
591 case COSE_ES256:
592 ok = fido_verify_sig_es256(&dgst, pk, &stmt->sig);
593 break;
594 case COSE_RS256:
595 ok = fido_verify_sig_rs256(&dgst, pk, &stmt->sig);
596 break;
597 case COSE_EDDSA:
598 ok = fido_verify_sig_eddsa(&dgst, pk, &stmt->sig);
599 break;
600 default:
601 fido_log_debug("%s: unsupported cose_alg %d", __func__,
602 cose_alg);
603 r = FIDO_ERR_UNSUPPORTED_OPTION;
604 goto out;
605 }
606
607 if (ok < 0)
608 r = FIDO_ERR_INVALID_SIG;
609 else
610 r = FIDO_OK;
611 out:
612 explicit_bzero(buf, sizeof(buf));
613
614 return (r);
615 }
616
617 int
fido_assert_set_clientdata(fido_assert_t * assert,const unsigned char * data,size_t data_len)618 fido_assert_set_clientdata(fido_assert_t *assert, const unsigned char *data,
619 size_t data_len)
620 {
621 if (!fido_blob_is_empty(&assert->cdh) ||
622 fido_blob_set(&assert->cd, data, data_len) < 0) {
623 return (FIDO_ERR_INVALID_ARGUMENT);
624 }
625 if (fido_sha256(&assert->cdh, data, data_len) < 0) {
626 fido_blob_reset(&assert->cd);
627 return (FIDO_ERR_INTERNAL);
628 }
629
630 return (FIDO_OK);
631 }
632
633 int
fido_assert_set_clientdata_hash(fido_assert_t * assert,const unsigned char * hash,size_t hash_len)634 fido_assert_set_clientdata_hash(fido_assert_t *assert,
635 const unsigned char *hash, size_t hash_len)
636 {
637 if (!fido_blob_is_empty(&assert->cd) ||
638 fido_blob_set(&assert->cdh, hash, hash_len) < 0)
639 return (FIDO_ERR_INVALID_ARGUMENT);
640
641 return (FIDO_OK);
642 }
643
644 int
fido_assert_set_hmac_salt(fido_assert_t * assert,const unsigned char * salt,size_t salt_len)645 fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt,
646 size_t salt_len)
647 {
648 if ((salt_len != 32 && salt_len != 64) ||
649 fido_blob_set(&assert->ext.hmac_salt, salt, salt_len) < 0)
650 return (FIDO_ERR_INVALID_ARGUMENT);
651
652 return (FIDO_OK);
653 }
654
655 int
fido_assert_set_hmac_secret(fido_assert_t * assert,size_t idx,const unsigned char * secret,size_t secret_len)656 fido_assert_set_hmac_secret(fido_assert_t *assert, size_t idx,
657 const unsigned char *secret, size_t secret_len)
658 {
659 if (idx >= assert->stmt_len || (secret_len != 32 && secret_len != 64) ||
660 fido_blob_set(&assert->stmt[idx].hmac_secret, secret,
661 secret_len) < 0)
662 return (FIDO_ERR_INVALID_ARGUMENT);
663
664 return (FIDO_OK);
665 }
666
667 int
fido_assert_set_rp(fido_assert_t * assert,const char * id)668 fido_assert_set_rp(fido_assert_t *assert, const char *id)
669 {
670 if (assert->rp_id != NULL) {
671 free(assert->rp_id);
672 assert->rp_id = NULL;
673 }
674
675 if (id == NULL)
676 return (FIDO_ERR_INVALID_ARGUMENT);
677
678 if ((assert->rp_id = strdup(id)) == NULL)
679 return (FIDO_ERR_INTERNAL);
680
681 return (FIDO_OK);
682 }
683
684 int
fido_assert_allow_cred(fido_assert_t * assert,const unsigned char * ptr,size_t len)685 fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr,
686 size_t len)
687 {
688 fido_blob_t id;
689 fido_blob_t *list_ptr;
690 int r;
691
692 memset(&id, 0, sizeof(id));
693
694 if (assert->allow_list.len == SIZE_MAX) {
695 r = FIDO_ERR_INVALID_ARGUMENT;
696 goto fail;
697 }
698
699 if (fido_blob_set(&id, ptr, len) < 0 || (list_ptr =
700 recallocarray(assert->allow_list.ptr, assert->allow_list.len,
701 assert->allow_list.len + 1, sizeof(fido_blob_t))) == NULL) {
702 r = FIDO_ERR_INVALID_ARGUMENT;
703 goto fail;
704 }
705
706 list_ptr[assert->allow_list.len++] = id;
707 assert->allow_list.ptr = list_ptr;
708
709 return (FIDO_OK);
710 fail:
711 free(id.ptr);
712
713 return (r);
714
715 }
716
717 int
fido_assert_set_extensions(fido_assert_t * assert,int ext)718 fido_assert_set_extensions(fido_assert_t *assert, int ext)
719 {
720 if (ext == 0)
721 assert->ext.mask = 0;
722 else {
723 if ((ext & FIDO_EXT_ASSERT_MASK) != ext)
724 return (FIDO_ERR_INVALID_ARGUMENT);
725 assert->ext.mask |= ext;
726 }
727
728 return (FIDO_OK);
729 }
730
731 int
fido_assert_set_options(fido_assert_t * assert,bool up,bool uv)732 fido_assert_set_options(fido_assert_t *assert, bool up, bool uv)
733 {
734 assert->up = up ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
735 assert->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
736
737 return (FIDO_OK);
738 }
739
740 int
fido_assert_set_up(fido_assert_t * assert,fido_opt_t up)741 fido_assert_set_up(fido_assert_t *assert, fido_opt_t up)
742 {
743 assert->up = up;
744
745 return (FIDO_OK);
746 }
747
748 int
fido_assert_set_uv(fido_assert_t * assert,fido_opt_t uv)749 fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv)
750 {
751 assert->uv = uv;
752
753 return (FIDO_OK);
754 }
755
756 const unsigned char *
fido_assert_clientdata_hash_ptr(const fido_assert_t * assert)757 fido_assert_clientdata_hash_ptr(const fido_assert_t *assert)
758 {
759 return (assert->cdh.ptr);
760 }
761
762 size_t
fido_assert_clientdata_hash_len(const fido_assert_t * assert)763 fido_assert_clientdata_hash_len(const fido_assert_t *assert)
764 {
765 return (assert->cdh.len);
766 }
767
768 fido_assert_t *
fido_assert_new(void)769 fido_assert_new(void)
770 {
771 return (calloc(1, sizeof(fido_assert_t)));
772 }
773
774 void
fido_assert_reset_tx(fido_assert_t * assert)775 fido_assert_reset_tx(fido_assert_t *assert)
776 {
777 free(assert->rp_id);
778 fido_blob_reset(&assert->cd);
779 fido_blob_reset(&assert->cdh);
780 fido_blob_reset(&assert->ext.hmac_salt);
781 fido_free_blob_array(&assert->allow_list);
782 memset(&assert->ext, 0, sizeof(assert->ext));
783 memset(&assert->allow_list, 0, sizeof(assert->allow_list));
784 assert->rp_id = NULL;
785 assert->up = FIDO_OPT_OMIT;
786 assert->uv = FIDO_OPT_OMIT;
787 }
788
fido_assert_reset_extattr(fido_assert_extattr_t * ext)789 static void fido_assert_reset_extattr(fido_assert_extattr_t *ext)
790 {
791 fido_blob_reset(&ext->hmac_secret_enc);
792 fido_blob_reset(&ext->blob);
793 memset(ext, 0, sizeof(*ext));
794 }
795
796 void
fido_assert_reset_rx(fido_assert_t * assert)797 fido_assert_reset_rx(fido_assert_t *assert)
798 {
799 for (size_t i = 0; i < assert->stmt_cnt; i++) {
800 free(assert->stmt[i].user.icon);
801 free(assert->stmt[i].user.name);
802 free(assert->stmt[i].user.display_name);
803 fido_blob_reset(&assert->stmt[i].user.id);
804 fido_blob_reset(&assert->stmt[i].id);
805 fido_blob_reset(&assert->stmt[i].hmac_secret);
806 fido_blob_reset(&assert->stmt[i].authdata_cbor);
807 fido_blob_reset(&assert->stmt[i].largeblob_key);
808 fido_blob_reset(&assert->stmt[i].sig);
809 fido_assert_reset_extattr(&assert->stmt[i].authdata_ext);
810 memset(&assert->stmt[i], 0, sizeof(assert->stmt[i]));
811 }
812 free(assert->stmt);
813 assert->stmt = NULL;
814 assert->stmt_len = 0;
815 assert->stmt_cnt = 0;
816 }
817
818 void
fido_assert_free(fido_assert_t ** assert_p)819 fido_assert_free(fido_assert_t **assert_p)
820 {
821 fido_assert_t *assert;
822
823 if (assert_p == NULL || (assert = *assert_p) == NULL)
824 return;
825 fido_assert_reset_tx(assert);
826 fido_assert_reset_rx(assert);
827 free(assert);
828 *assert_p = NULL;
829 }
830
831 size_t
fido_assert_count(const fido_assert_t * assert)832 fido_assert_count(const fido_assert_t *assert)
833 {
834 return (assert->stmt_len);
835 }
836
837 const char *
fido_assert_rp_id(const fido_assert_t * assert)838 fido_assert_rp_id(const fido_assert_t *assert)
839 {
840 return (assert->rp_id);
841 }
842
843 uint8_t
fido_assert_flags(const fido_assert_t * assert,size_t idx)844 fido_assert_flags(const fido_assert_t *assert, size_t idx)
845 {
846 if (idx >= assert->stmt_len)
847 return (0);
848
849 return (assert->stmt[idx].authdata.flags);
850 }
851
852 uint32_t
fido_assert_sigcount(const fido_assert_t * assert,size_t idx)853 fido_assert_sigcount(const fido_assert_t *assert, size_t idx)
854 {
855 if (idx >= assert->stmt_len)
856 return (0);
857
858 return (assert->stmt[idx].authdata.sigcount);
859 }
860
861 const unsigned char *
fido_assert_authdata_ptr(const fido_assert_t * assert,size_t idx)862 fido_assert_authdata_ptr(const fido_assert_t *assert, size_t idx)
863 {
864 if (idx >= assert->stmt_len)
865 return (NULL);
866
867 return (assert->stmt[idx].authdata_cbor.ptr);
868 }
869
870 size_t
fido_assert_authdata_len(const fido_assert_t * assert,size_t idx)871 fido_assert_authdata_len(const fido_assert_t *assert, size_t idx)
872 {
873 if (idx >= assert->stmt_len)
874 return (0);
875
876 return (assert->stmt[idx].authdata_cbor.len);
877 }
878
879 const unsigned char *
fido_assert_sig_ptr(const fido_assert_t * assert,size_t idx)880 fido_assert_sig_ptr(const fido_assert_t *assert, size_t idx)
881 {
882 if (idx >= assert->stmt_len)
883 return (NULL);
884
885 return (assert->stmt[idx].sig.ptr);
886 }
887
888 size_t
fido_assert_sig_len(const fido_assert_t * assert,size_t idx)889 fido_assert_sig_len(const fido_assert_t *assert, size_t idx)
890 {
891 if (idx >= assert->stmt_len)
892 return (0);
893
894 return (assert->stmt[idx].sig.len);
895 }
896
897 const unsigned char *
fido_assert_id_ptr(const fido_assert_t * assert,size_t idx)898 fido_assert_id_ptr(const fido_assert_t *assert, size_t idx)
899 {
900 if (idx >= assert->stmt_len)
901 return (NULL);
902
903 return (assert->stmt[idx].id.ptr);
904 }
905
906 size_t
fido_assert_id_len(const fido_assert_t * assert,size_t idx)907 fido_assert_id_len(const fido_assert_t *assert, size_t idx)
908 {
909 if (idx >= assert->stmt_len)
910 return (0);
911
912 return (assert->stmt[idx].id.len);
913 }
914
915 const unsigned char *
fido_assert_user_id_ptr(const fido_assert_t * assert,size_t idx)916 fido_assert_user_id_ptr(const fido_assert_t *assert, size_t idx)
917 {
918 if (idx >= assert->stmt_len)
919 return (NULL);
920
921 return (assert->stmt[idx].user.id.ptr);
922 }
923
924 size_t
fido_assert_user_id_len(const fido_assert_t * assert,size_t idx)925 fido_assert_user_id_len(const fido_assert_t *assert, size_t idx)
926 {
927 if (idx >= assert->stmt_len)
928 return (0);
929
930 return (assert->stmt[idx].user.id.len);
931 }
932
933 const char *
fido_assert_user_icon(const fido_assert_t * assert,size_t idx)934 fido_assert_user_icon(const fido_assert_t *assert, size_t idx)
935 {
936 if (idx >= assert->stmt_len)
937 return (NULL);
938
939 return (assert->stmt[idx].user.icon);
940 }
941
942 const char *
fido_assert_user_name(const fido_assert_t * assert,size_t idx)943 fido_assert_user_name(const fido_assert_t *assert, size_t idx)
944 {
945 if (idx >= assert->stmt_len)
946 return (NULL);
947
948 return (assert->stmt[idx].user.name);
949 }
950
951 const char *
fido_assert_user_display_name(const fido_assert_t * assert,size_t idx)952 fido_assert_user_display_name(const fido_assert_t *assert, size_t idx)
953 {
954 if (idx >= assert->stmt_len)
955 return (NULL);
956
957 return (assert->stmt[idx].user.display_name);
958 }
959
960 const unsigned char *
fido_assert_hmac_secret_ptr(const fido_assert_t * assert,size_t idx)961 fido_assert_hmac_secret_ptr(const fido_assert_t *assert, size_t idx)
962 {
963 if (idx >= assert->stmt_len)
964 return (NULL);
965
966 return (assert->stmt[idx].hmac_secret.ptr);
967 }
968
969 size_t
fido_assert_hmac_secret_len(const fido_assert_t * assert,size_t idx)970 fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx)
971 {
972 if (idx >= assert->stmt_len)
973 return (0);
974
975 return (assert->stmt[idx].hmac_secret.len);
976 }
977
978 const unsigned char *
fido_assert_largeblob_key_ptr(const fido_assert_t * assert,size_t idx)979 fido_assert_largeblob_key_ptr(const fido_assert_t *assert, size_t idx)
980 {
981 if (idx >= assert->stmt_len)
982 return (NULL);
983
984 return (assert->stmt[idx].largeblob_key.ptr);
985 }
986
987 size_t
fido_assert_largeblob_key_len(const fido_assert_t * assert,size_t idx)988 fido_assert_largeblob_key_len(const fido_assert_t *assert, size_t idx)
989 {
990 if (idx >= assert->stmt_len)
991 return (0);
992
993 return (assert->stmt[idx].largeblob_key.len);
994 }
995
996 const unsigned char *
fido_assert_blob_ptr(const fido_assert_t * assert,size_t idx)997 fido_assert_blob_ptr(const fido_assert_t *assert, size_t idx)
998 {
999 if (idx >= assert->stmt_len)
1000 return (NULL);
1001
1002 return (assert->stmt[idx].authdata_ext.blob.ptr);
1003 }
1004
1005 size_t
fido_assert_blob_len(const fido_assert_t * assert,size_t idx)1006 fido_assert_blob_len(const fido_assert_t *assert, size_t idx)
1007 {
1008 if (idx >= assert->stmt_len)
1009 return (0);
1010
1011 return (assert->stmt[idx].authdata_ext.blob.len);
1012 }
1013
1014 static void
fido_assert_clean_authdata(fido_assert_stmt * stmt)1015 fido_assert_clean_authdata(fido_assert_stmt *stmt)
1016 {
1017 fido_blob_reset(&stmt->authdata_cbor);
1018 fido_assert_reset_extattr(&stmt->authdata_ext);
1019 memset(&stmt->authdata, 0, sizeof(stmt->authdata));
1020 }
1021
1022 int
fido_assert_set_authdata(fido_assert_t * assert,size_t idx,const unsigned char * ptr,size_t len)1023 fido_assert_set_authdata(fido_assert_t *assert, size_t idx,
1024 const unsigned char *ptr, size_t len)
1025 {
1026 cbor_item_t *item = NULL;
1027 fido_assert_stmt *stmt = NULL;
1028 struct cbor_load_result cbor;
1029 int r;
1030
1031 if (idx >= assert->stmt_len || ptr == NULL || len == 0)
1032 return (FIDO_ERR_INVALID_ARGUMENT);
1033
1034 stmt = &assert->stmt[idx];
1035 fido_assert_clean_authdata(stmt);
1036
1037 if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
1038 fido_log_debug("%s: cbor_load", __func__);
1039 r = FIDO_ERR_INVALID_ARGUMENT;
1040 goto fail;
1041 }
1042
1043 if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
1044 &stmt->authdata, &stmt->authdata_ext) < 0) {
1045 fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
1046 r = FIDO_ERR_INVALID_ARGUMENT;
1047 goto fail;
1048 }
1049
1050 r = FIDO_OK;
1051 fail:
1052 if (item != NULL)
1053 cbor_decref(&item);
1054
1055 if (r != FIDO_OK)
1056 fido_assert_clean_authdata(stmt);
1057
1058 return (r);
1059 }
1060
1061 int
fido_assert_set_authdata_raw(fido_assert_t * assert,size_t idx,const unsigned char * ptr,size_t len)1062 fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx,
1063 const unsigned char *ptr, size_t len)
1064 {
1065 cbor_item_t *item = NULL;
1066 fido_assert_stmt *stmt = NULL;
1067 int r;
1068
1069 if (idx >= assert->stmt_len || ptr == NULL || len == 0)
1070 return (FIDO_ERR_INVALID_ARGUMENT);
1071
1072 stmt = &assert->stmt[idx];
1073 fido_assert_clean_authdata(stmt);
1074
1075 if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
1076 fido_log_debug("%s: cbor_build_bytestring", __func__);
1077 r = FIDO_ERR_INTERNAL;
1078 goto fail;
1079 }
1080
1081 if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
1082 &stmt->authdata, &stmt->authdata_ext) < 0) {
1083 fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
1084 r = FIDO_ERR_INVALID_ARGUMENT;
1085 goto fail;
1086 }
1087
1088 r = FIDO_OK;
1089 fail:
1090 if (item != NULL)
1091 cbor_decref(&item);
1092
1093 if (r != FIDO_OK)
1094 fido_assert_clean_authdata(stmt);
1095
1096 return (r);
1097 }
1098
1099 int
fido_assert_set_sig(fido_assert_t * a,size_t idx,const unsigned char * ptr,size_t len)1100 fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr,
1101 size_t len)
1102 {
1103 if (idx >= a->stmt_len || ptr == NULL || len == 0)
1104 return (FIDO_ERR_INVALID_ARGUMENT);
1105 if (fido_blob_set(&a->stmt[idx].sig, ptr, len) < 0)
1106 return (FIDO_ERR_INTERNAL);
1107
1108 return (FIDO_OK);
1109 }
1110
1111 /* XXX shrinking leaks memory; fortunately that shouldn't happen */
1112 int
fido_assert_set_count(fido_assert_t * assert,size_t n)1113 fido_assert_set_count(fido_assert_t *assert, size_t n)
1114 {
1115 void *new_stmt;
1116
1117 #ifdef FIDO_FUZZ
1118 if (n > UINT8_MAX) {
1119 fido_log_debug("%s: n > UINT8_MAX", __func__);
1120 return (FIDO_ERR_INTERNAL);
1121 }
1122 #endif
1123
1124 new_stmt = recallocarray(assert->stmt, assert->stmt_cnt, n,
1125 sizeof(fido_assert_stmt));
1126 if (new_stmt == NULL)
1127 return (FIDO_ERR_INTERNAL);
1128
1129 assert->stmt = new_stmt;
1130 assert->stmt_cnt = n;
1131 assert->stmt_len = n;
1132
1133 return (FIDO_OK);
1134 }
1135