10afa8e06SEd Maste /*
2*2ccfa855SEd Maste * Copyright (c) 2020-2022 Yubico AB. All rights reserved.
30afa8e06SEd Maste * Use of this source code is governed by a BSD-style
40afa8e06SEd Maste * license that can be found in the LICENSE file.
5*2ccfa855SEd Maste * SPDX-License-Identifier: BSD-2-Clause
60afa8e06SEd Maste */
70afa8e06SEd Maste
80afa8e06SEd Maste #include <sys/types.h>
90afa8e06SEd Maste #include <sys/stat.h>
100afa8e06SEd Maste
110afa8e06SEd Maste #include <fido.h>
120afa8e06SEd Maste #include <fido/credman.h>
130afa8e06SEd Maste
140afa8e06SEd Maste #include <cbor.h>
150afa8e06SEd Maste #include <fcntl.h>
160afa8e06SEd Maste #include <limits.h>
170afa8e06SEd Maste #include <stdio.h>
180afa8e06SEd Maste #include <stdlib.h>
190afa8e06SEd Maste #include <string.h>
200afa8e06SEd Maste #ifdef HAVE_UNISTD_H
210afa8e06SEd Maste #include <unistd.h>
220afa8e06SEd Maste #endif
230afa8e06SEd Maste #include <zlib.h>
240afa8e06SEd Maste
250afa8e06SEd Maste #include "../openbsd-compat/openbsd-compat.h"
260afa8e06SEd Maste #include "extern.h"
270afa8e06SEd Maste
28*2ccfa855SEd Maste #define BOUND (1024UL * 1024UL)
29*2ccfa855SEd Maste
300afa8e06SEd Maste struct rkmap {
310afa8e06SEd Maste fido_credman_rp_t *rp; /* known rps */
320afa8e06SEd Maste fido_credman_rk_t **rk; /* rk per rp */
330afa8e06SEd Maste };
340afa8e06SEd Maste
350afa8e06SEd Maste static void
free_rkmap(struct rkmap * map)360afa8e06SEd Maste free_rkmap(struct rkmap *map)
370afa8e06SEd Maste {
380afa8e06SEd Maste if (map->rp != NULL) {
390afa8e06SEd Maste for (size_t i = 0; i < fido_credman_rp_count(map->rp); i++)
400afa8e06SEd Maste fido_credman_rk_free(&map->rk[i]);
410afa8e06SEd Maste fido_credman_rp_free(&map->rp);
420afa8e06SEd Maste }
430afa8e06SEd Maste free(map->rk);
440afa8e06SEd Maste }
450afa8e06SEd Maste
460afa8e06SEd Maste static int
map_known_rps(fido_dev_t * dev,const char * path,struct rkmap * map)470afa8e06SEd Maste map_known_rps(fido_dev_t *dev, const char *path, struct rkmap *map)
480afa8e06SEd Maste {
490afa8e06SEd Maste const char *rp_id;
500afa8e06SEd Maste char *pin = NULL;
510afa8e06SEd Maste size_t n;
520afa8e06SEd Maste int r, ok = -1;
530afa8e06SEd Maste
540afa8e06SEd Maste if ((map->rp = fido_credman_rp_new()) == NULL) {
550afa8e06SEd Maste warnx("%s: fido_credman_rp_new", __func__);
560afa8e06SEd Maste goto out;
570afa8e06SEd Maste }
580afa8e06SEd Maste if ((pin = get_pin(path)) == NULL)
590afa8e06SEd Maste goto out;
600afa8e06SEd Maste if ((r = fido_credman_get_dev_rp(dev, map->rp, pin)) != FIDO_OK) {
610afa8e06SEd Maste warnx("fido_credman_get_dev_rp: %s", fido_strerr(r));
620afa8e06SEd Maste goto out;
630afa8e06SEd Maste }
640afa8e06SEd Maste if ((n = fido_credman_rp_count(map->rp)) > UINT8_MAX) {
650afa8e06SEd Maste warnx("%s: fido_credman_rp_count > UINT8_MAX", __func__);
660afa8e06SEd Maste goto out;
670afa8e06SEd Maste }
680afa8e06SEd Maste if ((map->rk = calloc(n, sizeof(*map->rk))) == NULL) {
690afa8e06SEd Maste warnx("%s: calloc", __func__);
700afa8e06SEd Maste goto out;
710afa8e06SEd Maste }
720afa8e06SEd Maste for (size_t i = 0; i < n; i++) {
730afa8e06SEd Maste if ((rp_id = fido_credman_rp_id(map->rp, i)) == NULL) {
740afa8e06SEd Maste warnx("%s: fido_credman_rp_id %zu", __func__, i);
750afa8e06SEd Maste goto out;
760afa8e06SEd Maste }
770afa8e06SEd Maste if ((map->rk[i] = fido_credman_rk_new()) == NULL) {
780afa8e06SEd Maste warnx("%s: fido_credman_rk_new", __func__);
790afa8e06SEd Maste goto out;
800afa8e06SEd Maste }
810afa8e06SEd Maste if ((r = fido_credman_get_dev_rk(dev, rp_id, map->rk[i],
820afa8e06SEd Maste pin)) != FIDO_OK) {
830afa8e06SEd Maste warnx("%s: fido_credman_get_dev_rk %s: %s", __func__,
840afa8e06SEd Maste rp_id, fido_strerr(r));
850afa8e06SEd Maste goto out;
860afa8e06SEd Maste }
870afa8e06SEd Maste }
880afa8e06SEd Maste
890afa8e06SEd Maste ok = 0;
900afa8e06SEd Maste out:
910afa8e06SEd Maste freezero(pin, PINBUF_LEN);
920afa8e06SEd Maste
930afa8e06SEd Maste return ok;
940afa8e06SEd Maste }
950afa8e06SEd Maste
960afa8e06SEd Maste static int
lookup_key(const char * path,fido_dev_t * dev,const char * rp_id,const struct blob * cred_id,char ** pin,struct blob * key)970afa8e06SEd Maste lookup_key(const char *path, fido_dev_t *dev, const char *rp_id,
980afa8e06SEd Maste const struct blob *cred_id, char **pin, struct blob *key)
990afa8e06SEd Maste {
1000afa8e06SEd Maste fido_credman_rk_t *rk = NULL;
1010afa8e06SEd Maste const fido_cred_t *cred = NULL;
1020afa8e06SEd Maste size_t i, n;
1030afa8e06SEd Maste int r, ok = -1;
1040afa8e06SEd Maste
1050afa8e06SEd Maste if ((rk = fido_credman_rk_new()) == NULL) {
1060afa8e06SEd Maste warnx("%s: fido_credman_rk_new", __func__);
1070afa8e06SEd Maste goto out;
1080afa8e06SEd Maste }
1090afa8e06SEd Maste if ((r = fido_credman_get_dev_rk(dev, rp_id, rk, *pin)) != FIDO_OK &&
1100afa8e06SEd Maste *pin == NULL && should_retry_with_pin(dev, r)) {
1110afa8e06SEd Maste if ((*pin = get_pin(path)) == NULL)
1120afa8e06SEd Maste goto out;
1130afa8e06SEd Maste r = fido_credman_get_dev_rk(dev, rp_id, rk, *pin);
1140afa8e06SEd Maste }
1150afa8e06SEd Maste if (r != FIDO_OK) {
1160afa8e06SEd Maste warnx("%s: fido_credman_get_dev_rk: %s", __func__,
1170afa8e06SEd Maste fido_strerr(r));
1180afa8e06SEd Maste goto out;
1190afa8e06SEd Maste }
1200afa8e06SEd Maste if ((n = fido_credman_rk_count(rk)) == 0) {
1210afa8e06SEd Maste warnx("%s: rp id not found", __func__);
1220afa8e06SEd Maste goto out;
1230afa8e06SEd Maste }
1240afa8e06SEd Maste if (n == 1 && cred_id->len == 0) {
1250afa8e06SEd Maste /* use the credential we found */
1260afa8e06SEd Maste cred = fido_credman_rk(rk, 0);
1270afa8e06SEd Maste } else {
1280afa8e06SEd Maste if (cred_id->len == 0) {
1290afa8e06SEd Maste warnx("%s: multiple credentials found", __func__);
1300afa8e06SEd Maste goto out;
1310afa8e06SEd Maste }
1320afa8e06SEd Maste for (i = 0; i < n; i++) {
1330afa8e06SEd Maste const fido_cred_t *x = fido_credman_rk(rk, i);
1340afa8e06SEd Maste if (fido_cred_id_len(x) <= cred_id->len &&
1350afa8e06SEd Maste !memcmp(fido_cred_id_ptr(x), cred_id->ptr,
1360afa8e06SEd Maste fido_cred_id_len(x))) {
1370afa8e06SEd Maste cred = x;
1380afa8e06SEd Maste break;
1390afa8e06SEd Maste }
1400afa8e06SEd Maste }
1410afa8e06SEd Maste }
1420afa8e06SEd Maste if (cred == NULL) {
1430afa8e06SEd Maste warnx("%s: credential not found", __func__);
1440afa8e06SEd Maste goto out;
1450afa8e06SEd Maste }
1460afa8e06SEd Maste if (fido_cred_largeblob_key_ptr(cred) == NULL) {
1470afa8e06SEd Maste warnx("%s: no associated blob key", __func__);
1480afa8e06SEd Maste goto out;
1490afa8e06SEd Maste }
1500afa8e06SEd Maste key->len = fido_cred_largeblob_key_len(cred);
1510afa8e06SEd Maste if ((key->ptr = malloc(key->len)) == NULL) {
1520afa8e06SEd Maste warnx("%s: malloc", __func__);
1530afa8e06SEd Maste goto out;
1540afa8e06SEd Maste }
1550afa8e06SEd Maste memcpy(key->ptr, fido_cred_largeblob_key_ptr(cred), key->len);
1560afa8e06SEd Maste
1570afa8e06SEd Maste ok = 0;
1580afa8e06SEd Maste out:
1590afa8e06SEd Maste fido_credman_rk_free(&rk);
1600afa8e06SEd Maste
1610afa8e06SEd Maste return ok;
1620afa8e06SEd Maste }
1630afa8e06SEd Maste
1640afa8e06SEd Maste static int
load_key(const char * keyf,const char * cred_id64,const char * rp_id,const char * path,fido_dev_t * dev,char ** pin,struct blob * key)1650afa8e06SEd Maste load_key(const char *keyf, const char *cred_id64, const char *rp_id,
1660afa8e06SEd Maste const char *path, fido_dev_t *dev, char **pin, struct blob *key)
1670afa8e06SEd Maste {
1680afa8e06SEd Maste struct blob cred_id;
1690afa8e06SEd Maste FILE *fp;
1700afa8e06SEd Maste int r;
1710afa8e06SEd Maste
1720afa8e06SEd Maste memset(&cred_id, 0, sizeof(cred_id));
1730afa8e06SEd Maste
1740afa8e06SEd Maste if (keyf != NULL) {
1750afa8e06SEd Maste if (rp_id != NULL || cred_id64 != NULL)
1760afa8e06SEd Maste usage();
1770afa8e06SEd Maste fp = open_read(keyf);
1780afa8e06SEd Maste if ((r = base64_read(fp, key)) < 0)
1790afa8e06SEd Maste warnx("%s: base64_read %s", __func__, keyf);
1800afa8e06SEd Maste fclose(fp);
1810afa8e06SEd Maste return r;
1820afa8e06SEd Maste }
1830afa8e06SEd Maste if (rp_id == NULL)
1840afa8e06SEd Maste usage();
1850afa8e06SEd Maste if (cred_id64 != NULL && base64_decode(cred_id64, (void *)&cred_id.ptr,
1860afa8e06SEd Maste &cred_id.len) < 0) {
1870afa8e06SEd Maste warnx("%s: base64_decode %s", __func__, cred_id64);
1880afa8e06SEd Maste return -1;
1890afa8e06SEd Maste }
1900afa8e06SEd Maste r = lookup_key(path, dev, rp_id, &cred_id, pin, key);
1910afa8e06SEd Maste free(cred_id.ptr);
1920afa8e06SEd Maste
1930afa8e06SEd Maste return r;
1940afa8e06SEd Maste }
1950afa8e06SEd Maste
1960afa8e06SEd Maste int
blob_set(const char * path,const char * keyf,const char * rp_id,const char * cred_id64,const char * blobf)1970afa8e06SEd Maste blob_set(const char *path, const char *keyf, const char *rp_id,
1980afa8e06SEd Maste const char *cred_id64, const char *blobf)
1990afa8e06SEd Maste {
2000afa8e06SEd Maste fido_dev_t *dev;
2010afa8e06SEd Maste struct blob key, blob;
2020afa8e06SEd Maste char *pin = NULL;
2030afa8e06SEd Maste int r, ok = 1;
2040afa8e06SEd Maste
2050afa8e06SEd Maste dev = open_dev(path);
2060afa8e06SEd Maste memset(&key, 0, sizeof(key));
2070afa8e06SEd Maste memset(&blob, 0, sizeof(blob));
2080afa8e06SEd Maste
2090afa8e06SEd Maste if (read_file(blobf, &blob.ptr, &blob.len) < 0 ||
2100afa8e06SEd Maste load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0)
2110afa8e06SEd Maste goto out;
2120afa8e06SEd Maste if ((r = fido_dev_largeblob_set(dev, key.ptr, key.len, blob.ptr,
2130afa8e06SEd Maste blob.len, pin)) != FIDO_OK && should_retry_with_pin(dev, r)) {
2140afa8e06SEd Maste if ((pin = get_pin(path)) == NULL)
2150afa8e06SEd Maste goto out;
2160afa8e06SEd Maste r = fido_dev_largeblob_set(dev, key.ptr, key.len, blob.ptr,
2170afa8e06SEd Maste blob.len, pin);
2180afa8e06SEd Maste }
2190afa8e06SEd Maste if (r != FIDO_OK) {
2200afa8e06SEd Maste warnx("fido_dev_largeblob_set: %s", fido_strerr(r));
2210afa8e06SEd Maste goto out;
2220afa8e06SEd Maste }
2230afa8e06SEd Maste
2240afa8e06SEd Maste ok = 0; /* success */
2250afa8e06SEd Maste out:
2260afa8e06SEd Maste freezero(key.ptr, key.len);
2270afa8e06SEd Maste freezero(blob.ptr, blob.len);
2280afa8e06SEd Maste freezero(pin, PINBUF_LEN);
2290afa8e06SEd Maste
2300afa8e06SEd Maste fido_dev_close(dev);
2310afa8e06SEd Maste fido_dev_free(&dev);
2320afa8e06SEd Maste
2330afa8e06SEd Maste exit(ok);
2340afa8e06SEd Maste }
2350afa8e06SEd Maste
2360afa8e06SEd Maste int
blob_get(const char * path,const char * keyf,const char * rp_id,const char * cred_id64,const char * blobf)2370afa8e06SEd Maste blob_get(const char *path, const char *keyf, const char *rp_id,
2380afa8e06SEd Maste const char *cred_id64, const char *blobf)
2390afa8e06SEd Maste {
2400afa8e06SEd Maste fido_dev_t *dev;
2410afa8e06SEd Maste struct blob key, blob;
2420afa8e06SEd Maste char *pin = NULL;
2430afa8e06SEd Maste int r, ok = 1;
2440afa8e06SEd Maste
2450afa8e06SEd Maste dev = open_dev(path);
2460afa8e06SEd Maste memset(&key, 0, sizeof(key));
2470afa8e06SEd Maste memset(&blob, 0, sizeof(blob));
2480afa8e06SEd Maste
2490afa8e06SEd Maste if (load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0)
2500afa8e06SEd Maste goto out;
2510afa8e06SEd Maste if ((r = fido_dev_largeblob_get(dev, key.ptr, key.len, &blob.ptr,
2520afa8e06SEd Maste &blob.len)) != FIDO_OK) {
2530afa8e06SEd Maste warnx("fido_dev_largeblob_get: %s", fido_strerr(r));
2540afa8e06SEd Maste goto out;
2550afa8e06SEd Maste }
2560afa8e06SEd Maste if (write_file(blobf, blob.ptr, blob.len) < 0)
2570afa8e06SEd Maste goto out;
2580afa8e06SEd Maste
2590afa8e06SEd Maste ok = 0; /* success */
2600afa8e06SEd Maste out:
2610afa8e06SEd Maste freezero(key.ptr, key.len);
2620afa8e06SEd Maste freezero(blob.ptr, blob.len);
2630afa8e06SEd Maste freezero(pin, PINBUF_LEN);
2640afa8e06SEd Maste
2650afa8e06SEd Maste fido_dev_close(dev);
2660afa8e06SEd Maste fido_dev_free(&dev);
2670afa8e06SEd Maste
2680afa8e06SEd Maste exit(ok);
2690afa8e06SEd Maste }
2700afa8e06SEd Maste
2710afa8e06SEd Maste int
blob_delete(const char * path,const char * keyf,const char * rp_id,const char * cred_id64)2720afa8e06SEd Maste blob_delete(const char *path, const char *keyf, const char *rp_id,
2730afa8e06SEd Maste const char *cred_id64)
2740afa8e06SEd Maste {
2750afa8e06SEd Maste fido_dev_t *dev;
2760afa8e06SEd Maste struct blob key;
2770afa8e06SEd Maste char *pin = NULL;
2780afa8e06SEd Maste int r, ok = 1;
2790afa8e06SEd Maste
2800afa8e06SEd Maste dev = open_dev(path);
2810afa8e06SEd Maste memset(&key, 0, sizeof(key));
2820afa8e06SEd Maste
2830afa8e06SEd Maste if (load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0)
2840afa8e06SEd Maste goto out;
2850afa8e06SEd Maste if ((r = fido_dev_largeblob_remove(dev, key.ptr, key.len,
2860afa8e06SEd Maste pin)) != FIDO_OK && should_retry_with_pin(dev, r)) {
2870afa8e06SEd Maste if ((pin = get_pin(path)) == NULL)
2880afa8e06SEd Maste goto out;
2890afa8e06SEd Maste r = fido_dev_largeblob_remove(dev, key.ptr, key.len, pin);
2900afa8e06SEd Maste }
2910afa8e06SEd Maste if (r != FIDO_OK) {
2920afa8e06SEd Maste warnx("fido_dev_largeblob_remove: %s", fido_strerr(r));
2930afa8e06SEd Maste goto out;
2940afa8e06SEd Maste }
2950afa8e06SEd Maste
2960afa8e06SEd Maste ok = 0; /* success */
2970afa8e06SEd Maste out:
2980afa8e06SEd Maste freezero(key.ptr, key.len);
2990afa8e06SEd Maste freezero(pin, PINBUF_LEN);
3000afa8e06SEd Maste
3010afa8e06SEd Maste fido_dev_close(dev);
3020afa8e06SEd Maste fido_dev_free(&dev);
3030afa8e06SEd Maste
3040afa8e06SEd Maste exit(ok);
3050afa8e06SEd Maste }
3060afa8e06SEd Maste
3070afa8e06SEd Maste static int
try_decompress(const struct blob * in,uint64_t origsiz,int wbits)308*2ccfa855SEd Maste try_decompress(const struct blob *in, uint64_t origsiz, int wbits)
3090afa8e06SEd Maste {
310*2ccfa855SEd Maste struct blob out;
311*2ccfa855SEd Maste z_stream zs;
312*2ccfa855SEd Maste u_int ilen, olen;
3130afa8e06SEd Maste int ok = -1;
3140afa8e06SEd Maste
315*2ccfa855SEd Maste memset(&zs, 0, sizeof(zs));
316*2ccfa855SEd Maste memset(&out, 0, sizeof(out));
3170afa8e06SEd Maste
318*2ccfa855SEd Maste if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND)
3190afa8e06SEd Maste return -1;
320*2ccfa855SEd Maste if (origsiz > SIZE_MAX || origsiz > UINT_MAX ||
321*2ccfa855SEd Maste (olen = (u_int)origsiz) > BOUND)
3220afa8e06SEd Maste return -1;
323*2ccfa855SEd Maste if (inflateInit2(&zs, wbits) != Z_OK)
3240afa8e06SEd Maste return -1;
3250afa8e06SEd Maste
326*2ccfa855SEd Maste if ((out.ptr = calloc(1, olen)) == NULL)
327*2ccfa855SEd Maste goto fail;
328*2ccfa855SEd Maste
329*2ccfa855SEd Maste out.len = olen;
330*2ccfa855SEd Maste zs.next_in = in->ptr;
331*2ccfa855SEd Maste zs.avail_in = ilen;
332*2ccfa855SEd Maste zs.next_out = out.ptr;
333*2ccfa855SEd Maste zs.avail_out = olen;
334*2ccfa855SEd Maste
335*2ccfa855SEd Maste if (inflate(&zs, Z_FINISH) != Z_STREAM_END)
336*2ccfa855SEd Maste goto fail;
337*2ccfa855SEd Maste if (zs.avail_out != 0)
338*2ccfa855SEd Maste goto fail;
339*2ccfa855SEd Maste
340*2ccfa855SEd Maste ok = 0;
341*2ccfa855SEd Maste fail:
342*2ccfa855SEd Maste if (inflateEnd(&zs) != Z_OK)
343*2ccfa855SEd Maste ok = -1;
344*2ccfa855SEd Maste
345*2ccfa855SEd Maste freezero(out.ptr, out.len);
3460afa8e06SEd Maste
3470afa8e06SEd Maste return ok;
3480afa8e06SEd Maste }
3490afa8e06SEd Maste
3500afa8e06SEd Maste static int
decompress(const struct blob * plaintext,uint64_t origsiz)351*2ccfa855SEd Maste decompress(const struct blob *plaintext, uint64_t origsiz)
352*2ccfa855SEd Maste {
353*2ccfa855SEd Maste if (try_decompress(plaintext, origsiz, MAX_WBITS) == 0) /* rfc1950 */
354*2ccfa855SEd Maste return 0;
355*2ccfa855SEd Maste return try_decompress(plaintext, origsiz, -MAX_WBITS); /* rfc1951 */
356*2ccfa855SEd Maste }
357*2ccfa855SEd Maste
358*2ccfa855SEd Maste static int
decode(const struct blob * ciphertext,const struct blob * nonce,uint64_t origsiz,const fido_cred_t * cred)3590afa8e06SEd Maste decode(const struct blob *ciphertext, const struct blob *nonce,
3600afa8e06SEd Maste uint64_t origsiz, const fido_cred_t *cred)
3610afa8e06SEd Maste {
3620afa8e06SEd Maste uint8_t aad[4 + sizeof(uint64_t)];
3630afa8e06SEd Maste EVP_CIPHER_CTX *ctx = NULL;
3640afa8e06SEd Maste const EVP_CIPHER *cipher;
3650afa8e06SEd Maste struct blob plaintext;
3660afa8e06SEd Maste uint64_t tmp;
3670afa8e06SEd Maste int ok = -1;
3680afa8e06SEd Maste
3690afa8e06SEd Maste memset(&plaintext, 0, sizeof(plaintext));
3700afa8e06SEd Maste
3710afa8e06SEd Maste if (nonce->len != 12)
3720afa8e06SEd Maste return -1;
3730afa8e06SEd Maste if (cred == NULL ||
3740afa8e06SEd Maste fido_cred_largeblob_key_ptr(cred) == NULL ||
3750afa8e06SEd Maste fido_cred_largeblob_key_len(cred) != 32)
3760afa8e06SEd Maste return -1;
3770afa8e06SEd Maste if (ciphertext->len > UINT_MAX ||
3780afa8e06SEd Maste ciphertext->len > SIZE_MAX - 16 ||
3790afa8e06SEd Maste ciphertext->len < 16)
3800afa8e06SEd Maste return -1;
3810afa8e06SEd Maste plaintext.len = ciphertext->len - 16;
3820afa8e06SEd Maste if ((plaintext.ptr = calloc(1, plaintext.len)) == NULL)
3830afa8e06SEd Maste return -1;
3840afa8e06SEd Maste if ((ctx = EVP_CIPHER_CTX_new()) == NULL ||
3850afa8e06SEd Maste (cipher = EVP_aes_256_gcm()) == NULL ||
3860afa8e06SEd Maste EVP_CipherInit(ctx, cipher, fido_cred_largeblob_key_ptr(cred),
3870afa8e06SEd Maste nonce->ptr, 0) == 0)
3880afa8e06SEd Maste goto out;
3890afa8e06SEd Maste if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16,
3900afa8e06SEd Maste ciphertext->ptr + ciphertext->len - 16) == 0)
3910afa8e06SEd Maste goto out;
3920afa8e06SEd Maste aad[0] = 0x62; /* b */
3930afa8e06SEd Maste aad[1] = 0x6c; /* l */
3940afa8e06SEd Maste aad[2] = 0x6f; /* o */
3950afa8e06SEd Maste aad[3] = 0x62; /* b */
3960afa8e06SEd Maste tmp = htole64(origsiz);
3970afa8e06SEd Maste memcpy(&aad[4], &tmp, sizeof(uint64_t));
3980afa8e06SEd Maste if (EVP_Cipher(ctx, NULL, aad, (u_int)sizeof(aad)) < 0 ||
3990afa8e06SEd Maste EVP_Cipher(ctx, plaintext.ptr, ciphertext->ptr,
4000afa8e06SEd Maste (u_int)plaintext.len) < 0 ||
4010afa8e06SEd Maste EVP_Cipher(ctx, NULL, NULL, 0) < 0)
4020afa8e06SEd Maste goto out;
4030afa8e06SEd Maste if (decompress(&plaintext, origsiz) < 0)
4040afa8e06SEd Maste goto out;
4050afa8e06SEd Maste
4060afa8e06SEd Maste ok = 0;
4070afa8e06SEd Maste out:
4080afa8e06SEd Maste freezero(plaintext.ptr, plaintext.len);
4090afa8e06SEd Maste
4100afa8e06SEd Maste if (ctx != NULL)
4110afa8e06SEd Maste EVP_CIPHER_CTX_free(ctx);
4120afa8e06SEd Maste
4130afa8e06SEd Maste return ok;
4140afa8e06SEd Maste }
4150afa8e06SEd Maste
4160afa8e06SEd Maste static const fido_cred_t *
try_rp(const fido_credman_rk_t * rk,const struct blob * ciphertext,const struct blob * nonce,uint64_t origsiz)4170afa8e06SEd Maste try_rp(const fido_credman_rk_t *rk, const struct blob *ciphertext,
4180afa8e06SEd Maste const struct blob *nonce, uint64_t origsiz)
4190afa8e06SEd Maste {
4200afa8e06SEd Maste const fido_cred_t *cred;
4210afa8e06SEd Maste
4220afa8e06SEd Maste for (size_t i = 0; i < fido_credman_rk_count(rk); i++)
4230afa8e06SEd Maste if ((cred = fido_credman_rk(rk, i)) != NULL &&
4240afa8e06SEd Maste decode(ciphertext, nonce, origsiz, cred) == 0)
4250afa8e06SEd Maste return cred;
4260afa8e06SEd Maste
4270afa8e06SEd Maste return NULL;
4280afa8e06SEd Maste }
4290afa8e06SEd Maste
4300afa8e06SEd Maste static int
decode_cbor_blob(struct blob * out,const cbor_item_t * item)4310afa8e06SEd Maste decode_cbor_blob(struct blob *out, const cbor_item_t *item)
4320afa8e06SEd Maste {
4330afa8e06SEd Maste if (out->ptr != NULL ||
4340afa8e06SEd Maste cbor_isa_bytestring(item) == false ||
4350afa8e06SEd Maste cbor_bytestring_is_definite(item) == false)
4360afa8e06SEd Maste return -1;
4370afa8e06SEd Maste out->len = cbor_bytestring_length(item);
4380afa8e06SEd Maste if ((out->ptr = malloc(out->len)) == NULL)
4390afa8e06SEd Maste return -1;
4400afa8e06SEd Maste memcpy(out->ptr, cbor_bytestring_handle(item), out->len);
4410afa8e06SEd Maste
4420afa8e06SEd Maste return 0;
4430afa8e06SEd Maste }
4440afa8e06SEd Maste
4450afa8e06SEd Maste static int
decode_blob_entry(const cbor_item_t * item,struct blob * ciphertext,struct blob * nonce,uint64_t * origsiz)4460afa8e06SEd Maste decode_blob_entry(const cbor_item_t *item, struct blob *ciphertext,
4470afa8e06SEd Maste struct blob *nonce, uint64_t *origsiz)
4480afa8e06SEd Maste {
4490afa8e06SEd Maste struct cbor_pair *v;
4500afa8e06SEd Maste
4510afa8e06SEd Maste if (item == NULL)
4520afa8e06SEd Maste return -1;
4530afa8e06SEd Maste if (cbor_isa_map(item) == false ||
4540afa8e06SEd Maste cbor_map_is_definite(item) == false ||
4550afa8e06SEd Maste (v = cbor_map_handle(item)) == NULL)
4560afa8e06SEd Maste return -1;
4570afa8e06SEd Maste if (cbor_map_size(item) > UINT8_MAX)
4580afa8e06SEd Maste return -1;
4590afa8e06SEd Maste
4600afa8e06SEd Maste for (size_t i = 0; i < cbor_map_size(item); i++) {
4610afa8e06SEd Maste if (cbor_isa_uint(v[i].key) == false ||
4620afa8e06SEd Maste cbor_int_get_width(v[i].key) != CBOR_INT_8)
4630afa8e06SEd Maste continue; /* ignore */
4640afa8e06SEd Maste switch (cbor_get_uint8(v[i].key)) {
4650afa8e06SEd Maste case 1: /* ciphertext */
4660afa8e06SEd Maste if (decode_cbor_blob(ciphertext, v[i].value) < 0)
4670afa8e06SEd Maste return -1;
4680afa8e06SEd Maste break;
4690afa8e06SEd Maste case 2: /* nonce */
4700afa8e06SEd Maste if (decode_cbor_blob(nonce, v[i].value) < 0)
4710afa8e06SEd Maste return -1;
4720afa8e06SEd Maste break;
4730afa8e06SEd Maste case 3: /* origSize */
4740afa8e06SEd Maste if (*origsiz != 0 ||
4750afa8e06SEd Maste cbor_isa_uint(v[i].value) == false ||
4760afa8e06SEd Maste (*origsiz = cbor_get_int(v[i].value)) > SIZE_MAX)
4770afa8e06SEd Maste return -1;
4780afa8e06SEd Maste }
4790afa8e06SEd Maste }
4800afa8e06SEd Maste if (ciphertext->ptr == NULL || nonce->ptr == NULL || *origsiz == 0)
4810afa8e06SEd Maste return -1;
4820afa8e06SEd Maste
4830afa8e06SEd Maste return 0;
4840afa8e06SEd Maste }
4850afa8e06SEd Maste
4860afa8e06SEd Maste static void
print_blob_entry(size_t idx,const cbor_item_t * item,const struct rkmap * map)4870afa8e06SEd Maste print_blob_entry(size_t idx, const cbor_item_t *item, const struct rkmap *map)
4880afa8e06SEd Maste {
4890afa8e06SEd Maste struct blob ciphertext, nonce;
4900afa8e06SEd Maste const fido_cred_t *cred = NULL;
4910afa8e06SEd Maste const char *rp_id = NULL;
4920afa8e06SEd Maste char *cred_id = NULL;
4930afa8e06SEd Maste uint64_t origsiz = 0;
4940afa8e06SEd Maste
4950afa8e06SEd Maste memset(&ciphertext, 0, sizeof(ciphertext));
4960afa8e06SEd Maste memset(&nonce, 0, sizeof(nonce));
4970afa8e06SEd Maste
4980afa8e06SEd Maste if (decode_blob_entry(item, &ciphertext, &nonce, &origsiz) < 0) {
4990afa8e06SEd Maste printf("%02zu: <skipped: bad cbor>\n", idx);
5000afa8e06SEd Maste goto out;
5010afa8e06SEd Maste }
5020afa8e06SEd Maste for (size_t i = 0; i < fido_credman_rp_count(map->rp); i++) {
5030afa8e06SEd Maste if ((cred = try_rp(map->rk[i], &ciphertext, &nonce,
5040afa8e06SEd Maste origsiz)) != NULL) {
5050afa8e06SEd Maste rp_id = fido_credman_rp_id(map->rp, i);
5060afa8e06SEd Maste break;
5070afa8e06SEd Maste }
5080afa8e06SEd Maste }
5090afa8e06SEd Maste if (cred == NULL) {
5100afa8e06SEd Maste if ((cred_id = strdup("<unknown>")) == NULL) {
5110afa8e06SEd Maste printf("%02zu: <skipped: strdup failed>\n", idx);
5120afa8e06SEd Maste goto out;
5130afa8e06SEd Maste }
5140afa8e06SEd Maste } else {
5150afa8e06SEd Maste if (base64_encode(fido_cred_id_ptr(cred),
5160afa8e06SEd Maste fido_cred_id_len(cred), &cred_id) < 0) {
5170afa8e06SEd Maste printf("%02zu: <skipped: base64_encode failed>\n", idx);
5180afa8e06SEd Maste goto out;
5190afa8e06SEd Maste }
5200afa8e06SEd Maste }
5210afa8e06SEd Maste if (rp_id == NULL)
5220afa8e06SEd Maste rp_id = "<unknown>";
5230afa8e06SEd Maste
5240afa8e06SEd Maste printf("%02zu: %4zu %4zu %s %s\n", idx, ciphertext.len,
5250afa8e06SEd Maste (size_t)origsiz, cred_id, rp_id);
5260afa8e06SEd Maste out:
5270afa8e06SEd Maste free(ciphertext.ptr);
5280afa8e06SEd Maste free(nonce.ptr);
5290afa8e06SEd Maste free(cred_id);
5300afa8e06SEd Maste }
5310afa8e06SEd Maste
5320afa8e06SEd Maste static cbor_item_t *
get_cbor_array(fido_dev_t * dev)5330afa8e06SEd Maste get_cbor_array(fido_dev_t *dev)
5340afa8e06SEd Maste {
5350afa8e06SEd Maste struct cbor_load_result cbor_result;
5360afa8e06SEd Maste cbor_item_t *item = NULL;
5370afa8e06SEd Maste u_char *cbor_ptr = NULL;
5380afa8e06SEd Maste size_t cbor_len;
5390afa8e06SEd Maste int r, ok = -1;
5400afa8e06SEd Maste
5410afa8e06SEd Maste if ((r = fido_dev_largeblob_get_array(dev, &cbor_ptr,
5420afa8e06SEd Maste &cbor_len)) != FIDO_OK) {
5430afa8e06SEd Maste warnx("%s: fido_dev_largeblob_get_array: %s", __func__,
5440afa8e06SEd Maste fido_strerr(r));
5450afa8e06SEd Maste goto out;
5460afa8e06SEd Maste }
5470afa8e06SEd Maste if ((item = cbor_load(cbor_ptr, cbor_len, &cbor_result)) == NULL) {
5480afa8e06SEd Maste warnx("%s: cbor_load", __func__);
5490afa8e06SEd Maste goto out;
5500afa8e06SEd Maste }
5510afa8e06SEd Maste if (cbor_result.read != cbor_len) {
5520afa8e06SEd Maste warnx("%s: cbor_result.read (%zu) != cbor_len (%zu)", __func__,
5530afa8e06SEd Maste cbor_result.read, cbor_len);
5540afa8e06SEd Maste /* continue */
5550afa8e06SEd Maste }
5560afa8e06SEd Maste if (cbor_isa_array(item) == false ||
5570afa8e06SEd Maste cbor_array_is_definite(item) == false) {
5580afa8e06SEd Maste warnx("%s: cbor type", __func__);
5590afa8e06SEd Maste goto out;
5600afa8e06SEd Maste }
5610afa8e06SEd Maste if (cbor_array_size(item) > UINT8_MAX) {
5620afa8e06SEd Maste warnx("%s: cbor_array_size > UINT8_MAX", __func__);
5630afa8e06SEd Maste goto out;
5640afa8e06SEd Maste }
5650afa8e06SEd Maste if (cbor_array_size(item) == 0) {
5660afa8e06SEd Maste ok = 0; /* nothing to do */
5670afa8e06SEd Maste goto out;
5680afa8e06SEd Maste }
5690afa8e06SEd Maste
5700afa8e06SEd Maste printf("total map size: %zu byte%s\n", cbor_len, plural(cbor_len));
5710afa8e06SEd Maste
5720afa8e06SEd Maste ok = 0;
5730afa8e06SEd Maste out:
5740afa8e06SEd Maste if (ok < 0 && item != NULL) {
5750afa8e06SEd Maste cbor_decref(&item);
5760afa8e06SEd Maste item = NULL;
5770afa8e06SEd Maste }
5780afa8e06SEd Maste free(cbor_ptr);
5790afa8e06SEd Maste
5800afa8e06SEd Maste return item;
5810afa8e06SEd Maste }
5820afa8e06SEd Maste
5830afa8e06SEd Maste int
blob_list(const char * path)5840afa8e06SEd Maste blob_list(const char *path)
5850afa8e06SEd Maste {
5860afa8e06SEd Maste struct rkmap map;
5870afa8e06SEd Maste fido_dev_t *dev = NULL;
5880afa8e06SEd Maste cbor_item_t *item = NULL, **v;
5890afa8e06SEd Maste int ok = 1;
5900afa8e06SEd Maste
5910afa8e06SEd Maste memset(&map, 0, sizeof(map));
5920afa8e06SEd Maste dev = open_dev(path);
5930afa8e06SEd Maste if (map_known_rps(dev, path, &map) < 0 ||
5940afa8e06SEd Maste (item = get_cbor_array(dev)) == NULL)
5950afa8e06SEd Maste goto out;
5960afa8e06SEd Maste if (cbor_array_size(item) == 0) {
5970afa8e06SEd Maste ok = 0; /* nothing to do */
5980afa8e06SEd Maste goto out;
5990afa8e06SEd Maste }
6000afa8e06SEd Maste if ((v = cbor_array_handle(item)) == NULL) {
6010afa8e06SEd Maste warnx("%s: cbor_array_handle", __func__);
6020afa8e06SEd Maste goto out;
6030afa8e06SEd Maste }
6040afa8e06SEd Maste for (size_t i = 0; i < cbor_array_size(item); i++)
6050afa8e06SEd Maste print_blob_entry(i, v[i], &map);
6060afa8e06SEd Maste
6070afa8e06SEd Maste ok = 0; /* success */
6080afa8e06SEd Maste out:
6090afa8e06SEd Maste free_rkmap(&map);
6100afa8e06SEd Maste
6110afa8e06SEd Maste if (item != NULL)
6120afa8e06SEd Maste cbor_decref(&item);
6130afa8e06SEd Maste
6140afa8e06SEd Maste fido_dev_close(dev);
6150afa8e06SEd Maste fido_dev_free(&dev);
6160afa8e06SEd Maste
6170afa8e06SEd Maste exit(ok);
6180afa8e06SEd Maste }
619