]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libfido2/src/assert.c
unbound: Vendor import 1.16.0
[FreeBSD/FreeBSD.git] / contrib / libfido2 / src / assert.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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 = 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
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 = 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
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
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
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
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
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
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
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
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
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
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
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
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 *
757 fido_assert_clientdata_hash_ptr(const fido_assert_t *assert)
758 {
759         return (assert->cdh.ptr);
760 }
761
762 size_t
763 fido_assert_clientdata_hash_len(const fido_assert_t *assert)
764 {
765         return (assert->cdh.len);
766 }
767
768 fido_assert_t *
769 fido_assert_new(void)
770 {
771         return (calloc(1, sizeof(fido_assert_t)));
772 }
773
774 void
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
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
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
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
832 fido_assert_count(const fido_assert_t *assert)
833 {
834         return (assert->stmt_len);
835 }
836
837 const char *
838 fido_assert_rp_id(const fido_assert_t *assert)
839 {
840         return (assert->rp_id);
841 }
842
843 uint8_t
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
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 *
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
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 *
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
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 *
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
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 *
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
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 *
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 *
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 *
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 *
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
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 *
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
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 *
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
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
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
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
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
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
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 }