1*0afa8e06SEd Maste /* 2*0afa8e06SEd Maste * Copyright (c) 2020 Yubico AB. All rights reserved. 3*0afa8e06SEd Maste * Use of this source code is governed by a BSD-style 4*0afa8e06SEd Maste * license that can be found in the LICENSE file. 5*0afa8e06SEd Maste */ 6*0afa8e06SEd Maste 7*0afa8e06SEd Maste #include <sys/types.h> 8*0afa8e06SEd Maste #include <sys/stat.h> 9*0afa8e06SEd Maste 10*0afa8e06SEd Maste #include <fido.h> 11*0afa8e06SEd Maste #include <fido/credman.h> 12*0afa8e06SEd Maste 13*0afa8e06SEd Maste #include <cbor.h> 14*0afa8e06SEd Maste #include <fcntl.h> 15*0afa8e06SEd Maste #include <limits.h> 16*0afa8e06SEd Maste #include <stdio.h> 17*0afa8e06SEd Maste #include <stdlib.h> 18*0afa8e06SEd Maste #include <string.h> 19*0afa8e06SEd Maste #ifdef HAVE_UNISTD_H 20*0afa8e06SEd Maste #include <unistd.h> 21*0afa8e06SEd Maste #endif 22*0afa8e06SEd Maste #include <zlib.h> 23*0afa8e06SEd Maste 24*0afa8e06SEd Maste #include "../openbsd-compat/openbsd-compat.h" 25*0afa8e06SEd Maste #include "extern.h" 26*0afa8e06SEd Maste 27*0afa8e06SEd Maste struct rkmap { 28*0afa8e06SEd Maste fido_credman_rp_t *rp; /* known rps */ 29*0afa8e06SEd Maste fido_credman_rk_t **rk; /* rk per rp */ 30*0afa8e06SEd Maste }; 31*0afa8e06SEd Maste 32*0afa8e06SEd Maste static void 33*0afa8e06SEd Maste free_rkmap(struct rkmap *map) 34*0afa8e06SEd Maste { 35*0afa8e06SEd Maste if (map->rp != NULL) { 36*0afa8e06SEd Maste for (size_t i = 0; i < fido_credman_rp_count(map->rp); i++) 37*0afa8e06SEd Maste fido_credman_rk_free(&map->rk[i]); 38*0afa8e06SEd Maste fido_credman_rp_free(&map->rp); 39*0afa8e06SEd Maste } 40*0afa8e06SEd Maste free(map->rk); 41*0afa8e06SEd Maste } 42*0afa8e06SEd Maste 43*0afa8e06SEd Maste static int 44*0afa8e06SEd Maste map_known_rps(fido_dev_t *dev, const char *path, struct rkmap *map) 45*0afa8e06SEd Maste { 46*0afa8e06SEd Maste const char *rp_id; 47*0afa8e06SEd Maste char *pin = NULL; 48*0afa8e06SEd Maste size_t n; 49*0afa8e06SEd Maste int r, ok = -1; 50*0afa8e06SEd Maste 51*0afa8e06SEd Maste if ((map->rp = fido_credman_rp_new()) == NULL) { 52*0afa8e06SEd Maste warnx("%s: fido_credman_rp_new", __func__); 53*0afa8e06SEd Maste goto out; 54*0afa8e06SEd Maste } 55*0afa8e06SEd Maste if ((pin = get_pin(path)) == NULL) 56*0afa8e06SEd Maste goto out; 57*0afa8e06SEd Maste if ((r = fido_credman_get_dev_rp(dev, map->rp, pin)) != FIDO_OK) { 58*0afa8e06SEd Maste warnx("fido_credman_get_dev_rp: %s", fido_strerr(r)); 59*0afa8e06SEd Maste goto out; 60*0afa8e06SEd Maste } 61*0afa8e06SEd Maste if ((n = fido_credman_rp_count(map->rp)) > UINT8_MAX) { 62*0afa8e06SEd Maste warnx("%s: fido_credman_rp_count > UINT8_MAX", __func__); 63*0afa8e06SEd Maste goto out; 64*0afa8e06SEd Maste } 65*0afa8e06SEd Maste if ((map->rk = calloc(n, sizeof(*map->rk))) == NULL) { 66*0afa8e06SEd Maste warnx("%s: calloc", __func__); 67*0afa8e06SEd Maste goto out; 68*0afa8e06SEd Maste } 69*0afa8e06SEd Maste for (size_t i = 0; i < n; i++) { 70*0afa8e06SEd Maste if ((rp_id = fido_credman_rp_id(map->rp, i)) == NULL) { 71*0afa8e06SEd Maste warnx("%s: fido_credman_rp_id %zu", __func__, i); 72*0afa8e06SEd Maste goto out; 73*0afa8e06SEd Maste } 74*0afa8e06SEd Maste if ((map->rk[i] = fido_credman_rk_new()) == NULL) { 75*0afa8e06SEd Maste warnx("%s: fido_credman_rk_new", __func__); 76*0afa8e06SEd Maste goto out; 77*0afa8e06SEd Maste } 78*0afa8e06SEd Maste if ((r = fido_credman_get_dev_rk(dev, rp_id, map->rk[i], 79*0afa8e06SEd Maste pin)) != FIDO_OK) { 80*0afa8e06SEd Maste warnx("%s: fido_credman_get_dev_rk %s: %s", __func__, 81*0afa8e06SEd Maste rp_id, fido_strerr(r)); 82*0afa8e06SEd Maste goto out; 83*0afa8e06SEd Maste } 84*0afa8e06SEd Maste } 85*0afa8e06SEd Maste 86*0afa8e06SEd Maste ok = 0; 87*0afa8e06SEd Maste out: 88*0afa8e06SEd Maste freezero(pin, PINBUF_LEN); 89*0afa8e06SEd Maste 90*0afa8e06SEd Maste return ok; 91*0afa8e06SEd Maste } 92*0afa8e06SEd Maste 93*0afa8e06SEd Maste static int 94*0afa8e06SEd Maste lookup_key(const char *path, fido_dev_t *dev, const char *rp_id, 95*0afa8e06SEd Maste const struct blob *cred_id, char **pin, struct blob *key) 96*0afa8e06SEd Maste { 97*0afa8e06SEd Maste fido_credman_rk_t *rk = NULL; 98*0afa8e06SEd Maste const fido_cred_t *cred = NULL; 99*0afa8e06SEd Maste size_t i, n; 100*0afa8e06SEd Maste int r, ok = -1; 101*0afa8e06SEd Maste 102*0afa8e06SEd Maste if ((rk = fido_credman_rk_new()) == NULL) { 103*0afa8e06SEd Maste warnx("%s: fido_credman_rk_new", __func__); 104*0afa8e06SEd Maste goto out; 105*0afa8e06SEd Maste } 106*0afa8e06SEd Maste if ((r = fido_credman_get_dev_rk(dev, rp_id, rk, *pin)) != FIDO_OK && 107*0afa8e06SEd Maste *pin == NULL && should_retry_with_pin(dev, r)) { 108*0afa8e06SEd Maste if ((*pin = get_pin(path)) == NULL) 109*0afa8e06SEd Maste goto out; 110*0afa8e06SEd Maste r = fido_credman_get_dev_rk(dev, rp_id, rk, *pin); 111*0afa8e06SEd Maste } 112*0afa8e06SEd Maste if (r != FIDO_OK) { 113*0afa8e06SEd Maste warnx("%s: fido_credman_get_dev_rk: %s", __func__, 114*0afa8e06SEd Maste fido_strerr(r)); 115*0afa8e06SEd Maste goto out; 116*0afa8e06SEd Maste } 117*0afa8e06SEd Maste if ((n = fido_credman_rk_count(rk)) == 0) { 118*0afa8e06SEd Maste warnx("%s: rp id not found", __func__); 119*0afa8e06SEd Maste goto out; 120*0afa8e06SEd Maste } 121*0afa8e06SEd Maste if (n == 1 && cred_id->len == 0) { 122*0afa8e06SEd Maste /* use the credential we found */ 123*0afa8e06SEd Maste cred = fido_credman_rk(rk, 0); 124*0afa8e06SEd Maste } else { 125*0afa8e06SEd Maste if (cred_id->len == 0) { 126*0afa8e06SEd Maste warnx("%s: multiple credentials found", __func__); 127*0afa8e06SEd Maste goto out; 128*0afa8e06SEd Maste } 129*0afa8e06SEd Maste for (i = 0; i < n; i++) { 130*0afa8e06SEd Maste const fido_cred_t *x = fido_credman_rk(rk, i); 131*0afa8e06SEd Maste if (fido_cred_id_len(x) <= cred_id->len && 132*0afa8e06SEd Maste !memcmp(fido_cred_id_ptr(x), cred_id->ptr, 133*0afa8e06SEd Maste fido_cred_id_len(x))) { 134*0afa8e06SEd Maste cred = x; 135*0afa8e06SEd Maste break; 136*0afa8e06SEd Maste } 137*0afa8e06SEd Maste } 138*0afa8e06SEd Maste } 139*0afa8e06SEd Maste if (cred == NULL) { 140*0afa8e06SEd Maste warnx("%s: credential not found", __func__); 141*0afa8e06SEd Maste goto out; 142*0afa8e06SEd Maste } 143*0afa8e06SEd Maste if (fido_cred_largeblob_key_ptr(cred) == NULL) { 144*0afa8e06SEd Maste warnx("%s: no associated blob key", __func__); 145*0afa8e06SEd Maste goto out; 146*0afa8e06SEd Maste } 147*0afa8e06SEd Maste key->len = fido_cred_largeblob_key_len(cred); 148*0afa8e06SEd Maste if ((key->ptr = malloc(key->len)) == NULL) { 149*0afa8e06SEd Maste warnx("%s: malloc", __func__); 150*0afa8e06SEd Maste goto out; 151*0afa8e06SEd Maste } 152*0afa8e06SEd Maste memcpy(key->ptr, fido_cred_largeblob_key_ptr(cred), key->len); 153*0afa8e06SEd Maste 154*0afa8e06SEd Maste ok = 0; 155*0afa8e06SEd Maste out: 156*0afa8e06SEd Maste fido_credman_rk_free(&rk); 157*0afa8e06SEd Maste 158*0afa8e06SEd Maste return ok; 159*0afa8e06SEd Maste } 160*0afa8e06SEd Maste 161*0afa8e06SEd Maste static int 162*0afa8e06SEd Maste load_key(const char *keyf, const char *cred_id64, const char *rp_id, 163*0afa8e06SEd Maste const char *path, fido_dev_t *dev, char **pin, struct blob *key) 164*0afa8e06SEd Maste { 165*0afa8e06SEd Maste struct blob cred_id; 166*0afa8e06SEd Maste FILE *fp; 167*0afa8e06SEd Maste int r; 168*0afa8e06SEd Maste 169*0afa8e06SEd Maste memset(&cred_id, 0, sizeof(cred_id)); 170*0afa8e06SEd Maste 171*0afa8e06SEd Maste if (keyf != NULL) { 172*0afa8e06SEd Maste if (rp_id != NULL || cred_id64 != NULL) 173*0afa8e06SEd Maste usage(); 174*0afa8e06SEd Maste fp = open_read(keyf); 175*0afa8e06SEd Maste if ((r = base64_read(fp, key)) < 0) 176*0afa8e06SEd Maste warnx("%s: base64_read %s", __func__, keyf); 177*0afa8e06SEd Maste fclose(fp); 178*0afa8e06SEd Maste return r; 179*0afa8e06SEd Maste } 180*0afa8e06SEd Maste if (rp_id == NULL) 181*0afa8e06SEd Maste usage(); 182*0afa8e06SEd Maste if (cred_id64 != NULL && base64_decode(cred_id64, (void *)&cred_id.ptr, 183*0afa8e06SEd Maste &cred_id.len) < 0) { 184*0afa8e06SEd Maste warnx("%s: base64_decode %s", __func__, cred_id64); 185*0afa8e06SEd Maste return -1; 186*0afa8e06SEd Maste } 187*0afa8e06SEd Maste r = lookup_key(path, dev, rp_id, &cred_id, pin, key); 188*0afa8e06SEd Maste free(cred_id.ptr); 189*0afa8e06SEd Maste 190*0afa8e06SEd Maste return r; 191*0afa8e06SEd Maste } 192*0afa8e06SEd Maste 193*0afa8e06SEd Maste int 194*0afa8e06SEd Maste blob_set(const char *path, const char *keyf, const char *rp_id, 195*0afa8e06SEd Maste const char *cred_id64, const char *blobf) 196*0afa8e06SEd Maste { 197*0afa8e06SEd Maste fido_dev_t *dev; 198*0afa8e06SEd Maste struct blob key, blob; 199*0afa8e06SEd Maste char *pin = NULL; 200*0afa8e06SEd Maste int r, ok = 1; 201*0afa8e06SEd Maste 202*0afa8e06SEd Maste dev = open_dev(path); 203*0afa8e06SEd Maste memset(&key, 0, sizeof(key)); 204*0afa8e06SEd Maste memset(&blob, 0, sizeof(blob)); 205*0afa8e06SEd Maste 206*0afa8e06SEd Maste if (read_file(blobf, &blob.ptr, &blob.len) < 0 || 207*0afa8e06SEd Maste load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0) 208*0afa8e06SEd Maste goto out; 209*0afa8e06SEd Maste if ((r = fido_dev_largeblob_set(dev, key.ptr, key.len, blob.ptr, 210*0afa8e06SEd Maste blob.len, pin)) != FIDO_OK && should_retry_with_pin(dev, r)) { 211*0afa8e06SEd Maste if ((pin = get_pin(path)) == NULL) 212*0afa8e06SEd Maste goto out; 213*0afa8e06SEd Maste r = fido_dev_largeblob_set(dev, key.ptr, key.len, blob.ptr, 214*0afa8e06SEd Maste blob.len, pin); 215*0afa8e06SEd Maste } 216*0afa8e06SEd Maste if (r != FIDO_OK) { 217*0afa8e06SEd Maste warnx("fido_dev_largeblob_set: %s", fido_strerr(r)); 218*0afa8e06SEd Maste goto out; 219*0afa8e06SEd Maste } 220*0afa8e06SEd Maste 221*0afa8e06SEd Maste ok = 0; /* success */ 222*0afa8e06SEd Maste out: 223*0afa8e06SEd Maste freezero(key.ptr, key.len); 224*0afa8e06SEd Maste freezero(blob.ptr, blob.len); 225*0afa8e06SEd Maste freezero(pin, PINBUF_LEN); 226*0afa8e06SEd Maste 227*0afa8e06SEd Maste fido_dev_close(dev); 228*0afa8e06SEd Maste fido_dev_free(&dev); 229*0afa8e06SEd Maste 230*0afa8e06SEd Maste exit(ok); 231*0afa8e06SEd Maste } 232*0afa8e06SEd Maste 233*0afa8e06SEd Maste int 234*0afa8e06SEd Maste blob_get(const char *path, const char *keyf, const char *rp_id, 235*0afa8e06SEd Maste const char *cred_id64, const char *blobf) 236*0afa8e06SEd Maste { 237*0afa8e06SEd Maste fido_dev_t *dev; 238*0afa8e06SEd Maste struct blob key, blob; 239*0afa8e06SEd Maste char *pin = NULL; 240*0afa8e06SEd Maste int r, ok = 1; 241*0afa8e06SEd Maste 242*0afa8e06SEd Maste dev = open_dev(path); 243*0afa8e06SEd Maste memset(&key, 0, sizeof(key)); 244*0afa8e06SEd Maste memset(&blob, 0, sizeof(blob)); 245*0afa8e06SEd Maste 246*0afa8e06SEd Maste if (load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0) 247*0afa8e06SEd Maste goto out; 248*0afa8e06SEd Maste if ((r = fido_dev_largeblob_get(dev, key.ptr, key.len, &blob.ptr, 249*0afa8e06SEd Maste &blob.len)) != FIDO_OK) { 250*0afa8e06SEd Maste warnx("fido_dev_largeblob_get: %s", fido_strerr(r)); 251*0afa8e06SEd Maste goto out; 252*0afa8e06SEd Maste } 253*0afa8e06SEd Maste if (write_file(blobf, blob.ptr, blob.len) < 0) 254*0afa8e06SEd Maste goto out; 255*0afa8e06SEd Maste 256*0afa8e06SEd Maste ok = 0; /* success */ 257*0afa8e06SEd Maste out: 258*0afa8e06SEd Maste freezero(key.ptr, key.len); 259*0afa8e06SEd Maste freezero(blob.ptr, blob.len); 260*0afa8e06SEd Maste freezero(pin, PINBUF_LEN); 261*0afa8e06SEd Maste 262*0afa8e06SEd Maste fido_dev_close(dev); 263*0afa8e06SEd Maste fido_dev_free(&dev); 264*0afa8e06SEd Maste 265*0afa8e06SEd Maste exit(ok); 266*0afa8e06SEd Maste } 267*0afa8e06SEd Maste 268*0afa8e06SEd Maste int 269*0afa8e06SEd Maste blob_delete(const char *path, const char *keyf, const char *rp_id, 270*0afa8e06SEd Maste const char *cred_id64) 271*0afa8e06SEd Maste { 272*0afa8e06SEd Maste fido_dev_t *dev; 273*0afa8e06SEd Maste struct blob key; 274*0afa8e06SEd Maste char *pin = NULL; 275*0afa8e06SEd Maste int r, ok = 1; 276*0afa8e06SEd Maste 277*0afa8e06SEd Maste dev = open_dev(path); 278*0afa8e06SEd Maste memset(&key, 0, sizeof(key)); 279*0afa8e06SEd Maste 280*0afa8e06SEd Maste if (load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0) 281*0afa8e06SEd Maste goto out; 282*0afa8e06SEd Maste if ((r = fido_dev_largeblob_remove(dev, key.ptr, key.len, 283*0afa8e06SEd Maste pin)) != FIDO_OK && should_retry_with_pin(dev, r)) { 284*0afa8e06SEd Maste if ((pin = get_pin(path)) == NULL) 285*0afa8e06SEd Maste goto out; 286*0afa8e06SEd Maste r = fido_dev_largeblob_remove(dev, key.ptr, key.len, pin); 287*0afa8e06SEd Maste } 288*0afa8e06SEd Maste if (r != FIDO_OK) { 289*0afa8e06SEd Maste warnx("fido_dev_largeblob_remove: %s", fido_strerr(r)); 290*0afa8e06SEd Maste goto out; 291*0afa8e06SEd Maste } 292*0afa8e06SEd Maste 293*0afa8e06SEd Maste ok = 0; /* success */ 294*0afa8e06SEd Maste out: 295*0afa8e06SEd Maste freezero(key.ptr, key.len); 296*0afa8e06SEd Maste freezero(pin, PINBUF_LEN); 297*0afa8e06SEd Maste 298*0afa8e06SEd Maste fido_dev_close(dev); 299*0afa8e06SEd Maste fido_dev_free(&dev); 300*0afa8e06SEd Maste 301*0afa8e06SEd Maste exit(ok); 302*0afa8e06SEd Maste } 303*0afa8e06SEd Maste 304*0afa8e06SEd Maste static int 305*0afa8e06SEd Maste decompress(const struct blob *plaintext, uint64_t origsiz) 306*0afa8e06SEd Maste { 307*0afa8e06SEd Maste struct blob inflated; 308*0afa8e06SEd Maste u_long ilen, plen; 309*0afa8e06SEd Maste int ok = -1; 310*0afa8e06SEd Maste 311*0afa8e06SEd Maste memset(&inflated, 0, sizeof(inflated)); 312*0afa8e06SEd Maste 313*0afa8e06SEd Maste if (plaintext->len > ULONG_MAX) 314*0afa8e06SEd Maste return -1; 315*0afa8e06SEd Maste if (origsiz > ULONG_MAX || origsiz > SIZE_MAX) 316*0afa8e06SEd Maste return -1; 317*0afa8e06SEd Maste plen = (u_long)plaintext->len; 318*0afa8e06SEd Maste ilen = (u_long)origsiz; 319*0afa8e06SEd Maste inflated.len = (size_t)origsiz; 320*0afa8e06SEd Maste if ((inflated.ptr = calloc(1, inflated.len)) == NULL) 321*0afa8e06SEd Maste return -1; 322*0afa8e06SEd Maste if (uncompress(inflated.ptr, &ilen, plaintext->ptr, plen) != Z_OK || 323*0afa8e06SEd Maste ilen > SIZE_MAX || (size_t)ilen != (size_t)origsiz) 324*0afa8e06SEd Maste goto out; 325*0afa8e06SEd Maste 326*0afa8e06SEd Maste ok = 0; /* success */ 327*0afa8e06SEd Maste out: 328*0afa8e06SEd Maste freezero(inflated.ptr, inflated.len); 329*0afa8e06SEd Maste 330*0afa8e06SEd Maste return ok; 331*0afa8e06SEd Maste } 332*0afa8e06SEd Maste 333*0afa8e06SEd Maste static int 334*0afa8e06SEd Maste decode(const struct blob *ciphertext, const struct blob *nonce, 335*0afa8e06SEd Maste uint64_t origsiz, const fido_cred_t *cred) 336*0afa8e06SEd Maste { 337*0afa8e06SEd Maste uint8_t aad[4 + sizeof(uint64_t)]; 338*0afa8e06SEd Maste EVP_CIPHER_CTX *ctx = NULL; 339*0afa8e06SEd Maste const EVP_CIPHER *cipher; 340*0afa8e06SEd Maste struct blob plaintext; 341*0afa8e06SEd Maste uint64_t tmp; 342*0afa8e06SEd Maste int ok = -1; 343*0afa8e06SEd Maste 344*0afa8e06SEd Maste memset(&plaintext, 0, sizeof(plaintext)); 345*0afa8e06SEd Maste 346*0afa8e06SEd Maste if (nonce->len != 12) 347*0afa8e06SEd Maste return -1; 348*0afa8e06SEd Maste if (cred == NULL || 349*0afa8e06SEd Maste fido_cred_largeblob_key_ptr(cred) == NULL || 350*0afa8e06SEd Maste fido_cred_largeblob_key_len(cred) != 32) 351*0afa8e06SEd Maste return -1; 352*0afa8e06SEd Maste if (ciphertext->len > UINT_MAX || 353*0afa8e06SEd Maste ciphertext->len > SIZE_MAX - 16 || 354*0afa8e06SEd Maste ciphertext->len < 16) 355*0afa8e06SEd Maste return -1; 356*0afa8e06SEd Maste plaintext.len = ciphertext->len - 16; 357*0afa8e06SEd Maste if ((plaintext.ptr = calloc(1, plaintext.len)) == NULL) 358*0afa8e06SEd Maste return -1; 359*0afa8e06SEd Maste if ((ctx = EVP_CIPHER_CTX_new()) == NULL || 360*0afa8e06SEd Maste (cipher = EVP_aes_256_gcm()) == NULL || 361*0afa8e06SEd Maste EVP_CipherInit(ctx, cipher, fido_cred_largeblob_key_ptr(cred), 362*0afa8e06SEd Maste nonce->ptr, 0) == 0) 363*0afa8e06SEd Maste goto out; 364*0afa8e06SEd Maste if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, 365*0afa8e06SEd Maste ciphertext->ptr + ciphertext->len - 16) == 0) 366*0afa8e06SEd Maste goto out; 367*0afa8e06SEd Maste aad[0] = 0x62; /* b */ 368*0afa8e06SEd Maste aad[1] = 0x6c; /* l */ 369*0afa8e06SEd Maste aad[2] = 0x6f; /* o */ 370*0afa8e06SEd Maste aad[3] = 0x62; /* b */ 371*0afa8e06SEd Maste tmp = htole64(origsiz); 372*0afa8e06SEd Maste memcpy(&aad[4], &tmp, sizeof(uint64_t)); 373*0afa8e06SEd Maste if (EVP_Cipher(ctx, NULL, aad, (u_int)sizeof(aad)) < 0 || 374*0afa8e06SEd Maste EVP_Cipher(ctx, plaintext.ptr, ciphertext->ptr, 375*0afa8e06SEd Maste (u_int)plaintext.len) < 0 || 376*0afa8e06SEd Maste EVP_Cipher(ctx, NULL, NULL, 0) < 0) 377*0afa8e06SEd Maste goto out; 378*0afa8e06SEd Maste if (decompress(&plaintext, origsiz) < 0) 379*0afa8e06SEd Maste goto out; 380*0afa8e06SEd Maste 381*0afa8e06SEd Maste ok = 0; 382*0afa8e06SEd Maste out: 383*0afa8e06SEd Maste freezero(plaintext.ptr, plaintext.len); 384*0afa8e06SEd Maste 385*0afa8e06SEd Maste if (ctx != NULL) 386*0afa8e06SEd Maste EVP_CIPHER_CTX_free(ctx); 387*0afa8e06SEd Maste 388*0afa8e06SEd Maste return ok; 389*0afa8e06SEd Maste } 390*0afa8e06SEd Maste 391*0afa8e06SEd Maste static const fido_cred_t * 392*0afa8e06SEd Maste try_rp(const fido_credman_rk_t *rk, const struct blob *ciphertext, 393*0afa8e06SEd Maste const struct blob *nonce, uint64_t origsiz) 394*0afa8e06SEd Maste { 395*0afa8e06SEd Maste const fido_cred_t *cred; 396*0afa8e06SEd Maste 397*0afa8e06SEd Maste for (size_t i = 0; i < fido_credman_rk_count(rk); i++) 398*0afa8e06SEd Maste if ((cred = fido_credman_rk(rk, i)) != NULL && 399*0afa8e06SEd Maste decode(ciphertext, nonce, origsiz, cred) == 0) 400*0afa8e06SEd Maste return cred; 401*0afa8e06SEd Maste 402*0afa8e06SEd Maste return NULL; 403*0afa8e06SEd Maste } 404*0afa8e06SEd Maste 405*0afa8e06SEd Maste static int 406*0afa8e06SEd Maste decode_cbor_blob(struct blob *out, const cbor_item_t *item) 407*0afa8e06SEd Maste { 408*0afa8e06SEd Maste if (out->ptr != NULL || 409*0afa8e06SEd Maste cbor_isa_bytestring(item) == false || 410*0afa8e06SEd Maste cbor_bytestring_is_definite(item) == false) 411*0afa8e06SEd Maste return -1; 412*0afa8e06SEd Maste out->len = cbor_bytestring_length(item); 413*0afa8e06SEd Maste if ((out->ptr = malloc(out->len)) == NULL) 414*0afa8e06SEd Maste return -1; 415*0afa8e06SEd Maste memcpy(out->ptr, cbor_bytestring_handle(item), out->len); 416*0afa8e06SEd Maste 417*0afa8e06SEd Maste return 0; 418*0afa8e06SEd Maste } 419*0afa8e06SEd Maste 420*0afa8e06SEd Maste static int 421*0afa8e06SEd Maste decode_blob_entry(const cbor_item_t *item, struct blob *ciphertext, 422*0afa8e06SEd Maste struct blob *nonce, uint64_t *origsiz) 423*0afa8e06SEd Maste { 424*0afa8e06SEd Maste struct cbor_pair *v; 425*0afa8e06SEd Maste 426*0afa8e06SEd Maste if (item == NULL) 427*0afa8e06SEd Maste return -1; 428*0afa8e06SEd Maste if (cbor_isa_map(item) == false || 429*0afa8e06SEd Maste cbor_map_is_definite(item) == false || 430*0afa8e06SEd Maste (v = cbor_map_handle(item)) == NULL) 431*0afa8e06SEd Maste return -1; 432*0afa8e06SEd Maste if (cbor_map_size(item) > UINT8_MAX) 433*0afa8e06SEd Maste return -1; 434*0afa8e06SEd Maste 435*0afa8e06SEd Maste for (size_t i = 0; i < cbor_map_size(item); i++) { 436*0afa8e06SEd Maste if (cbor_isa_uint(v[i].key) == false || 437*0afa8e06SEd Maste cbor_int_get_width(v[i].key) != CBOR_INT_8) 438*0afa8e06SEd Maste continue; /* ignore */ 439*0afa8e06SEd Maste switch (cbor_get_uint8(v[i].key)) { 440*0afa8e06SEd Maste case 1: /* ciphertext */ 441*0afa8e06SEd Maste if (decode_cbor_blob(ciphertext, v[i].value) < 0) 442*0afa8e06SEd Maste return -1; 443*0afa8e06SEd Maste break; 444*0afa8e06SEd Maste case 2: /* nonce */ 445*0afa8e06SEd Maste if (decode_cbor_blob(nonce, v[i].value) < 0) 446*0afa8e06SEd Maste return -1; 447*0afa8e06SEd Maste break; 448*0afa8e06SEd Maste case 3: /* origSize */ 449*0afa8e06SEd Maste if (*origsiz != 0 || 450*0afa8e06SEd Maste cbor_isa_uint(v[i].value) == false || 451*0afa8e06SEd Maste (*origsiz = cbor_get_int(v[i].value)) > SIZE_MAX) 452*0afa8e06SEd Maste return -1; 453*0afa8e06SEd Maste } 454*0afa8e06SEd Maste } 455*0afa8e06SEd Maste if (ciphertext->ptr == NULL || nonce->ptr == NULL || *origsiz == 0) 456*0afa8e06SEd Maste return -1; 457*0afa8e06SEd Maste 458*0afa8e06SEd Maste return 0; 459*0afa8e06SEd Maste } 460*0afa8e06SEd Maste 461*0afa8e06SEd Maste static void 462*0afa8e06SEd Maste print_blob_entry(size_t idx, const cbor_item_t *item, const struct rkmap *map) 463*0afa8e06SEd Maste { 464*0afa8e06SEd Maste struct blob ciphertext, nonce; 465*0afa8e06SEd Maste const fido_cred_t *cred = NULL; 466*0afa8e06SEd Maste const char *rp_id = NULL; 467*0afa8e06SEd Maste char *cred_id = NULL; 468*0afa8e06SEd Maste uint64_t origsiz = 0; 469*0afa8e06SEd Maste 470*0afa8e06SEd Maste memset(&ciphertext, 0, sizeof(ciphertext)); 471*0afa8e06SEd Maste memset(&nonce, 0, sizeof(nonce)); 472*0afa8e06SEd Maste 473*0afa8e06SEd Maste if (decode_blob_entry(item, &ciphertext, &nonce, &origsiz) < 0) { 474*0afa8e06SEd Maste printf("%02zu: <skipped: bad cbor>\n", idx); 475*0afa8e06SEd Maste goto out; 476*0afa8e06SEd Maste } 477*0afa8e06SEd Maste for (size_t i = 0; i < fido_credman_rp_count(map->rp); i++) { 478*0afa8e06SEd Maste if ((cred = try_rp(map->rk[i], &ciphertext, &nonce, 479*0afa8e06SEd Maste origsiz)) != NULL) { 480*0afa8e06SEd Maste rp_id = fido_credman_rp_id(map->rp, i); 481*0afa8e06SEd Maste break; 482*0afa8e06SEd Maste } 483*0afa8e06SEd Maste } 484*0afa8e06SEd Maste if (cred == NULL) { 485*0afa8e06SEd Maste if ((cred_id = strdup("<unknown>")) == NULL) { 486*0afa8e06SEd Maste printf("%02zu: <skipped: strdup failed>\n", idx); 487*0afa8e06SEd Maste goto out; 488*0afa8e06SEd Maste } 489*0afa8e06SEd Maste } else { 490*0afa8e06SEd Maste if (base64_encode(fido_cred_id_ptr(cred), 491*0afa8e06SEd Maste fido_cred_id_len(cred), &cred_id) < 0) { 492*0afa8e06SEd Maste printf("%02zu: <skipped: base64_encode failed>\n", idx); 493*0afa8e06SEd Maste goto out; 494*0afa8e06SEd Maste } 495*0afa8e06SEd Maste } 496*0afa8e06SEd Maste if (rp_id == NULL) 497*0afa8e06SEd Maste rp_id = "<unknown>"; 498*0afa8e06SEd Maste 499*0afa8e06SEd Maste printf("%02zu: %4zu %4zu %s %s\n", idx, ciphertext.len, 500*0afa8e06SEd Maste (size_t)origsiz, cred_id, rp_id); 501*0afa8e06SEd Maste out: 502*0afa8e06SEd Maste free(ciphertext.ptr); 503*0afa8e06SEd Maste free(nonce.ptr); 504*0afa8e06SEd Maste free(cred_id); 505*0afa8e06SEd Maste } 506*0afa8e06SEd Maste 507*0afa8e06SEd Maste static cbor_item_t * 508*0afa8e06SEd Maste get_cbor_array(fido_dev_t *dev) 509*0afa8e06SEd Maste { 510*0afa8e06SEd Maste struct cbor_load_result cbor_result; 511*0afa8e06SEd Maste cbor_item_t *item = NULL; 512*0afa8e06SEd Maste u_char *cbor_ptr = NULL; 513*0afa8e06SEd Maste size_t cbor_len; 514*0afa8e06SEd Maste int r, ok = -1; 515*0afa8e06SEd Maste 516*0afa8e06SEd Maste if ((r = fido_dev_largeblob_get_array(dev, &cbor_ptr, 517*0afa8e06SEd Maste &cbor_len)) != FIDO_OK) { 518*0afa8e06SEd Maste warnx("%s: fido_dev_largeblob_get_array: %s", __func__, 519*0afa8e06SEd Maste fido_strerr(r)); 520*0afa8e06SEd Maste goto out; 521*0afa8e06SEd Maste } 522*0afa8e06SEd Maste if ((item = cbor_load(cbor_ptr, cbor_len, &cbor_result)) == NULL) { 523*0afa8e06SEd Maste warnx("%s: cbor_load", __func__); 524*0afa8e06SEd Maste goto out; 525*0afa8e06SEd Maste } 526*0afa8e06SEd Maste if (cbor_result.read != cbor_len) { 527*0afa8e06SEd Maste warnx("%s: cbor_result.read (%zu) != cbor_len (%zu)", __func__, 528*0afa8e06SEd Maste cbor_result.read, cbor_len); 529*0afa8e06SEd Maste /* continue */ 530*0afa8e06SEd Maste } 531*0afa8e06SEd Maste if (cbor_isa_array(item) == false || 532*0afa8e06SEd Maste cbor_array_is_definite(item) == false) { 533*0afa8e06SEd Maste warnx("%s: cbor type", __func__); 534*0afa8e06SEd Maste goto out; 535*0afa8e06SEd Maste } 536*0afa8e06SEd Maste if (cbor_array_size(item) > UINT8_MAX) { 537*0afa8e06SEd Maste warnx("%s: cbor_array_size > UINT8_MAX", __func__); 538*0afa8e06SEd Maste goto out; 539*0afa8e06SEd Maste } 540*0afa8e06SEd Maste if (cbor_array_size(item) == 0) { 541*0afa8e06SEd Maste ok = 0; /* nothing to do */ 542*0afa8e06SEd Maste goto out; 543*0afa8e06SEd Maste } 544*0afa8e06SEd Maste 545*0afa8e06SEd Maste printf("total map size: %zu byte%s\n", cbor_len, plural(cbor_len)); 546*0afa8e06SEd Maste 547*0afa8e06SEd Maste ok = 0; 548*0afa8e06SEd Maste out: 549*0afa8e06SEd Maste if (ok < 0 && item != NULL) { 550*0afa8e06SEd Maste cbor_decref(&item); 551*0afa8e06SEd Maste item = NULL; 552*0afa8e06SEd Maste } 553*0afa8e06SEd Maste free(cbor_ptr); 554*0afa8e06SEd Maste 555*0afa8e06SEd Maste return item; 556*0afa8e06SEd Maste } 557*0afa8e06SEd Maste 558*0afa8e06SEd Maste int 559*0afa8e06SEd Maste blob_list(const char *path) 560*0afa8e06SEd Maste { 561*0afa8e06SEd Maste struct rkmap map; 562*0afa8e06SEd Maste fido_dev_t *dev = NULL; 563*0afa8e06SEd Maste cbor_item_t *item = NULL, **v; 564*0afa8e06SEd Maste int ok = 1; 565*0afa8e06SEd Maste 566*0afa8e06SEd Maste memset(&map, 0, sizeof(map)); 567*0afa8e06SEd Maste dev = open_dev(path); 568*0afa8e06SEd Maste if (map_known_rps(dev, path, &map) < 0 || 569*0afa8e06SEd Maste (item = get_cbor_array(dev)) == NULL) 570*0afa8e06SEd Maste goto out; 571*0afa8e06SEd Maste if (cbor_array_size(item) == 0) { 572*0afa8e06SEd Maste ok = 0; /* nothing to do */ 573*0afa8e06SEd Maste goto out; 574*0afa8e06SEd Maste } 575*0afa8e06SEd Maste if ((v = cbor_array_handle(item)) == NULL) { 576*0afa8e06SEd Maste warnx("%s: cbor_array_handle", __func__); 577*0afa8e06SEd Maste goto out; 578*0afa8e06SEd Maste } 579*0afa8e06SEd Maste for (size_t i = 0; i < cbor_array_size(item); i++) 580*0afa8e06SEd Maste print_blob_entry(i, v[i], &map); 581*0afa8e06SEd Maste 582*0afa8e06SEd Maste ok = 0; /* success */ 583*0afa8e06SEd Maste out: 584*0afa8e06SEd Maste free_rkmap(&map); 585*0afa8e06SEd Maste 586*0afa8e06SEd Maste if (item != NULL) 587*0afa8e06SEd Maste cbor_decref(&item); 588*0afa8e06SEd Maste 589*0afa8e06SEd Maste fido_dev_close(dev); 590*0afa8e06SEd Maste fido_dev_free(&dev); 591*0afa8e06SEd Maste 592*0afa8e06SEd Maste exit(ok); 593*0afa8e06SEd Maste } 594