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