]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libfido2/src/cred.c
Merge llvm-project main llvmorg-14-init-18294-gdb01b123d012
[FreeBSD/FreeBSD.git] / contrib / libfido2 / src / cred.c
1 /*
2  * Copyright (c) 2018 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/sha.h>
8 #include <openssl/x509.h>
9
10 #include "fido.h"
11 #include "fido/es256.h"
12
13 static int
14 parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
15 {
16         fido_cred_t *cred = arg;
17
18         if (cbor_isa_uint(key) == false ||
19             cbor_int_get_width(key) != CBOR_INT_8) {
20                 fido_log_debug("%s: cbor type", __func__);
21                 return (0); /* ignore */
22         }
23
24         switch (cbor_get_uint8(key)) {
25         case 1: /* fmt */
26                 return (cbor_decode_fmt(val, &cred->fmt));
27         case 2: /* authdata */
28                 if (fido_blob_decode(val, &cred->authdata_raw) < 0) {
29                         fido_log_debug("%s: fido_blob_decode", __func__);
30                         return (-1);
31                 }
32                 return (cbor_decode_cred_authdata(val, cred->type,
33                     &cred->authdata_cbor, &cred->authdata, &cred->attcred,
34                     &cred->authdata_ext));
35         case 3: /* attestation statement */
36                 return (cbor_decode_attstmt(val, &cred->attstmt));
37         case 5: /* large blob key */
38                 return (fido_blob_decode(val, &cred->largeblob_key));
39         default: /* ignore */
40                 fido_log_debug("%s: cbor type", __func__);
41                 return (0);
42         }
43 }
44
45 static int
46 fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
47 {
48         fido_blob_t      f;
49         fido_blob_t     *ecdh = NULL;
50         fido_opt_t       uv = cred->uv;
51         es256_pk_t      *pk = NULL;
52         cbor_item_t     *argv[9];
53         const uint8_t    cmd = CTAP_CBOR_MAKECRED;
54         int              r;
55
56         memset(&f, 0, sizeof(f));
57         memset(argv, 0, sizeof(argv));
58
59         if (cred->cdh.ptr == NULL || cred->type == 0) {
60                 fido_log_debug("%s: cdh=%p, type=%d", __func__,
61                     (void *)cred->cdh.ptr, cred->type);
62                 r = FIDO_ERR_INVALID_ARGUMENT;
63                 goto fail;
64         }
65
66         if ((argv[0] = fido_blob_encode(&cred->cdh)) == NULL ||
67             (argv[1] = cbor_encode_rp_entity(&cred->rp)) == NULL ||
68             (argv[2] = cbor_encode_user_entity(&cred->user)) == NULL ||
69             (argv[3] = cbor_encode_pubkey_param(cred->type)) == NULL) {
70                 fido_log_debug("%s: cbor encode", __func__);
71                 r = FIDO_ERR_INTERNAL;
72                 goto fail;
73         }
74
75         /* excluded credentials */
76         if (cred->excl.len)
77                 if ((argv[4] = cbor_encode_pubkey_list(&cred->excl)) == NULL) {
78                         fido_log_debug("%s: cbor_encode_pubkey_list", __func__);
79                         r = FIDO_ERR_INTERNAL;
80                         goto fail;
81                 }
82
83         /* extensions */
84         if (cred->ext.mask)
85                 if ((argv[5] = cbor_encode_cred_ext(&cred->ext,
86                     &cred->blob)) == NULL) {
87                         fido_log_debug("%s: cbor_encode_cred_ext", __func__);
88                         r = FIDO_ERR_INTERNAL;
89                         goto fail;
90                 }
91
92         /* user verification */
93         if (pin != NULL || (uv == FIDO_OPT_TRUE &&
94             fido_dev_supports_permissions(dev))) {
95                 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
96                         fido_log_debug("%s: fido_do_ecdh", __func__);
97                         goto fail;
98                 }
99                 if ((r = cbor_add_uv_params(dev, cmd, &cred->cdh, pk, ecdh,
100                     pin, cred->rp.id, &argv[7], &argv[8])) != FIDO_OK) {
101                         fido_log_debug("%s: cbor_add_uv_params", __func__);
102                         goto fail;
103                 }
104                 uv = FIDO_OPT_OMIT;
105         }
106
107         /* options */
108         if (cred->rk != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT)
109                 if ((argv[6] = cbor_encode_cred_opt(cred->rk, uv)) == NULL) {
110                         fido_log_debug("%s: cbor_encode_cred_opt", __func__);
111                         r = FIDO_ERR_INTERNAL;
112                         goto fail;
113                 }
114
115         /* framing and transmission */
116         if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
117             fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
118                 fido_log_debug("%s: fido_tx", __func__);
119                 r = FIDO_ERR_TX;
120                 goto fail;
121         }
122
123         r = FIDO_OK;
124 fail:
125         es256_pk_free(&pk);
126         fido_blob_free(&ecdh);
127         cbor_vector_free(argv, nitems(argv));
128         free(f.ptr);
129
130         return (r);
131 }
132
133 static int
134 fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int ms)
135 {
136         unsigned char   reply[FIDO_MAXMSG];
137         int             reply_len;
138         int             r;
139
140         fido_cred_reset_rx(cred);
141
142         if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
143             ms)) < 0) {
144                 fido_log_debug("%s: fido_rx", __func__);
145                 return (FIDO_ERR_RX);
146         }
147
148         if ((r = cbor_parse_reply(reply, (size_t)reply_len, cred,
149             parse_makecred_reply)) != FIDO_OK) {
150                 fido_log_debug("%s: parse_makecred_reply", __func__);
151                 return (r);
152         }
153
154         if (cred->fmt == NULL || fido_blob_is_empty(&cred->authdata_cbor) ||
155             fido_blob_is_empty(&cred->attcred.id)) {
156                 fido_cred_reset_rx(cred);
157                 return (FIDO_ERR_INVALID_CBOR);
158         }
159
160         return (FIDO_OK);
161 }
162
163 static int
164 fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
165     int ms)
166 {
167         int  r;
168
169         if ((r = fido_dev_make_cred_tx(dev, cred, pin)) != FIDO_OK ||
170             (r = fido_dev_make_cred_rx(dev, cred, ms)) != FIDO_OK)
171                 return (r);
172
173         return (FIDO_OK);
174 }
175
176 int
177 fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
178 {
179 #ifdef USE_WINHELLO
180         if (dev->flags & FIDO_DEV_WINHELLO)
181                 return (fido_winhello_make_cred(dev, cred, pin));
182 #endif
183         if (fido_dev_is_fido2(dev) == false) {
184                 if (pin != NULL || cred->rk == FIDO_OPT_TRUE ||
185                     cred->ext.mask != 0)
186                         return (FIDO_ERR_UNSUPPORTED_OPTION);
187                 return (u2f_register(dev, cred, -1));
188         }
189
190         return (fido_dev_make_cred_wait(dev, cred, pin, -1));
191 }
192
193 static int
194 check_extensions(const fido_cred_ext_t *authdata_ext,
195     const fido_cred_ext_t *ext)
196 {
197         fido_cred_ext_t  tmp;
198
199         /* XXX: largeBlobKey is not part of the extensions map */
200         memcpy(&tmp, ext, sizeof(tmp));
201         tmp.mask &= ~FIDO_EXT_LARGEBLOB_KEY;
202
203         return (timingsafe_bcmp(authdata_ext, &tmp, sizeof(*authdata_ext)));
204 }
205
206 int
207 fido_check_rp_id(const char *id, const unsigned char *obtained_hash)
208 {
209         unsigned char expected_hash[SHA256_DIGEST_LENGTH];
210
211         explicit_bzero(expected_hash, sizeof(expected_hash));
212
213         if (SHA256((const unsigned char *)id, strlen(id),
214             expected_hash) != expected_hash) {
215                 fido_log_debug("%s: sha256", __func__);
216                 return (-1);
217         }
218
219         return (timingsafe_bcmp(expected_hash, obtained_hash,
220             SHA256_DIGEST_LENGTH));
221 }
222
223 static int
224 get_signed_hash_u2f(fido_blob_t *dgst, const unsigned char *rp_id,
225     size_t rp_id_len, const fido_blob_t *clientdata, const fido_blob_t *id,
226     const es256_pk_t *pk)
227 {
228         const uint8_t           zero = 0;
229         const uint8_t           four = 4; /* uncompressed point */
230         SHA256_CTX              ctx;
231
232         if (dgst->len != SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 ||
233             SHA256_Update(&ctx, &zero, sizeof(zero)) == 0 ||
234             SHA256_Update(&ctx, rp_id, rp_id_len) == 0 ||
235             SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 ||
236             SHA256_Update(&ctx, id->ptr, id->len) == 0 ||
237             SHA256_Update(&ctx, &four, sizeof(four)) == 0 ||
238             SHA256_Update(&ctx, pk->x, sizeof(pk->x)) == 0 ||
239             SHA256_Update(&ctx, pk->y, sizeof(pk->y)) == 0 ||
240             SHA256_Final(dgst->ptr, &ctx) == 0) {
241                 fido_log_debug("%s: sha256", __func__);
242                 return (-1);
243         }
244
245         return (0);
246 }
247
248 static int
249 verify_sig(const fido_blob_t *dgst, const fido_blob_t *x5c,
250     const fido_blob_t *sig)
251 {
252         BIO             *rawcert = NULL;
253         X509            *cert = NULL;
254         EVP_PKEY        *pkey = NULL;
255         EC_KEY          *ec;
256         int              ok = -1;
257
258         /* openssl needs ints */
259         if (dgst->len > INT_MAX || x5c->len > INT_MAX || sig->len > INT_MAX) {
260                 fido_log_debug("%s: dgst->len=%zu, x5c->len=%zu, sig->len=%zu",
261                     __func__, dgst->len, x5c->len, sig->len);
262                 return (-1);
263         }
264
265         /* fetch key from x509 */
266         if ((rawcert = BIO_new_mem_buf(x5c->ptr, (int)x5c->len)) == NULL ||
267             (cert = d2i_X509_bio(rawcert, NULL)) == NULL ||
268             (pkey = X509_get_pubkey(cert)) == NULL ||
269             (ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
270                 fido_log_debug("%s: x509 key", __func__);
271                 goto fail;
272         }
273
274         if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr,
275             (int)sig->len, ec) != 1) {
276                 fido_log_debug("%s: ECDSA_verify", __func__);
277                 goto fail;
278         }
279
280         ok = 0;
281 fail:
282         if (rawcert != NULL)
283                 BIO_free(rawcert);
284         if (cert != NULL)
285                 X509_free(cert);
286         if (pkey != NULL)
287                 EVP_PKEY_free(pkey);
288
289         return (ok);
290 }
291
292 int
293 fido_cred_verify(const fido_cred_t *cred)
294 {
295         unsigned char   buf[SHA256_DIGEST_LENGTH];
296         fido_blob_t     dgst;
297         int             r;
298
299         dgst.ptr = buf;
300         dgst.len = sizeof(buf);
301
302         /* do we have everything we need? */
303         if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL ||
304             cred->attstmt.x5c.ptr == NULL || cred->attstmt.sig.ptr == NULL ||
305             cred->fmt == NULL || cred->attcred.id.ptr == NULL ||
306             cred->rp.id == NULL) {
307                 fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, "
308                     "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr,
309                     (void *)cred->authdata_cbor.ptr,
310                     (void *)cred->attstmt.x5c.ptr,
311                     (void *)cred->attstmt.sig.ptr, (void *)cred->fmt,
312                     (void *)cred->attcred.id.ptr, cred->rp.id);
313                 r = FIDO_ERR_INVALID_ARGUMENT;
314                 goto out;
315         }
316
317         if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) {
318                 fido_log_debug("%s: fido_check_rp_id", __func__);
319                 r = FIDO_ERR_INVALID_PARAM;
320                 goto out;
321         }
322
323         if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE,
324             cred->uv) < 0) {
325                 fido_log_debug("%s: fido_check_flags", __func__);
326                 r = FIDO_ERR_INVALID_PARAM;
327                 goto out;
328         }
329
330         if (check_extensions(&cred->authdata_ext, &cred->ext) != 0) {
331                 fido_log_debug("%s: check_extensions", __func__);
332                 r = FIDO_ERR_INVALID_PARAM;
333                 goto out;
334         }
335
336         if (!strcmp(cred->fmt, "packed")) {
337                 if (fido_get_signed_hash(COSE_ES256, &dgst, &cred->cdh,
338                     &cred->authdata_cbor) < 0) {
339                         fido_log_debug("%s: fido_get_signed_hash", __func__);
340                         r = FIDO_ERR_INTERNAL;
341                         goto out;
342                 }
343         } else if (!strcmp(cred->fmt, "fido-u2f")) {
344                 if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
345                     sizeof(cred->authdata.rp_id_hash), &cred->cdh,
346                     &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
347                         fido_log_debug("%s: get_signed_hash_u2f", __func__);
348                         r = FIDO_ERR_INTERNAL;
349                         goto out;
350                 }
351         } else {
352                 fido_log_debug("%s: unknown fmt %s", __func__, cred->fmt);
353                 r = FIDO_ERR_INVALID_ARGUMENT;
354                 goto out;
355         }
356
357         if (verify_sig(&dgst, &cred->attstmt.x5c, &cred->attstmt.sig) < 0) {
358                 fido_log_debug("%s: verify_sig", __func__);
359                 r = FIDO_ERR_INVALID_SIG;
360                 goto out;
361         }
362
363         r = FIDO_OK;
364 out:
365         explicit_bzero(buf, sizeof(buf));
366
367         return (r);
368 }
369
370 int
371 fido_cred_verify_self(const fido_cred_t *cred)
372 {
373         unsigned char   buf[1024]; /* XXX */
374         fido_blob_t     dgst;
375         int             ok = -1;
376         int             r;
377
378         dgst.ptr = buf;
379         dgst.len = sizeof(buf);
380
381         /* do we have everything we need? */
382         if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL ||
383             cred->attstmt.x5c.ptr != NULL || cred->attstmt.sig.ptr == NULL ||
384             cred->fmt == NULL || cred->attcred.id.ptr == NULL ||
385             cred->rp.id == NULL) {
386                 fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, "
387                     "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr,
388                     (void *)cred->authdata_cbor.ptr,
389                     (void *)cred->attstmt.x5c.ptr,
390                     (void *)cred->attstmt.sig.ptr, (void *)cred->fmt,
391                     (void *)cred->attcred.id.ptr, cred->rp.id);
392                 r = FIDO_ERR_INVALID_ARGUMENT;
393                 goto out;
394         }
395
396         if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) {
397                 fido_log_debug("%s: fido_check_rp_id", __func__);
398                 r = FIDO_ERR_INVALID_PARAM;
399                 goto out;
400         }
401
402         if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE,
403             cred->uv) < 0) {
404                 fido_log_debug("%s: fido_check_flags", __func__);
405                 r = FIDO_ERR_INVALID_PARAM;
406                 goto out;
407         }
408
409         if (check_extensions(&cred->authdata_ext, &cred->ext) != 0) {
410                 fido_log_debug("%s: check_extensions", __func__);
411                 r = FIDO_ERR_INVALID_PARAM;
412                 goto out;
413         }
414
415         if (!strcmp(cred->fmt, "packed")) {
416                 if (fido_get_signed_hash(cred->attcred.type, &dgst, &cred->cdh,
417                     &cred->authdata_cbor) < 0) {
418                         fido_log_debug("%s: fido_get_signed_hash", __func__);
419                         r = FIDO_ERR_INTERNAL;
420                         goto out;
421                 }
422         } else if (!strcmp(cred->fmt, "fido-u2f")) {
423                 if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
424                     sizeof(cred->authdata.rp_id_hash), &cred->cdh,
425                     &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
426                         fido_log_debug("%s: get_signed_hash_u2f", __func__);
427                         r = FIDO_ERR_INTERNAL;
428                         goto out;
429                 }
430         } else {
431                 fido_log_debug("%s: unknown fmt %s", __func__, cred->fmt);
432                 r = FIDO_ERR_INVALID_ARGUMENT;
433                 goto out;
434         }
435
436         switch (cred->attcred.type) {
437         case COSE_ES256:
438                 ok = fido_verify_sig_es256(&dgst, &cred->attcred.pubkey.es256,
439                     &cred->attstmt.sig);
440                 break;
441         case COSE_RS256:
442                 ok = fido_verify_sig_rs256(&dgst, &cred->attcred.pubkey.rs256,
443                     &cred->attstmt.sig);
444                 break;
445         case COSE_EDDSA:
446                 ok = fido_verify_sig_eddsa(&dgst, &cred->attcred.pubkey.eddsa,
447                     &cred->attstmt.sig);
448                 break;
449         default:
450                 fido_log_debug("%s: unsupported cose_alg %d", __func__,
451                     cred->attcred.type);
452                 r = FIDO_ERR_UNSUPPORTED_OPTION;
453                 goto out;
454         }
455
456         if (ok < 0)
457                 r = FIDO_ERR_INVALID_SIG;
458         else
459                 r = FIDO_OK;
460
461 out:
462         explicit_bzero(buf, sizeof(buf));
463
464         return (r);
465 }
466
467 fido_cred_t *
468 fido_cred_new(void)
469 {
470         return (calloc(1, sizeof(fido_cred_t)));
471 }
472
473 static void
474 fido_cred_clean_authdata(fido_cred_t *cred)
475 {
476         fido_blob_reset(&cred->authdata_cbor);
477         fido_blob_reset(&cred->authdata_raw);
478         fido_blob_reset(&cred->attcred.id);
479
480         memset(&cred->authdata_ext, 0, sizeof(cred->authdata_ext));
481         memset(&cred->authdata, 0, sizeof(cred->authdata));
482         memset(&cred->attcred, 0, sizeof(cred->attcred));
483 }
484
485 void
486 fido_cred_reset_tx(fido_cred_t *cred)
487 {
488         fido_blob_reset(&cred->cd);
489         fido_blob_reset(&cred->cdh);
490         fido_blob_reset(&cred->user.id);
491         fido_blob_reset(&cred->blob);
492
493         free(cred->rp.id);
494         free(cred->rp.name);
495         free(cred->user.icon);
496         free(cred->user.name);
497         free(cred->user.display_name);
498         fido_free_blob_array(&cred->excl);
499
500         memset(&cred->rp, 0, sizeof(cred->rp));
501         memset(&cred->user, 0, sizeof(cred->user));
502         memset(&cred->excl, 0, sizeof(cred->excl));
503         memset(&cred->ext, 0, sizeof(cred->ext));
504
505         cred->type = 0;
506         cred->rk = FIDO_OPT_OMIT;
507         cred->uv = FIDO_OPT_OMIT;
508 }
509
510 void
511 fido_cred_reset_rx(fido_cred_t *cred)
512 {
513         free(cred->fmt);
514         cred->fmt = NULL;
515         fido_cred_clean_authdata(cred);
516         fido_blob_reset(&cred->attstmt.x5c);
517         fido_blob_reset(&cred->attstmt.sig);
518         fido_blob_reset(&cred->largeblob_key);
519 }
520
521 void
522 fido_cred_free(fido_cred_t **cred_p)
523 {
524         fido_cred_t *cred;
525
526         if (cred_p == NULL || (cred = *cred_p) == NULL)
527                 return;
528         fido_cred_reset_tx(cred);
529         fido_cred_reset_rx(cred);
530         free(cred);
531         *cred_p = NULL;
532 }
533
534 int
535 fido_cred_set_authdata(fido_cred_t *cred, const unsigned char *ptr, size_t len)
536 {
537         cbor_item_t             *item = NULL;
538         struct cbor_load_result  cbor;
539         int                      r = FIDO_ERR_INVALID_ARGUMENT;
540
541         fido_cred_clean_authdata(cred);
542
543         if (ptr == NULL || len == 0)
544                 goto fail;
545
546         if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
547                 fido_log_debug("%s: cbor_load", __func__);
548                 goto fail;
549         }
550
551         if (fido_blob_decode(item, &cred->authdata_raw) < 0) {
552                 fido_log_debug("%s: fido_blob_decode", __func__);
553                 goto fail;
554         }
555
556         if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
557             &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
558                 fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
559                 goto fail;
560         }
561
562         r = FIDO_OK;
563 fail:
564         if (item != NULL)
565                 cbor_decref(&item);
566
567         if (r != FIDO_OK)
568                 fido_cred_clean_authdata(cred);
569
570         return (r);
571
572 }
573
574 int
575 fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr,
576     size_t len)
577 {
578         cbor_item_t     *item = NULL;
579         int              r = FIDO_ERR_INVALID_ARGUMENT;
580
581         fido_cred_clean_authdata(cred);
582
583         if (ptr == NULL || len == 0)
584                 goto fail;
585
586         if (fido_blob_set(&cred->authdata_raw, ptr, len) < 0) {
587                 fido_log_debug("%s: fido_blob_set", __func__);
588                 r = FIDO_ERR_INTERNAL;
589                 goto fail;
590         }
591
592         if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
593                 fido_log_debug("%s: cbor_build_bytestring", __func__);
594                 r = FIDO_ERR_INTERNAL;
595                 goto fail;
596         }
597
598         if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
599             &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
600                 fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
601                 goto fail;
602         }
603
604         r = FIDO_OK;
605 fail:
606         if (item != NULL)
607                 cbor_decref(&item);
608
609         if (r != FIDO_OK)
610                 fido_cred_clean_authdata(cred);
611
612         return (r);
613
614 }
615
616 int
617 fido_cred_set_id(fido_cred_t *cred, const unsigned char *ptr, size_t len)
618 {
619         if (fido_blob_set(&cred->attcred.id, ptr, len) < 0)
620                 return (FIDO_ERR_INVALID_ARGUMENT);
621
622         return (FIDO_OK);
623 }
624
625 int
626 fido_cred_set_x509(fido_cred_t *cred, const unsigned char *ptr, size_t len)
627 {
628         if (fido_blob_set(&cred->attstmt.x5c, ptr, len) < 0)
629                 return (FIDO_ERR_INVALID_ARGUMENT);
630
631         return (FIDO_OK);
632 }
633
634 int
635 fido_cred_set_sig(fido_cred_t *cred, const unsigned char *ptr, size_t len)
636 {
637         if (fido_blob_set(&cred->attstmt.sig, ptr, len) < 0)
638                 return (FIDO_ERR_INVALID_ARGUMENT);
639
640         return (FIDO_OK);
641 }
642
643 int
644 fido_cred_exclude(fido_cred_t *cred, const unsigned char *id_ptr, size_t id_len)
645 {
646         fido_blob_t id_blob;
647         fido_blob_t *list_ptr;
648
649         memset(&id_blob, 0, sizeof(id_blob));
650
651         if (fido_blob_set(&id_blob, id_ptr, id_len) < 0)
652                 return (FIDO_ERR_INVALID_ARGUMENT);
653
654         if (cred->excl.len == SIZE_MAX) {
655                 free(id_blob.ptr);
656                 return (FIDO_ERR_INVALID_ARGUMENT);
657         }
658
659         if ((list_ptr = recallocarray(cred->excl.ptr, cred->excl.len,
660             cred->excl.len + 1, sizeof(fido_blob_t))) == NULL) {
661                 free(id_blob.ptr);
662                 return (FIDO_ERR_INTERNAL);
663         }
664
665         list_ptr[cred->excl.len++] = id_blob;
666         cred->excl.ptr = list_ptr;
667
668         return (FIDO_OK);
669 }
670
671 int
672 fido_cred_set_clientdata(fido_cred_t *cred, const unsigned char *data,
673     size_t data_len)
674 {
675         if (!fido_blob_is_empty(&cred->cdh) ||
676             fido_blob_set(&cred->cd, data, data_len) < 0) {
677                 return (FIDO_ERR_INVALID_ARGUMENT);
678         }
679         if (fido_sha256(&cred->cdh, data, data_len) < 0) {
680                 fido_blob_reset(&cred->cd);
681                 return (FIDO_ERR_INTERNAL);
682         }
683
684         return (FIDO_OK);
685 }
686
687 int
688 fido_cred_set_clientdata_hash(fido_cred_t *cred, const unsigned char *hash,
689     size_t hash_len)
690 {
691         if (!fido_blob_is_empty(&cred->cd) ||
692             fido_blob_set(&cred->cdh, hash, hash_len) < 0)
693                 return (FIDO_ERR_INVALID_ARGUMENT);
694
695         return (FIDO_OK);
696 }
697
698 int
699 fido_cred_set_rp(fido_cred_t *cred, const char *id, const char *name)
700 {
701         fido_rp_t *rp = &cred->rp;
702
703         if (rp->id != NULL) {
704                 free(rp->id);
705                 rp->id = NULL;
706         }
707         if (rp->name != NULL) {
708                 free(rp->name);
709                 rp->name = NULL;
710         }
711
712         if (id != NULL && (rp->id = strdup(id)) == NULL)
713                 goto fail;
714         if (name != NULL && (rp->name = strdup(name)) == NULL)
715                 goto fail;
716
717         return (FIDO_OK);
718 fail:
719         free(rp->id);
720         free(rp->name);
721         rp->id = NULL;
722         rp->name = NULL;
723
724         return (FIDO_ERR_INTERNAL);
725 }
726
727 int
728 fido_cred_set_user(fido_cred_t *cred, const unsigned char *user_id,
729     size_t user_id_len, const char *name, const char *display_name,
730     const char *icon)
731 {
732         fido_user_t *up = &cred->user;
733
734         if (up->id.ptr != NULL) {
735                 free(up->id.ptr);
736                 up->id.ptr = NULL;
737                 up->id.len = 0;
738         }
739         if (up->name != NULL) {
740                 free(up->name);
741                 up->name = NULL;
742         }
743         if (up->display_name != NULL) {
744                 free(up->display_name);
745                 up->display_name = NULL;
746         }
747         if (up->icon != NULL) {
748                 free(up->icon);
749                 up->icon = NULL;
750         }
751
752         if (user_id != NULL && fido_blob_set(&up->id, user_id, user_id_len) < 0)
753                 goto fail;
754         if (name != NULL && (up->name = strdup(name)) == NULL)
755                 goto fail;
756         if (display_name != NULL &&
757             (up->display_name = strdup(display_name)) == NULL)
758                 goto fail;
759         if (icon != NULL && (up->icon = strdup(icon)) == NULL)
760                 goto fail;
761
762         return (FIDO_OK);
763 fail:
764         free(up->id.ptr);
765         free(up->name);
766         free(up->display_name);
767         free(up->icon);
768
769         up->id.ptr = NULL;
770         up->id.len = 0;
771         up->name = NULL;
772         up->display_name = NULL;
773         up->icon = NULL;
774
775         return (FIDO_ERR_INTERNAL);
776 }
777
778 int
779 fido_cred_set_extensions(fido_cred_t *cred, int ext)
780 {
781         if (ext == 0)
782                 cred->ext.mask = 0;
783         else {
784                 if ((ext & FIDO_EXT_CRED_MASK) != ext)
785                         return (FIDO_ERR_INVALID_ARGUMENT);
786                 cred->ext.mask |= ext;
787         }
788
789         return (FIDO_OK);
790 }
791
792 int
793 fido_cred_set_options(fido_cred_t *cred, bool rk, bool uv)
794 {
795         cred->rk = rk ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
796         cred->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
797
798         return (FIDO_OK);
799 }
800
801 int
802 fido_cred_set_rk(fido_cred_t *cred, fido_opt_t rk)
803 {
804         cred->rk = rk;
805
806         return (FIDO_OK);
807 }
808
809 int
810 fido_cred_set_uv(fido_cred_t *cred, fido_opt_t uv)
811 {
812         cred->uv = uv;
813
814         return (FIDO_OK);
815 }
816
817 int
818 fido_cred_set_prot(fido_cred_t *cred, int prot)
819 {
820         if (prot == 0) {
821                 cred->ext.mask &= ~FIDO_EXT_CRED_PROTECT;
822                 cred->ext.prot = 0;
823         } else {
824                 if (prot != FIDO_CRED_PROT_UV_OPTIONAL &&
825                     prot != FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID &&
826                     prot != FIDO_CRED_PROT_UV_REQUIRED)
827                         return (FIDO_ERR_INVALID_ARGUMENT);
828
829                 cred->ext.mask |= FIDO_EXT_CRED_PROTECT;
830                 cred->ext.prot = prot;
831         }
832
833         return (FIDO_OK);
834 }
835
836 int
837 fido_cred_set_blob(fido_cred_t *cred, const unsigned char *ptr, size_t len)
838 {
839         if (ptr == NULL || len == 0)
840                 return (FIDO_ERR_INVALID_ARGUMENT);
841         if (fido_blob_set(&cred->blob, ptr, len) < 0)
842                 return (FIDO_ERR_INTERNAL);
843
844         cred->ext.mask |= FIDO_EXT_CRED_BLOB;
845
846         return (FIDO_OK);
847 }
848
849 int
850 fido_cred_set_fmt(fido_cred_t *cred, const char *fmt)
851 {
852         free(cred->fmt);
853         cred->fmt = NULL;
854
855         if (fmt == NULL)
856                 return (FIDO_ERR_INVALID_ARGUMENT);
857
858         if (strcmp(fmt, "packed") && strcmp(fmt, "fido-u2f") &&
859             strcmp(fmt, "none"))
860                 return (FIDO_ERR_INVALID_ARGUMENT);
861
862         if ((cred->fmt = strdup(fmt)) == NULL)
863                 return (FIDO_ERR_INTERNAL);
864
865         return (FIDO_OK);
866 }
867
868 int
869 fido_cred_set_type(fido_cred_t *cred, int cose_alg)
870 {
871         if ((cose_alg != COSE_ES256 && cose_alg != COSE_RS256 &&
872             cose_alg != COSE_EDDSA) || cred->type != 0)
873                 return (FIDO_ERR_INVALID_ARGUMENT);
874
875         cred->type = cose_alg;
876
877         return (FIDO_OK);
878 }
879
880 int
881 fido_cred_type(const fido_cred_t *cred)
882 {
883         return (cred->type);
884 }
885
886 uint8_t
887 fido_cred_flags(const fido_cred_t *cred)
888 {
889         return (cred->authdata.flags);
890 }
891
892 uint32_t
893 fido_cred_sigcount(const fido_cred_t *cred)
894 {
895         return (cred->authdata.sigcount);
896 }
897
898 const unsigned char *
899 fido_cred_clientdata_hash_ptr(const fido_cred_t *cred)
900 {
901         return (cred->cdh.ptr);
902 }
903
904 size_t
905 fido_cred_clientdata_hash_len(const fido_cred_t *cred)
906 {
907         return (cred->cdh.len);
908 }
909
910 const unsigned char *
911 fido_cred_x5c_ptr(const fido_cred_t *cred)
912 {
913         return (cred->attstmt.x5c.ptr);
914 }
915
916 size_t
917 fido_cred_x5c_len(const fido_cred_t *cred)
918 {
919         return (cred->attstmt.x5c.len);
920 }
921
922 const unsigned char *
923 fido_cred_sig_ptr(const fido_cred_t *cred)
924 {
925         return (cred->attstmt.sig.ptr);
926 }
927
928 size_t
929 fido_cred_sig_len(const fido_cred_t *cred)
930 {
931         return (cred->attstmt.sig.len);
932 }
933
934 const unsigned char *
935 fido_cred_authdata_ptr(const fido_cred_t *cred)
936 {
937         return (cred->authdata_cbor.ptr);
938 }
939
940 size_t
941 fido_cred_authdata_len(const fido_cred_t *cred)
942 {
943         return (cred->authdata_cbor.len);
944 }
945
946 const unsigned char *
947 fido_cred_authdata_raw_ptr(const fido_cred_t *cred)
948 {
949         return (cred->authdata_raw.ptr);
950 }
951
952 size_t
953 fido_cred_authdata_raw_len(const fido_cred_t *cred)
954 {
955         return (cred->authdata_raw.len);
956 }
957
958 const unsigned char *
959 fido_cred_pubkey_ptr(const fido_cred_t *cred)
960 {
961         const void *ptr;
962
963         switch (cred->attcred.type) {
964         case COSE_ES256:
965                 ptr = &cred->attcred.pubkey.es256;
966                 break;
967         case COSE_RS256:
968                 ptr = &cred->attcred.pubkey.rs256;
969                 break;
970         case COSE_EDDSA:
971                 ptr = &cred->attcred.pubkey.eddsa;
972                 break;
973         default:
974                 ptr = NULL;
975                 break;
976         }
977
978         return (ptr);
979 }
980
981 size_t
982 fido_cred_pubkey_len(const fido_cred_t *cred)
983 {
984         size_t len;
985
986         switch (cred->attcred.type) {
987         case COSE_ES256:
988                 len = sizeof(cred->attcred.pubkey.es256);
989                 break;
990         case COSE_RS256:
991                 len = sizeof(cred->attcred.pubkey.rs256);
992                 break;
993         case COSE_EDDSA:
994                 len = sizeof(cred->attcred.pubkey.eddsa);
995                 break;
996         default:
997                 len = 0;
998                 break;
999         }
1000
1001         return (len);
1002 }
1003
1004 const unsigned char *
1005 fido_cred_id_ptr(const fido_cred_t *cred)
1006 {
1007         return (cred->attcred.id.ptr);
1008 }
1009
1010 size_t
1011 fido_cred_id_len(const fido_cred_t *cred)
1012 {
1013         return (cred->attcred.id.len);
1014 }
1015
1016 const unsigned char *
1017 fido_cred_aaguid_ptr(const fido_cred_t *cred)
1018 {
1019         return (cred->attcred.aaguid);
1020 }
1021
1022 size_t
1023 fido_cred_aaguid_len(const fido_cred_t *cred)
1024 {
1025         return (sizeof(cred->attcred.aaguid));
1026 }
1027
1028 int
1029 fido_cred_prot(const fido_cred_t *cred)
1030 {
1031         return (cred->ext.prot);
1032 }
1033
1034 const char *
1035 fido_cred_fmt(const fido_cred_t *cred)
1036 {
1037         return (cred->fmt);
1038 }
1039
1040 const char *
1041 fido_cred_rp_id(const fido_cred_t *cred)
1042 {
1043         return (cred->rp.id);
1044 }
1045
1046 const char *
1047 fido_cred_rp_name(const fido_cred_t *cred)
1048 {
1049         return (cred->rp.name);
1050 }
1051
1052 const char *
1053 fido_cred_user_name(const fido_cred_t *cred)
1054 {
1055         return (cred->user.name);
1056 }
1057
1058 const char *
1059 fido_cred_display_name(const fido_cred_t *cred)
1060 {
1061         return (cred->user.display_name);
1062 }
1063
1064 const unsigned char *
1065 fido_cred_user_id_ptr(const fido_cred_t *cred)
1066 {
1067         return (cred->user.id.ptr);
1068 }
1069
1070 size_t
1071 fido_cred_user_id_len(const fido_cred_t *cred)
1072 {
1073         return (cred->user.id.len);
1074 }
1075
1076 const unsigned char *
1077 fido_cred_largeblob_key_ptr(const fido_cred_t *cred)
1078 {
1079         return (cred->largeblob_key.ptr);
1080 }
1081
1082 size_t
1083 fido_cred_largeblob_key_len(const fido_cred_t *cred)
1084 {
1085         return (cred->largeblob_key.len);
1086 }