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 * 24 largeblob_new(void) 25 { 26 return calloc(1, sizeof(largeblob_t)); 27 } 28 29 static void 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 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 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 * 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 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 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 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 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 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 * 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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