1 /*
2 * Copyright (c) 2020-2022 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 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8 #include <openssl/sha.h>
9
10 #include "fido.h"
11 #include "fido/es256.h"
12
13 #define LARGEBLOB_DIGEST_LENGTH 16
14 #define LARGEBLOB_NONCE_LENGTH 12
15 #define LARGEBLOB_TAG_LENGTH 16
16
17 typedef struct largeblob {
18 size_t origsiz;
19 fido_blob_t ciphertext;
20 fido_blob_t nonce;
21 } largeblob_t;
22
23 static largeblob_t *
largeblob_new(void)24 largeblob_new(void)
25 {
26 return calloc(1, sizeof(largeblob_t));
27 }
28
29 static void
largeblob_reset(largeblob_t * blob)30 largeblob_reset(largeblob_t *blob)
31 {
32 fido_blob_reset(&blob->ciphertext);
33 fido_blob_reset(&blob->nonce);
34 blob->origsiz = 0;
35 }
36
37 static void
largeblob_free(largeblob_t ** blob_ptr)38 largeblob_free(largeblob_t **blob_ptr)
39 {
40 largeblob_t *blob;
41
42 if (blob_ptr == NULL || (blob = *blob_ptr) == NULL)
43 return;
44 largeblob_reset(blob);
45 free(blob);
46 *blob_ptr = NULL;
47 }
48
49 static int
largeblob_aad(fido_blob_t * aad,uint64_t size)50 largeblob_aad(fido_blob_t *aad, uint64_t size)
51 {
52 uint8_t buf[4 + sizeof(uint64_t)];
53
54 buf[0] = 0x62; /* b */
55 buf[1] = 0x6c; /* l */
56 buf[2] = 0x6f; /* o */
57 buf[3] = 0x62; /* b */
58 size = htole64(size);
59 memcpy(&buf[4], &size, sizeof(uint64_t));
60
61 return fido_blob_set(aad, buf, sizeof(buf));
62 }
63
64 static fido_blob_t *
largeblob_decrypt(const largeblob_t * blob,const fido_blob_t * key)65 largeblob_decrypt(const largeblob_t *blob, const fido_blob_t *key)
66 {
67 fido_blob_t *plaintext = NULL, *aad = NULL;
68 int ok = -1;
69
70 if ((plaintext = fido_blob_new()) == NULL ||
71 (aad = fido_blob_new()) == NULL) {
72 fido_log_debug("%s: fido_blob_new", __func__);
73 goto fail;
74 }
75 if (largeblob_aad(aad, blob->origsiz) < 0) {
76 fido_log_debug("%s: largeblob_aad", __func__);
77 goto fail;
78 }
79 if (aes256_gcm_dec(key, &blob->nonce, aad, &blob->ciphertext,
80 plaintext) < 0) {
81 fido_log_debug("%s: aes256_gcm_dec", __func__);
82 goto fail;
83 }
84
85 ok = 0;
86 fail:
87 fido_blob_free(&aad);
88
89 if (ok < 0)
90 fido_blob_free(&plaintext);
91
92 return plaintext;
93 }
94
95 static int
largeblob_get_nonce(largeblob_t * blob)96 largeblob_get_nonce(largeblob_t *blob)
97 {
98 uint8_t buf[LARGEBLOB_NONCE_LENGTH];
99 int ok = -1;
100
101 if (fido_get_random(buf, sizeof(buf)) < 0) {
102 fido_log_debug("%s: fido_get_random", __func__);
103 goto fail;
104 }
105 if (fido_blob_set(&blob->nonce, buf, sizeof(buf)) < 0) {
106 fido_log_debug("%s: fido_blob_set", __func__);
107 goto fail;
108 }
109
110 ok = 0;
111 fail:
112 explicit_bzero(buf, sizeof(buf));
113
114 return ok;
115 }
116
117 static int
largeblob_seal(largeblob_t * blob,const fido_blob_t * body,const fido_blob_t * key)118 largeblob_seal(largeblob_t *blob, const fido_blob_t *body,
119 const fido_blob_t *key)
120 {
121 fido_blob_t *plaintext = NULL, *aad = NULL;
122 int ok = -1;
123
124 if ((plaintext = fido_blob_new()) == NULL ||
125 (aad = fido_blob_new()) == NULL) {
126 fido_log_debug("%s: fido_blob_new", __func__);
127 goto fail;
128 }
129 if (fido_compress(plaintext, body) != FIDO_OK) {
130 fido_log_debug("%s: fido_compress", __func__);
131 goto fail;
132 }
133 if (largeblob_aad(aad, body->len) < 0) {
134 fido_log_debug("%s: largeblob_aad", __func__);
135 goto fail;
136 }
137 if (largeblob_get_nonce(blob) < 0) {
138 fido_log_debug("%s: largeblob_get_nonce", __func__);
139 goto fail;
140 }
141 if (aes256_gcm_enc(key, &blob->nonce, aad, plaintext,
142 &blob->ciphertext) < 0) {
143 fido_log_debug("%s: aes256_gcm_enc", __func__);
144 goto fail;
145 }
146 blob->origsiz = body->len;
147
148 ok = 0;
149 fail:
150 fido_blob_free(&plaintext);
151 fido_blob_free(&aad);
152
153 return ok;
154 }
155
156 static int
largeblob_get_tx(fido_dev_t * dev,size_t offset,size_t count,int * ms)157 largeblob_get_tx(fido_dev_t *dev, size_t offset, size_t count, int *ms)
158 {
159 fido_blob_t f;
160 cbor_item_t *argv[3];
161 int r;
162
163 memset(argv, 0, sizeof(argv));
164 memset(&f, 0, sizeof(f));
165
166 if ((argv[0] = cbor_build_uint(count)) == NULL ||
167 (argv[2] = cbor_build_uint(offset)) == NULL) {
168 fido_log_debug("%s: cbor encode", __func__);
169 r = FIDO_ERR_INTERNAL;
170 goto fail;
171 }
172 if (cbor_build_frame(CTAP_CBOR_LARGEBLOB, argv, nitems(argv), &f) < 0 ||
173 fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
174 fido_log_debug("%s: fido_tx", __func__);
175 r = FIDO_ERR_TX;
176 goto fail;
177 }
178
179 r = FIDO_OK;
180 fail:
181 cbor_vector_free(argv, nitems(argv));
182 free(f.ptr);
183
184 return r;
185 }
186
187 static int
parse_largeblob_reply(const cbor_item_t * key,const cbor_item_t * val,void * arg)188 parse_largeblob_reply(const cbor_item_t *key, const cbor_item_t *val,
189 void *arg)
190 {
191 if (cbor_isa_uint(key) == false ||
192 cbor_int_get_width(key) != CBOR_INT_8 ||
193 cbor_get_uint8(key) != 1) {
194 fido_log_debug("%s: cbor type", __func__);
195 return 0; /* ignore */
196 }
197
198 return fido_blob_decode(val, arg);
199 }
200
201 static int
largeblob_get_rx(fido_dev_t * dev,fido_blob_t ** chunk,int * ms)202 largeblob_get_rx(fido_dev_t *dev, fido_blob_t **chunk, int *ms)
203 {
204 unsigned char *msg;
205 int msglen, r;
206
207 *chunk = NULL;
208 if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
209 r = FIDO_ERR_INTERNAL;
210 goto out;
211 }
212 if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
213 fido_log_debug("%s: fido_rx", __func__);
214 r = FIDO_ERR_RX;
215 goto out;
216 }
217 if ((*chunk = fido_blob_new()) == NULL) {
218 fido_log_debug("%s: fido_blob_new", __func__);
219 r = FIDO_ERR_INTERNAL;
220 goto out;
221 }
222 if ((r = cbor_parse_reply(msg, (size_t)msglen, *chunk,
223 parse_largeblob_reply)) != FIDO_OK) {
224 fido_log_debug("%s: parse_largeblob_reply", __func__);
225 goto out;
226 }
227
228 r = FIDO_OK;
229 out:
230 if (r != FIDO_OK)
231 fido_blob_free(chunk);
232
233 freezero(msg, FIDO_MAXMSG);
234
235 return r;
236 }
237
238 static cbor_item_t *
largeblob_array_load(const uint8_t * ptr,size_t len)239 largeblob_array_load(const uint8_t *ptr, size_t len)
240 {
241 struct cbor_load_result cbor;
242 cbor_item_t *item;
243
244 if (len < LARGEBLOB_DIGEST_LENGTH) {
245 fido_log_debug("%s: len", __func__);
246 return NULL;
247 }
248 len -= LARGEBLOB_DIGEST_LENGTH;
249 if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
250 fido_log_debug("%s: cbor_load", __func__);
251 return NULL;
252 }
253 if (!cbor_isa_array(item) || !cbor_array_is_definite(item)) {
254 fido_log_debug("%s: cbor type", __func__);
255 cbor_decref(&item);
256 return NULL;
257 }
258
259 return item;
260 }
261
262 static size_t
get_chunklen(fido_dev_t * dev)263 get_chunklen(fido_dev_t *dev)
264 {
265 uint64_t maxchunklen;
266
267 if ((maxchunklen = fido_dev_maxmsgsize(dev)) > SIZE_MAX)
268 maxchunklen = SIZE_MAX;
269 if (maxchunklen > FIDO_MAXMSG)
270 maxchunklen = FIDO_MAXMSG;
271 maxchunklen = maxchunklen > 64 ? maxchunklen - 64 : 0;
272
273 return (size_t)maxchunklen;
274 }
275
276 static int
largeblob_do_decode(const cbor_item_t * key,const cbor_item_t * val,void * arg)277 largeblob_do_decode(const cbor_item_t *key, const cbor_item_t *val, void *arg)
278 {
279 largeblob_t *blob = arg;
280 uint64_t origsiz;
281
282 if (cbor_isa_uint(key) == false ||
283 cbor_int_get_width(key) != CBOR_INT_8) {
284 fido_log_debug("%s: cbor type", __func__);
285 return 0; /* ignore */
286 }
287
288 switch (cbor_get_uint8(key)) {
289 case 1: /* ciphertext */
290 if (fido_blob_decode(val, &blob->ciphertext) < 0 ||
291 blob->ciphertext.len < LARGEBLOB_TAG_LENGTH)
292 return -1;
293 return 0;
294 case 2: /* nonce */
295 if (fido_blob_decode(val, &blob->nonce) < 0 ||
296 blob->nonce.len != LARGEBLOB_NONCE_LENGTH)
297 return -1;
298 return 0;
299 case 3: /* origSize */
300 if (!cbor_isa_uint(val) ||
301 (origsiz = cbor_get_int(val)) > SIZE_MAX)
302 return -1;
303 blob->origsiz = (size_t)origsiz;
304 return 0;
305 default: /* ignore */
306 fido_log_debug("%s: cbor type", __func__);
307 return 0;
308 }
309 }
310
311 static int
largeblob_decode(largeblob_t * blob,const cbor_item_t * item)312 largeblob_decode(largeblob_t *blob, const cbor_item_t *item)
313 {
314 if (!cbor_isa_map(item) || !cbor_map_is_definite(item)) {
315 fido_log_debug("%s: cbor type", __func__);
316 return -1;
317 }
318 if (cbor_map_iter(item, blob, largeblob_do_decode) < 0) {
319 fido_log_debug("%s: cbor_map_iter", __func__);
320 return -1;
321 }
322 if (fido_blob_is_empty(&blob->ciphertext) ||
323 fido_blob_is_empty(&blob->nonce) || blob->origsiz == 0) {
324 fido_log_debug("%s: incomplete blob", __func__);
325 return -1;
326 }
327
328 return 0;
329 }
330
331 static cbor_item_t *
largeblob_encode(const fido_blob_t * body,const fido_blob_t * key)332 largeblob_encode(const fido_blob_t *body, const fido_blob_t *key)
333 {
334 largeblob_t *blob;
335 cbor_item_t *argv[3], *item = NULL;
336
337 memset(argv, 0, sizeof(argv));
338 if ((blob = largeblob_new()) == NULL ||
339 largeblob_seal(blob, body, key) < 0) {
340 fido_log_debug("%s: largeblob_seal", __func__);
341 goto fail;
342 }
343 if ((argv[0] = fido_blob_encode(&blob->ciphertext)) == NULL ||
344 (argv[1] = fido_blob_encode(&blob->nonce)) == NULL ||
345 (argv[2] = cbor_build_uint(blob->origsiz)) == NULL) {
346 fido_log_debug("%s: cbor encode", __func__);
347 goto fail;
348 }
349 item = cbor_flatten_vector(argv, nitems(argv));
350 fail:
351 cbor_vector_free(argv, nitems(argv));
352 largeblob_free(&blob);
353
354 return item;
355 }
356
357 static int
largeblob_array_lookup(fido_blob_t * out,size_t * idx,const cbor_item_t * item,const fido_blob_t * key)358 largeblob_array_lookup(fido_blob_t *out, size_t *idx, const cbor_item_t *item,
359 const fido_blob_t *key)
360 {
361 cbor_item_t **v;
362 fido_blob_t *plaintext = NULL;
363 largeblob_t blob;
364 int r;
365
366 memset(&blob, 0, sizeof(blob));
367 if (idx != NULL)
368 *idx = 0;
369 if ((v = cbor_array_handle(item)) == NULL)
370 return FIDO_ERR_INVALID_ARGUMENT;
371 for (size_t i = 0; i < cbor_array_size(item); i++) {
372 if (largeblob_decode(&blob, v[i]) < 0 ||
373 (plaintext = largeblob_decrypt(&blob, key)) == NULL) {
374 fido_log_debug("%s: largeblob_decode", __func__);
375 largeblob_reset(&blob);
376 continue;
377 }
378 if (idx != NULL)
379 *idx = i;
380 break;
381 }
382 if (plaintext == NULL) {
383 fido_log_debug("%s: not found", __func__);
384 return FIDO_ERR_NOTFOUND;
385 }
386 if (out != NULL)
387 r = fido_uncompress(out, plaintext, blob.origsiz);
388 else
389 r = FIDO_OK;
390
391 fido_blob_free(&plaintext);
392 largeblob_reset(&blob);
393
394 return r;
395 }
396
397 static int
largeblob_array_digest(u_char out[LARGEBLOB_DIGEST_LENGTH],const u_char * data,size_t len)398 largeblob_array_digest(u_char out[LARGEBLOB_DIGEST_LENGTH], const u_char *data,
399 size_t len)
400 {
401 u_char dgst[SHA256_DIGEST_LENGTH];
402
403 if (data == NULL || len == 0)
404 return -1;
405 if (SHA256(data, len, dgst) != dgst)
406 return -1;
407 memcpy(out, dgst, LARGEBLOB_DIGEST_LENGTH);
408
409 return 0;
410 }
411
412 static int
largeblob_array_check(const fido_blob_t * array)413 largeblob_array_check(const fido_blob_t *array)
414 {
415 u_char expected_hash[LARGEBLOB_DIGEST_LENGTH];
416 size_t body_len;
417
418 fido_log_xxd(array->ptr, array->len, __func__);
419 if (array->len < sizeof(expected_hash)) {
420 fido_log_debug("%s: len %zu", __func__, array->len);
421 return -1;
422 }
423 body_len = array->len - sizeof(expected_hash);
424 if (largeblob_array_digest(expected_hash, array->ptr, body_len) < 0) {
425 fido_log_debug("%s: largeblob_array_digest", __func__);
426 return -1;
427 }
428
429 return timingsafe_bcmp(expected_hash, array->ptr + body_len,
430 sizeof(expected_hash));
431 }
432
433 static int
largeblob_get_array(fido_dev_t * dev,cbor_item_t ** item,int * ms)434 largeblob_get_array(fido_dev_t *dev, cbor_item_t **item, int *ms)
435 {
436 fido_blob_t *array, *chunk = NULL;
437 size_t n;
438 int r;
439
440 *item = NULL;
441 if ((n = get_chunklen(dev)) == 0)
442 return FIDO_ERR_INVALID_ARGUMENT;
443 if ((array = fido_blob_new()) == NULL)
444 return FIDO_ERR_INTERNAL;
445 do {
446 fido_blob_free(&chunk);
447 if ((r = largeblob_get_tx(dev, array->len, n, ms)) != FIDO_OK ||
448 (r = largeblob_get_rx(dev, &chunk, ms)) != FIDO_OK) {
449 fido_log_debug("%s: largeblob_get_wait %zu/%zu",
450 __func__, array->len, n);
451 goto fail;
452 }
453 if (fido_blob_append(array, chunk->ptr, chunk->len) < 0) {
454 fido_log_debug("%s: fido_blob_append", __func__);
455 r = FIDO_ERR_INTERNAL;
456 goto fail;
457 }
458 } while (chunk->len == n);
459
460 if (largeblob_array_check(array) != 0)
461 *item = cbor_new_definite_array(0); /* per spec */
462 else
463 *item = largeblob_array_load(array->ptr, array->len);
464 if (*item == NULL)
465 r = FIDO_ERR_INTERNAL;
466 else
467 r = FIDO_OK;
468 fail:
469 fido_blob_free(&array);
470 fido_blob_free(&chunk);
471
472 return r;
473 }
474
475 static int
prepare_hmac(size_t offset,const u_char * data,size_t len,fido_blob_t * hmac)476 prepare_hmac(size_t offset, const u_char *data, size_t len, fido_blob_t *hmac)
477 {
478 uint8_t buf[32 + 2 + sizeof(uint32_t) + SHA256_DIGEST_LENGTH];
479 uint32_t u32_offset;
480
481 if (data == NULL || len == 0) {
482 fido_log_debug("%s: invalid data=%p, len=%zu", __func__,
483 (const void *)data, len);
484 return -1;
485 }
486 if (offset > UINT32_MAX) {
487 fido_log_debug("%s: invalid offset=%zu", __func__, offset);
488 return -1;
489 }
490
491 memset(buf, 0xff, 32);
492 buf[32] = CTAP_CBOR_LARGEBLOB;
493 buf[33] = 0x00;
494 u32_offset = htole32((uint32_t)offset);
495 memcpy(&buf[34], &u32_offset, sizeof(uint32_t));
496 if (SHA256(data, len, &buf[38]) != &buf[38]) {
497 fido_log_debug("%s: SHA256", __func__);
498 return -1;
499 }
500
501 return fido_blob_set(hmac, buf, sizeof(buf));
502 }
503
504 static int
largeblob_set_tx(fido_dev_t * dev,const fido_blob_t * token,const u_char * chunk,size_t chunk_len,size_t offset,size_t totalsiz,int * ms)505 largeblob_set_tx(fido_dev_t *dev, const fido_blob_t *token, const u_char *chunk,
506 size_t chunk_len, size_t offset, size_t totalsiz, int *ms)
507 {
508 fido_blob_t *hmac = NULL, f;
509 cbor_item_t *argv[6];
510 int r;
511
512 memset(argv, 0, sizeof(argv));
513 memset(&f, 0, sizeof(f));
514
515 if ((argv[1] = cbor_build_bytestring(chunk, chunk_len)) == NULL ||
516 (argv[2] = cbor_build_uint(offset)) == NULL ||
517 (offset == 0 && (argv[3] = cbor_build_uint(totalsiz)) == NULL)) {
518 fido_log_debug("%s: cbor encode", __func__);
519 r = FIDO_ERR_INTERNAL;
520 goto fail;
521 }
522 if (token != NULL) {
523 if ((hmac = fido_blob_new()) == NULL ||
524 prepare_hmac(offset, chunk, chunk_len, hmac) < 0 ||
525 (argv[4] = cbor_encode_pin_auth(dev, token, hmac)) == NULL ||
526 (argv[5] = cbor_encode_pin_opt(dev)) == NULL) {
527 fido_log_debug("%s: cbor_encode_pin_auth", __func__);
528 r = FIDO_ERR_INTERNAL;
529 goto fail;
530 }
531 }
532 if (cbor_build_frame(CTAP_CBOR_LARGEBLOB, argv, nitems(argv), &f) < 0 ||
533 fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
534 fido_log_debug("%s: fido_tx", __func__);
535 r = FIDO_ERR_TX;
536 goto fail;
537 }
538
539 r = FIDO_OK;
540 fail:
541 cbor_vector_free(argv, nitems(argv));
542 fido_blob_free(&hmac);
543 free(f.ptr);
544
545 return r;
546 }
547
548 static int
largeblob_get_uv_token(fido_dev_t * dev,const char * pin,fido_blob_t ** token,int * ms)549 largeblob_get_uv_token(fido_dev_t *dev, const char *pin, fido_blob_t **token,
550 int *ms)
551 {
552 es256_pk_t *pk = NULL;
553 fido_blob_t *ecdh = NULL;
554 int r;
555
556 if ((*token = fido_blob_new()) == NULL)
557 return FIDO_ERR_INTERNAL;
558 if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
559 fido_log_debug("%s: fido_do_ecdh", __func__);
560 goto fail;
561 }
562 if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_LARGEBLOB, pin, ecdh, pk,
563 NULL, *token, ms)) != FIDO_OK) {
564 fido_log_debug("%s: fido_dev_get_uv_token", __func__);
565 goto fail;
566 }
567
568 r = FIDO_OK;
569 fail:
570 if (r != FIDO_OK)
571 fido_blob_free(token);
572
573 fido_blob_free(&ecdh);
574 es256_pk_free(&pk);
575
576 return r;
577 }
578
579 static int
largeblob_set_array(fido_dev_t * dev,const cbor_item_t * item,const char * pin,int * ms)580 largeblob_set_array(fido_dev_t *dev, const cbor_item_t *item, const char *pin,
581 int *ms)
582 {
583 unsigned char dgst[SHA256_DIGEST_LENGTH];
584 fido_blob_t cbor, *token = NULL;
585 size_t chunklen, maxchunklen, totalsize;
586 int r;
587
588 memset(&cbor, 0, sizeof(cbor));
589
590 if ((maxchunklen = get_chunklen(dev)) == 0) {
591 fido_log_debug("%s: maxchunklen=%zu", __func__, maxchunklen);
592 r = FIDO_ERR_INVALID_ARGUMENT;
593 goto fail;
594 }
595 if (!cbor_isa_array(item) || !cbor_array_is_definite(item)) {
596 fido_log_debug("%s: cbor type", __func__);
597 r = FIDO_ERR_INVALID_ARGUMENT;
598 goto fail;
599 }
600 if ((fido_blob_serialise(&cbor, item)) < 0) {
601 fido_log_debug("%s: fido_blob_serialise", __func__);
602 r = FIDO_ERR_INTERNAL;
603 goto fail;
604 }
605 if (cbor.len > SIZE_MAX - sizeof(dgst)) {
606 fido_log_debug("%s: cbor.len=%zu", __func__, cbor.len);
607 r = FIDO_ERR_INVALID_ARGUMENT;
608 goto fail;
609 }
610 if (SHA256(cbor.ptr, cbor.len, dgst) != dgst) {
611 fido_log_debug("%s: SHA256", __func__);
612 r = FIDO_ERR_INTERNAL;
613 goto fail;
614 }
615 totalsize = cbor.len + sizeof(dgst) - 16; /* the first 16 bytes only */
616 if (pin != NULL || fido_dev_supports_permissions(dev)) {
617 if ((r = largeblob_get_uv_token(dev, pin, &token,
618 ms)) != FIDO_OK) {
619 fido_log_debug("%s: largeblob_get_uv_token", __func__);
620 goto fail;
621 }
622 }
623 for (size_t offset = 0; offset < cbor.len; offset += chunklen) {
624 if ((chunklen = cbor.len - offset) > maxchunklen)
625 chunklen = maxchunklen;
626 if ((r = largeblob_set_tx(dev, token, cbor.ptr + offset,
627 chunklen, offset, totalsize, ms)) != FIDO_OK ||
628 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
629 fido_log_debug("%s: body", __func__);
630 goto fail;
631 }
632 }
633 if ((r = largeblob_set_tx(dev, token, dgst, sizeof(dgst) - 16, cbor.len,
634 totalsize, ms)) != FIDO_OK ||
635 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
636 fido_log_debug("%s: dgst", __func__);
637 goto fail;
638 }
639
640 r = FIDO_OK;
641 fail:
642 fido_blob_free(&token);
643 fido_blob_reset(&cbor);
644
645 return r;
646 }
647
648 static int
largeblob_add(fido_dev_t * dev,const fido_blob_t * key,cbor_item_t * item,const char * pin,int * ms)649 largeblob_add(fido_dev_t *dev, const fido_blob_t *key, cbor_item_t *item,
650 const char *pin, int *ms)
651 {
652 cbor_item_t *array = NULL;
653 size_t idx;
654 int r;
655
656 if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) {
657 fido_log_debug("%s: largeblob_get_array", __func__);
658 goto fail;
659 }
660
661 switch (r = largeblob_array_lookup(NULL, &idx, array, key)) {
662 case FIDO_OK:
663 if (!cbor_array_replace(array, idx, item)) {
664 r = FIDO_ERR_INTERNAL;
665 goto fail;
666 }
667 break;
668 case FIDO_ERR_NOTFOUND:
669 if (cbor_array_append(&array, item) < 0) {
670 r = FIDO_ERR_INTERNAL;
671 goto fail;
672 }
673 break;
674 default:
675 fido_log_debug("%s: largeblob_array_lookup", __func__);
676 goto fail;
677 }
678
679 if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) {
680 fido_log_debug("%s: largeblob_set_array", __func__);
681 goto fail;
682 }
683
684 r = FIDO_OK;
685 fail:
686 if (array != NULL)
687 cbor_decref(&array);
688
689 return r;
690 }
691
692 static int
largeblob_drop(fido_dev_t * dev,const fido_blob_t * key,const char * pin,int * ms)693 largeblob_drop(fido_dev_t *dev, const fido_blob_t *key, const char *pin,
694 int *ms)
695 {
696 cbor_item_t *array = NULL;
697 size_t idx;
698 int r;
699
700 if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) {
701 fido_log_debug("%s: largeblob_get_array", __func__);
702 goto fail;
703 }
704 if ((r = largeblob_array_lookup(NULL, &idx, array, key)) != FIDO_OK) {
705 fido_log_debug("%s: largeblob_array_lookup", __func__);
706 goto fail;
707 }
708 if (cbor_array_drop(&array, idx) < 0) {
709 fido_log_debug("%s: cbor_array_drop", __func__);
710 r = FIDO_ERR_INTERNAL;
711 goto fail;
712 }
713 if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) {
714 fido_log_debug("%s: largeblob_set_array", __func__);
715 goto fail;
716 }
717
718 r = FIDO_OK;
719 fail:
720 if (array != NULL)
721 cbor_decref(&array);
722
723 return r;
724 }
725
726 int
fido_dev_largeblob_get(fido_dev_t * dev,const unsigned char * key_ptr,size_t key_len,unsigned char ** blob_ptr,size_t * blob_len)727 fido_dev_largeblob_get(fido_dev_t *dev, const unsigned char *key_ptr,
728 size_t key_len, unsigned char **blob_ptr, size_t *blob_len)
729 {
730 cbor_item_t *item = NULL;
731 fido_blob_t key, body;
732 int ms = dev->timeout_ms;
733 int r;
734
735 memset(&key, 0, sizeof(key));
736 memset(&body, 0, sizeof(body));
737
738 if (key_len != 32) {
739 fido_log_debug("%s: invalid key len %zu", __func__, key_len);
740 return FIDO_ERR_INVALID_ARGUMENT;
741 }
742 if (blob_ptr == NULL || blob_len == NULL) {
743 fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%p", __func__,
744 (const void *)blob_ptr, (const void *)blob_len);
745 return FIDO_ERR_INVALID_ARGUMENT;
746 }
747 *blob_ptr = NULL;
748 *blob_len = 0;
749 if (fido_blob_set(&key, key_ptr, key_len) < 0) {
750 fido_log_debug("%s: fido_blob_set", __func__);
751 return FIDO_ERR_INTERNAL;
752 }
753 if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) {
754 fido_log_debug("%s: largeblob_get_array", __func__);
755 goto fail;
756 }
757 if ((r = largeblob_array_lookup(&body, NULL, item, &key)) != FIDO_OK)
758 fido_log_debug("%s: largeblob_array_lookup", __func__);
759 else {
760 *blob_ptr = body.ptr;
761 *blob_len = body.len;
762 }
763 fail:
764 if (item != NULL)
765 cbor_decref(&item);
766
767 fido_blob_reset(&key);
768
769 return r;
770 }
771
772 int
fido_dev_largeblob_set(fido_dev_t * dev,const unsigned char * key_ptr,size_t key_len,const unsigned char * blob_ptr,size_t blob_len,const char * pin)773 fido_dev_largeblob_set(fido_dev_t *dev, const unsigned char *key_ptr,
774 size_t key_len, const unsigned char *blob_ptr, size_t blob_len,
775 const char *pin)
776 {
777 cbor_item_t *item = NULL;
778 fido_blob_t key, body;
779 int ms = dev->timeout_ms;
780 int r;
781
782 memset(&key, 0, sizeof(key));
783 memset(&body, 0, sizeof(body));
784
785 if (key_len != 32) {
786 fido_log_debug("%s: invalid key len %zu", __func__, key_len);
787 return FIDO_ERR_INVALID_ARGUMENT;
788 }
789 if (blob_ptr == NULL || blob_len == 0) {
790 fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%zu", __func__,
791 (const void *)blob_ptr, blob_len);
792 return FIDO_ERR_INVALID_ARGUMENT;
793 }
794 if (fido_blob_set(&key, key_ptr, key_len) < 0 ||
795 fido_blob_set(&body, blob_ptr, blob_len) < 0) {
796 fido_log_debug("%s: fido_blob_set", __func__);
797 r = FIDO_ERR_INTERNAL;
798 goto fail;
799 }
800 if ((item = largeblob_encode(&body, &key)) == NULL) {
801 fido_log_debug("%s: largeblob_encode", __func__);
802 r = FIDO_ERR_INTERNAL;
803 goto fail;
804 }
805 if ((r = largeblob_add(dev, &key, item, pin, &ms)) != FIDO_OK)
806 fido_log_debug("%s: largeblob_add", __func__);
807 fail:
808 if (item != NULL)
809 cbor_decref(&item);
810
811 fido_blob_reset(&key);
812 fido_blob_reset(&body);
813
814 return r;
815 }
816
817 int
fido_dev_largeblob_remove(fido_dev_t * dev,const unsigned char * key_ptr,size_t key_len,const char * pin)818 fido_dev_largeblob_remove(fido_dev_t *dev, const unsigned char *key_ptr,
819 size_t key_len, const char *pin)
820 {
821 fido_blob_t key;
822 int ms = dev->timeout_ms;
823 int r;
824
825 memset(&key, 0, sizeof(key));
826
827 if (key_len != 32) {
828 fido_log_debug("%s: invalid key len %zu", __func__, key_len);
829 return FIDO_ERR_INVALID_ARGUMENT;
830 }
831 if (fido_blob_set(&key, key_ptr, key_len) < 0) {
832 fido_log_debug("%s: fido_blob_set", __func__);
833 return FIDO_ERR_INTERNAL;
834 }
835 if ((r = largeblob_drop(dev, &key, pin, &ms)) != FIDO_OK)
836 fido_log_debug("%s: largeblob_drop", __func__);
837
838 fido_blob_reset(&key);
839
840 return r;
841 }
842
843 int
fido_dev_largeblob_get_array(fido_dev_t * dev,unsigned char ** cbor_ptr,size_t * cbor_len)844 fido_dev_largeblob_get_array(fido_dev_t *dev, unsigned char **cbor_ptr,
845 size_t *cbor_len)
846 {
847 cbor_item_t *item = NULL;
848 fido_blob_t cbor;
849 int ms = dev->timeout_ms;
850 int r;
851
852 memset(&cbor, 0, sizeof(cbor));
853
854 if (cbor_ptr == NULL || cbor_len == NULL) {
855 fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%p", __func__,
856 (const void *)cbor_ptr, (const void *)cbor_len);
857 return FIDO_ERR_INVALID_ARGUMENT;
858 }
859 *cbor_ptr = NULL;
860 *cbor_len = 0;
861 if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) {
862 fido_log_debug("%s: largeblob_get_array", __func__);
863 return r;
864 }
865 if (fido_blob_serialise(&cbor, item) < 0) {
866 fido_log_debug("%s: fido_blob_serialise", __func__);
867 r = FIDO_ERR_INTERNAL;
868 } else {
869 *cbor_ptr = cbor.ptr;
870 *cbor_len = cbor.len;
871 }
872
873 cbor_decref(&item);
874
875 return r;
876 }
877
878 int
fido_dev_largeblob_set_array(fido_dev_t * dev,const unsigned char * cbor_ptr,size_t cbor_len,const char * pin)879 fido_dev_largeblob_set_array(fido_dev_t *dev, const unsigned char *cbor_ptr,
880 size_t cbor_len, const char *pin)
881 {
882 cbor_item_t *item = NULL;
883 struct cbor_load_result cbor_result;
884 int ms = dev->timeout_ms;
885 int r;
886
887 if (cbor_ptr == NULL || cbor_len == 0) {
888 fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%zu", __func__,
889 (const void *)cbor_ptr, cbor_len);
890 return FIDO_ERR_INVALID_ARGUMENT;
891 }
892 if ((item = cbor_load(cbor_ptr, cbor_len, &cbor_result)) == NULL) {
893 fido_log_debug("%s: cbor_load", __func__);
894 return FIDO_ERR_INVALID_ARGUMENT;
895 }
896 if ((r = largeblob_set_array(dev, item, pin, &ms)) != FIDO_OK)
897 fido_log_debug("%s: largeblob_set_array", __func__);
898
899 cbor_decref(&item);
900
901 return r;
902 }
903