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