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 * 23 largeblob_new(void) 24 { 25 return calloc(1, sizeof(largeblob_t)); 26 } 27 28 static void 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 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 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 * 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 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 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 156 largeblob_get_tx(fido_dev_t *dev, size_t offset, size_t count, int *ms) 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, ms) < 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 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 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 * 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 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 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 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 * 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 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 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 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 422 largeblob_get_array(fido_dev_t *dev, cbor_item_t **item, int *ms) 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, ms)) != FIDO_OK || 436 (r = largeblob_get_rx(dev, &chunk, ms)) != 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 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 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, int *ms) 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, ms) < 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 537 largeblob_get_uv_token(fido_dev_t *dev, const char *pin, fido_blob_t **token, 538 int *ms) 539 { 540 es256_pk_t *pk = NULL; 541 fido_blob_t *ecdh = NULL; 542 int r; 543 544 if ((*token = fido_blob_new()) == NULL) 545 return FIDO_ERR_INTERNAL; 546 if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) { 547 fido_log_debug("%s: fido_do_ecdh", __func__); 548 goto fail; 549 } 550 if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_LARGEBLOB, pin, ecdh, pk, 551 NULL, *token, ms)) != FIDO_OK) { 552 fido_log_debug("%s: fido_dev_get_uv_token", __func__); 553 goto fail; 554 } 555 556 r = FIDO_OK; 557 fail: 558 if (r != FIDO_OK) 559 fido_blob_free(token); 560 561 fido_blob_free(&ecdh); 562 es256_pk_free(&pk); 563 564 return r; 565 } 566 567 static int 568 largeblob_set_array(fido_dev_t *dev, const cbor_item_t *item, const char *pin, 569 int *ms) 570 { 571 unsigned char dgst[SHA256_DIGEST_LENGTH]; 572 fido_blob_t cbor, *token = NULL; 573 size_t chunklen, maxchunklen, totalsize; 574 int r; 575 576 memset(&cbor, 0, sizeof(cbor)); 577 578 if ((maxchunklen = get_chunklen(dev)) == 0) { 579 fido_log_debug("%s: maxchunklen=%zu", __func__, maxchunklen); 580 r = FIDO_ERR_INVALID_ARGUMENT; 581 goto fail; 582 } 583 if (!cbor_isa_array(item) || !cbor_array_is_definite(item)) { 584 fido_log_debug("%s: cbor type", __func__); 585 r = FIDO_ERR_INVALID_ARGUMENT; 586 goto fail; 587 } 588 if ((fido_blob_serialise(&cbor, item)) < 0) { 589 fido_log_debug("%s: fido_blob_serialise", __func__); 590 r = FIDO_ERR_INTERNAL; 591 goto fail; 592 } 593 if (cbor.len > SIZE_MAX - sizeof(dgst)) { 594 fido_log_debug("%s: cbor.len=%zu", __func__, cbor.len); 595 r = FIDO_ERR_INVALID_ARGUMENT; 596 goto fail; 597 } 598 if (SHA256(cbor.ptr, cbor.len, dgst) != dgst) { 599 fido_log_debug("%s: SHA256", __func__); 600 r = FIDO_ERR_INTERNAL; 601 goto fail; 602 } 603 totalsize = cbor.len + sizeof(dgst) - 16; /* the first 16 bytes only */ 604 if (pin != NULL || fido_dev_supports_permissions(dev)) { 605 if ((r = largeblob_get_uv_token(dev, pin, &token, 606 ms)) != FIDO_OK) { 607 fido_log_debug("%s: largeblob_get_uv_token", __func__); 608 goto fail; 609 } 610 } 611 for (size_t offset = 0; offset < cbor.len; offset += chunklen) { 612 if ((chunklen = cbor.len - offset) > maxchunklen) 613 chunklen = maxchunklen; 614 if ((r = largeblob_set_tx(dev, token, cbor.ptr + offset, 615 chunklen, offset, totalsize, ms)) != FIDO_OK || 616 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { 617 fido_log_debug("%s: body", __func__); 618 goto fail; 619 } 620 } 621 if ((r = largeblob_set_tx(dev, token, dgst, sizeof(dgst) - 16, cbor.len, 622 totalsize, ms)) != FIDO_OK || 623 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { 624 fido_log_debug("%s: dgst", __func__); 625 goto fail; 626 } 627 628 r = FIDO_OK; 629 fail: 630 fido_blob_free(&token); 631 fido_blob_reset(&cbor); 632 633 return r; 634 } 635 636 static int 637 largeblob_add(fido_dev_t *dev, const fido_blob_t *key, cbor_item_t *item, 638 const char *pin, int *ms) 639 { 640 cbor_item_t *array = NULL; 641 size_t idx; 642 int r; 643 644 if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) { 645 fido_log_debug("%s: largeblob_get_array", __func__); 646 goto fail; 647 } 648 649 switch (r = largeblob_array_lookup(NULL, &idx, array, key)) { 650 case FIDO_OK: 651 if (!cbor_array_replace(array, idx, item)) { 652 r = FIDO_ERR_INTERNAL; 653 goto fail; 654 } 655 break; 656 case FIDO_ERR_NOTFOUND: 657 if (cbor_array_append(&array, item) < 0) { 658 r = FIDO_ERR_INTERNAL; 659 goto fail; 660 } 661 break; 662 default: 663 fido_log_debug("%s: largeblob_array_lookup", __func__); 664 goto fail; 665 } 666 667 if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) { 668 fido_log_debug("%s: largeblob_set_array", __func__); 669 goto fail; 670 } 671 672 r = FIDO_OK; 673 fail: 674 if (array != NULL) 675 cbor_decref(&array); 676 677 return r; 678 } 679 680 static int 681 largeblob_drop(fido_dev_t *dev, const fido_blob_t *key, const char *pin, 682 int *ms) 683 { 684 cbor_item_t *array = NULL; 685 size_t idx; 686 int r; 687 688 if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) { 689 fido_log_debug("%s: largeblob_get_array", __func__); 690 goto fail; 691 } 692 if ((r = largeblob_array_lookup(NULL, &idx, array, key)) != FIDO_OK) { 693 fido_log_debug("%s: largeblob_array_lookup", __func__); 694 goto fail; 695 } 696 if (cbor_array_drop(&array, idx) < 0) { 697 fido_log_debug("%s: cbor_array_drop", __func__); 698 r = FIDO_ERR_INTERNAL; 699 goto fail; 700 } 701 if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) { 702 fido_log_debug("%s: largeblob_set_array", __func__); 703 goto fail; 704 } 705 706 r = FIDO_OK; 707 fail: 708 if (array != NULL) 709 cbor_decref(&array); 710 711 return r; 712 } 713 714 int 715 fido_dev_largeblob_get(fido_dev_t *dev, const unsigned char *key_ptr, 716 size_t key_len, unsigned char **blob_ptr, size_t *blob_len) 717 { 718 cbor_item_t *item = NULL; 719 fido_blob_t key, body; 720 int ms = dev->timeout_ms; 721 int r; 722 723 memset(&key, 0, sizeof(key)); 724 memset(&body, 0, sizeof(body)); 725 726 if (key_len != 32) { 727 fido_log_debug("%s: invalid key len %zu", __func__, key_len); 728 return FIDO_ERR_INVALID_ARGUMENT; 729 } 730 if (blob_ptr == NULL || blob_len == NULL) { 731 fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%p", __func__, 732 (const void *)blob_ptr, (const void *)blob_len); 733 return FIDO_ERR_INVALID_ARGUMENT; 734 } 735 *blob_ptr = NULL; 736 *blob_len = 0; 737 if (fido_blob_set(&key, key_ptr, key_len) < 0) { 738 fido_log_debug("%s: fido_blob_set", __func__); 739 return FIDO_ERR_INTERNAL; 740 } 741 if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) { 742 fido_log_debug("%s: largeblob_get_array", __func__); 743 goto fail; 744 } 745 if ((r = largeblob_array_lookup(&body, NULL, item, &key)) != FIDO_OK) 746 fido_log_debug("%s: largeblob_array_lookup", __func__); 747 else { 748 *blob_ptr = body.ptr; 749 *blob_len = body.len; 750 } 751 fail: 752 if (item != NULL) 753 cbor_decref(&item); 754 755 fido_blob_reset(&key); 756 757 return r; 758 } 759 760 int 761 fido_dev_largeblob_set(fido_dev_t *dev, const unsigned char *key_ptr, 762 size_t key_len, const unsigned char *blob_ptr, size_t blob_len, 763 const char *pin) 764 { 765 cbor_item_t *item = NULL; 766 fido_blob_t key, body; 767 int ms = dev->timeout_ms; 768 int r; 769 770 memset(&key, 0, sizeof(key)); 771 memset(&body, 0, sizeof(body)); 772 773 if (key_len != 32) { 774 fido_log_debug("%s: invalid key len %zu", __func__, key_len); 775 return FIDO_ERR_INVALID_ARGUMENT; 776 } 777 if (blob_ptr == NULL || blob_len == 0) { 778 fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%zu", __func__, 779 (const void *)blob_ptr, blob_len); 780 return FIDO_ERR_INVALID_ARGUMENT; 781 } 782 if (fido_blob_set(&key, key_ptr, key_len) < 0 || 783 fido_blob_set(&body, blob_ptr, blob_len) < 0) { 784 fido_log_debug("%s: fido_blob_set", __func__); 785 r = FIDO_ERR_INTERNAL; 786 goto fail; 787 } 788 if ((item = largeblob_encode(&body, &key)) == NULL) { 789 fido_log_debug("%s: largeblob_encode", __func__); 790 r = FIDO_ERR_INTERNAL; 791 goto fail; 792 } 793 if ((r = largeblob_add(dev, &key, item, pin, &ms)) != FIDO_OK) 794 fido_log_debug("%s: largeblob_add", __func__); 795 fail: 796 if (item != NULL) 797 cbor_decref(&item); 798 799 fido_blob_reset(&key); 800 fido_blob_reset(&body); 801 802 return r; 803 } 804 805 int 806 fido_dev_largeblob_remove(fido_dev_t *dev, const unsigned char *key_ptr, 807 size_t key_len, const char *pin) 808 { 809 fido_blob_t key; 810 int ms = dev->timeout_ms; 811 int r; 812 813 memset(&key, 0, sizeof(key)); 814 815 if (key_len != 32) { 816 fido_log_debug("%s: invalid key len %zu", __func__, key_len); 817 return FIDO_ERR_INVALID_ARGUMENT; 818 } 819 if (fido_blob_set(&key, key_ptr, key_len) < 0) { 820 fido_log_debug("%s: fido_blob_set", __func__); 821 return FIDO_ERR_INTERNAL; 822 } 823 if ((r = largeblob_drop(dev, &key, pin, &ms)) != FIDO_OK) 824 fido_log_debug("%s: largeblob_drop", __func__); 825 826 fido_blob_reset(&key); 827 828 return r; 829 } 830 831 int 832 fido_dev_largeblob_get_array(fido_dev_t *dev, unsigned char **cbor_ptr, 833 size_t *cbor_len) 834 { 835 cbor_item_t *item = NULL; 836 fido_blob_t cbor; 837 int ms = dev->timeout_ms; 838 int r; 839 840 memset(&cbor, 0, sizeof(cbor)); 841 842 if (cbor_ptr == NULL || cbor_len == NULL) { 843 fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%p", __func__, 844 (const void *)cbor_ptr, (const void *)cbor_len); 845 return FIDO_ERR_INVALID_ARGUMENT; 846 } 847 *cbor_ptr = NULL; 848 *cbor_len = 0; 849 if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) { 850 fido_log_debug("%s: largeblob_get_array", __func__); 851 return r; 852 } 853 if (fido_blob_serialise(&cbor, item) < 0) { 854 fido_log_debug("%s: fido_blob_serialise", __func__); 855 r = FIDO_ERR_INTERNAL; 856 } else { 857 *cbor_ptr = cbor.ptr; 858 *cbor_len = cbor.len; 859 } 860 861 cbor_decref(&item); 862 863 return r; 864 } 865 866 int 867 fido_dev_largeblob_set_array(fido_dev_t *dev, const unsigned char *cbor_ptr, 868 size_t cbor_len, const char *pin) 869 { 870 cbor_item_t *item = NULL; 871 struct cbor_load_result cbor_result; 872 int ms = dev->timeout_ms; 873 int r; 874 875 if (cbor_ptr == NULL || cbor_len == 0) { 876 fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%zu", __func__, 877 (const void *)cbor_ptr, cbor_len); 878 return FIDO_ERR_INVALID_ARGUMENT; 879 } 880 if ((item = cbor_load(cbor_ptr, cbor_len, &cbor_result)) == NULL) { 881 fido_log_debug("%s: cbor_load", __func__); 882 return FIDO_ERR_INVALID_ARGUMENT; 883 } 884 if ((r = largeblob_set_array(dev, item, pin, &ms)) != FIDO_OK) 885 fido_log_debug("%s: largeblob_set_array", __func__); 886 887 cbor_decref(&item); 888 889 return r; 890 } 891