Home | History | Annotate | Line # | Download | only in fuzz
      1 /*
      2  * Copyright (c) 2019-2024 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 <assert.h>
      9 #include <stdint.h>
     10 #include <stdio.h>
     11 #include <stdlib.h>
     12 #include <string.h>
     13 
     14 #include "mutator_aux.h"
     15 #include "wiredata_fido2.h"
     16 #include "wiredata_u2f.h"
     17 #include "dummy.h"
     18 
     19 #include "../openbsd-compat/openbsd-compat.h"
     20 
     21 /* Parameter set defining a FIDO2 make credential operation. */
     22 struct param {
     23 	char pin[MAXSTR];
     24 	char rp_id[MAXSTR];
     25 	char rp_name[MAXSTR];
     26 	char user_icon[MAXSTR];
     27 	char user_name[MAXSTR];
     28 	char user_nick[MAXSTR];
     29 	int ext;
     30 	int seed;
     31 	struct blob cdh;
     32 	struct blob excl_cred;
     33 	struct blob user_id;
     34 	struct blob wire_data;
     35 	uint8_t excl_count;
     36 	uint8_t rk;
     37 	uint8_t type;
     38 	uint8_t opt;
     39 	uint8_t uv;
     40 };
     41 
     42 /*
     43  * Collection of HID reports from an authenticator issued with a FIDO2
     44  * make credential using the example parameters above.
     45  */
     46 static const uint8_t dummy_wire_data_fido[] = {
     47 	WIREDATA_CTAP_INIT,
     48 	WIREDATA_CTAP_CBOR_INFO,
     49 	WIREDATA_CTAP_CBOR_AUTHKEY,
     50 	WIREDATA_CTAP_CBOR_PINTOKEN,
     51 	WIREDATA_CTAP_KEEPALIVE,
     52 	WIREDATA_CTAP_KEEPALIVE,
     53 	WIREDATA_CTAP_KEEPALIVE,
     54 	WIREDATA_CTAP_CBOR_CRED,
     55 };
     56 
     57 /*
     58  * Collection of HID reports from an authenticator issued with a U2F
     59  * registration using the example parameters above.
     60  */
     61 static const uint8_t dummy_wire_data_u2f[] = {
     62 	WIREDATA_CTAP_INIT,
     63 	WIREDATA_CTAP_U2F_6985,
     64 	WIREDATA_CTAP_U2F_6985,
     65 	WIREDATA_CTAP_U2F_6985,
     66 	WIREDATA_CTAP_U2F_6985,
     67 	WIREDATA_CTAP_U2F_6985,
     68 	WIREDATA_CTAP_U2F_REGISTER,
     69 };
     70 
     71 struct param *
     72 unpack(const uint8_t *ptr, size_t len)
     73 {
     74 	cbor_item_t *item = NULL, **v;
     75 	struct cbor_load_result cbor;
     76 	struct param *p;
     77 	int ok = -1;
     78 
     79 	if ((p = calloc(1, sizeof(*p))) == NULL ||
     80 	    (item = cbor_load(ptr, len, &cbor)) == NULL ||
     81 	    cbor.read != len ||
     82 	    cbor_isa_array(item) == false ||
     83 	    cbor_array_is_definite(item) == false ||
     84 	    cbor_array_size(item) != 17 ||
     85 	    (v = cbor_array_handle(item)) == NULL)
     86 		goto fail;
     87 
     88 	if (unpack_byte(v[0], &p->rk) < 0 ||
     89 	    unpack_byte(v[1], &p->type) < 0 ||
     90 	    unpack_byte(v[2], &p->opt) < 0 ||
     91 	    unpack_byte(v[3], &p->uv) < 0 ||
     92 	    unpack_byte(v[4], &p->excl_count) < 0 ||
     93 	    unpack_int(v[5], &p->ext) < 0 ||
     94 	    unpack_int(v[6], &p->seed) < 0 ||
     95 	    unpack_string(v[7], p->pin) < 0 ||
     96 	    unpack_string(v[8], p->rp_id) < 0 ||
     97 	    unpack_string(v[9], p->rp_name) < 0 ||
     98 	    unpack_string(v[10], p->user_icon) < 0 ||
     99 	    unpack_string(v[11], p->user_name) < 0 ||
    100 	    unpack_string(v[12], p->user_nick) < 0 ||
    101 	    unpack_blob(v[13], &p->cdh) < 0 ||
    102 	    unpack_blob(v[14], &p->user_id) < 0 ||
    103 	    unpack_blob(v[15], &p->wire_data) < 0 ||
    104 	    unpack_blob(v[16], &p->excl_cred) < 0)
    105 		goto fail;
    106 
    107 	ok = 0;
    108 fail:
    109 	if (ok < 0) {
    110 		free(p);
    111 		p = NULL;
    112 	}
    113 
    114 	if (item)
    115 		cbor_decref(&item);
    116 
    117 	return p;
    118 }
    119 
    120 size_t
    121 pack(uint8_t *ptr, size_t len, const struct param *p)
    122 {
    123 	cbor_item_t *argv[17], *array = NULL;
    124 	size_t cbor_alloc_len, cbor_len = 0;
    125 	unsigned char *cbor = NULL;
    126 
    127 	memset(argv, 0, sizeof(argv));
    128 
    129 	if ((array = cbor_new_definite_array(17)) == NULL ||
    130 	    (argv[0] = pack_byte(p->rk)) == NULL ||
    131 	    (argv[1] = pack_byte(p->type)) == NULL ||
    132 	    (argv[2] = pack_byte(p->opt)) == NULL ||
    133 	    (argv[3] = pack_byte(p->uv)) == NULL ||
    134 	    (argv[4] = pack_byte(p->excl_count)) == NULL ||
    135 	    (argv[5] = pack_int(p->ext)) == NULL ||
    136 	    (argv[6] = pack_int(p->seed)) == NULL ||
    137 	    (argv[7] = pack_string(p->pin)) == NULL ||
    138 	    (argv[8] = pack_string(p->rp_id)) == NULL ||
    139 	    (argv[9] = pack_string(p->rp_name)) == NULL ||
    140 	    (argv[10] = pack_string(p->user_icon)) == NULL ||
    141 	    (argv[11] = pack_string(p->user_name)) == NULL ||
    142 	    (argv[12] = pack_string(p->user_nick)) == NULL ||
    143 	    (argv[13] = pack_blob(&p->cdh)) == NULL ||
    144 	    (argv[14] = pack_blob(&p->user_id)) == NULL ||
    145 	    (argv[15] = pack_blob(&p->wire_data)) == NULL ||
    146 	    (argv[16] = pack_blob(&p->excl_cred)) == NULL)
    147 		goto fail;
    148 
    149 	for (size_t i = 0; i < 17; i++)
    150 		if (cbor_array_push(array, argv[i]) == false)
    151 			goto fail;
    152 
    153 	if ((cbor_len = cbor_serialize_alloc(array, &cbor,
    154 	    &cbor_alloc_len)) == 0 || cbor_len > len) {
    155 		cbor_len = 0;
    156 		goto fail;
    157 	}
    158 
    159 	memcpy(ptr, cbor, cbor_len);
    160 fail:
    161 	for (size_t i = 0; i < 17; i++)
    162 		if (argv[i])
    163 			cbor_decref(&argv[i]);
    164 
    165 	if (array)
    166 		cbor_decref(&array);
    167 
    168 	free(cbor);
    169 
    170 	return cbor_len;
    171 }
    172 
    173 size_t
    174 pack_dummy(uint8_t *ptr, size_t len)
    175 {
    176 	struct param dummy;
    177 	uint8_t blob[MAXCORPUS];
    178 	size_t blob_len;
    179 
    180 	memset(&dummy, 0, sizeof(dummy));
    181 
    182 	dummy.type = 1;
    183 	dummy.ext = FIDO_EXT_HMAC_SECRET;
    184 
    185 	strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
    186 	strlcpy(dummy.rp_id, dummy_rp_id, sizeof(dummy.rp_id));
    187 	strlcpy(dummy.rp_name, dummy_rp_name, sizeof(dummy.rp_name));
    188 	strlcpy(dummy.user_icon, dummy_user_icon, sizeof(dummy.user_icon));
    189 	strlcpy(dummy.user_name, dummy_user_name, sizeof(dummy.user_name));
    190 	strlcpy(dummy.user_nick, dummy_user_nick, sizeof(dummy.user_nick));
    191 
    192 	dummy.cdh.len = sizeof(dummy_cdh);
    193 	dummy.user_id.len = sizeof(dummy_user_id);
    194 	dummy.wire_data.len = sizeof(dummy_wire_data_fido);
    195 
    196 	memcpy(&dummy.cdh.body, &dummy_cdh, dummy.cdh.len);
    197 	memcpy(&dummy.user_id.body, &dummy_user_id, dummy.user_id.len);
    198 	memcpy(&dummy.wire_data.body, &dummy_wire_data_fido,
    199 	    dummy.wire_data.len);
    200 
    201 	assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
    202 
    203 	if (blob_len > len) {
    204 		memcpy(ptr, blob, len);
    205 		return len;
    206 	}
    207 
    208 	memcpy(ptr, blob, blob_len);
    209 
    210 	return blob_len;
    211 }
    212 
    213 static void
    214 make_cred(fido_cred_t *cred, uint8_t opt, int type, const struct blob *cdh,
    215     const char *rp_id, const char *rp_name, const struct blob *user_id,
    216     const char *user_name, const char *user_nick, const char *user_icon,
    217     int ext, uint8_t rk, uint8_t uv, const char *pin, uint8_t excl_count,
    218     const struct blob *excl_cred)
    219 {
    220 	fido_dev_t *dev;
    221 
    222 	if ((dev = open_dev(opt & 2)) == NULL)
    223 		return;
    224 	if (opt & 1)
    225 		fido_dev_force_u2f(dev);
    226 
    227 	for (uint8_t i = 0; i < excl_count; i++)
    228 		fido_cred_exclude(cred, excl_cred->body, excl_cred->len);
    229 
    230 	fido_cred_set_type(cred, type);
    231 	fido_cred_set_clientdata_hash(cred, cdh->body, cdh->len);
    232 	fido_cred_set_rp(cred, rp_id, rp_name);
    233 	fido_cred_set_user(cred, user_id->body, user_id->len, user_name,
    234 	    user_nick, user_icon);
    235 
    236 	if (ext & FIDO_EXT_HMAC_SECRET)
    237 		fido_cred_set_extensions(cred, FIDO_EXT_HMAC_SECRET);
    238 	if (ext & FIDO_EXT_CRED_BLOB)
    239 		fido_cred_set_blob(cred, user_id->body, user_id->len);
    240 	if (ext & FIDO_EXT_LARGEBLOB_KEY)
    241 		fido_cred_set_extensions(cred, FIDO_EXT_LARGEBLOB_KEY);
    242 	if (ext & FIDO_EXT_MINPINLEN)
    243 		fido_cred_set_pin_minlen(cred, strlen(pin));
    244 
    245 	if (rk & 1)
    246 		fido_cred_set_rk(cred, FIDO_OPT_TRUE);
    247 	if (uv & 1)
    248 		fido_cred_set_uv(cred, FIDO_OPT_TRUE);
    249 	if (user_id->len)
    250 		fido_cred_set_prot(cred, user_id->body[0] & 0x03);
    251 	if (excl_cred->len)
    252 		fido_cred_set_entattest(cred, excl_cred->body[0] & 0x03);
    253 
    254 	/* repeat memory operations to trigger reallocation paths */
    255 	fido_cred_set_type(cred, type);
    256 	fido_cred_set_clientdata_hash(cred, cdh->body, cdh->len);
    257 	fido_cred_set_rp(cred, rp_id, rp_name);
    258 	fido_cred_set_user(cred, user_id->body, user_id->len, user_name,
    259 	    user_nick, user_icon);
    260 
    261 	if (strlen(pin) == 0)
    262 		pin = NULL;
    263 
    264 	fido_dev_make_cred(dev, cred, (opt & 1) ? NULL : pin);
    265 
    266 	fido_dev_cancel(dev);
    267 	fido_dev_close(dev);
    268 	fido_dev_free(&dev);
    269 }
    270 
    271 static void
    272 verify_cred(int type, const unsigned char *cdh_ptr, size_t cdh_len,
    273     const char *rp_id, const char *rp_name, const unsigned char *authdata_ptr,
    274     size_t authdata_len, const unsigned char *authdata_raw_ptr,
    275     size_t authdata_raw_len, int ext, uint8_t rk, uint8_t uv,
    276     const unsigned char *x5c_ptr, size_t x5c_len, const unsigned char *sig_ptr,
    277     size_t sig_len, const unsigned char *attstmt_ptr, size_t attstmt_len,
    278     const char *fmt, int prot, size_t minpinlen)
    279 {
    280 	fido_cred_t *cred;
    281 	uint8_t flags;
    282 	uint32_t sigcount;
    283 	int r;
    284 	bool ea;
    285 
    286 	if ((cred = fido_cred_new()) == NULL)
    287 		return;
    288 
    289 	fido_cred_set_type(cred, type);
    290 	fido_cred_set_clientdata_hash(cred, cdh_ptr, cdh_len);
    291 	fido_cred_set_rp(cred, rp_id, rp_name);
    292 	consume(authdata_ptr, authdata_len);
    293 	consume(authdata_raw_ptr, authdata_raw_len);
    294 	consume(x5c_ptr, x5c_len);
    295 	consume(sig_ptr, sig_len);
    296 	consume(attstmt_ptr, attstmt_len);
    297 	if (fido_cred_set_authdata(cred, authdata_ptr, authdata_len) != FIDO_OK)
    298 		fido_cred_set_authdata_raw(cred, authdata_raw_ptr,
    299 		    authdata_raw_len);
    300 	fido_cred_set_extensions(cred, ext);
    301 	if (fido_cred_set_attstmt(cred, attstmt_ptr, attstmt_len) != FIDO_OK) {
    302 		fido_cred_set_x509(cred, x5c_ptr, x5c_len);
    303 		fido_cred_set_sig(cred, sig_ptr, sig_len);
    304 	}
    305 	fido_cred_set_prot(cred, prot);
    306 	fido_cred_set_pin_minlen(cred, minpinlen);
    307 
    308 	if (rk & 1)
    309 		fido_cred_set_rk(cred, FIDO_OPT_TRUE);
    310 	if (uv & 1)
    311 		fido_cred_set_uv(cred, FIDO_OPT_TRUE);
    312 	if (fmt)
    313 		fido_cred_set_fmt(cred, fmt);
    314 
    315 	/* XXX +1 on purpose */
    316 	for (size_t i = 0; i < fido_cred_x5c_list_count(cred) + 1; i++)
    317 		consume(fido_cred_x5c_list_ptr(cred, i),
    318 		    fido_cred_x5c_list_len(cred, i));
    319 
    320 	/* repeat memory operations to trigger reallocation paths */
    321 	if (fido_cred_set_authdata(cred, authdata_ptr, authdata_len) != FIDO_OK)
    322 		fido_cred_set_authdata_raw(cred, authdata_raw_ptr,
    323 		    authdata_raw_len);
    324 	if (fido_cred_set_attstmt(cred, attstmt_ptr, attstmt_len) != FIDO_OK) {
    325 		fido_cred_set_x509(cred, x5c_ptr, x5c_len);
    326 		fido_cred_set_sig(cred, sig_ptr, sig_len);
    327 	}
    328 	fido_cred_set_x509(cred, x5c_ptr, x5c_len);
    329 	fido_cred_set_sig(cred, sig_ptr, sig_len);
    330 
    331 	r = fido_cred_verify(cred);
    332 	consume(&r, sizeof(r));
    333 	r = fido_cred_verify_self(cred);
    334 	consume(&r, sizeof(r));
    335 
    336 	consume(fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred));
    337 	consume(fido_cred_id_ptr(cred), fido_cred_id_len(cred));
    338 	consume(fido_cred_aaguid_ptr(cred), fido_cred_aaguid_len(cred));
    339 	consume(fido_cred_user_id_ptr(cred), fido_cred_user_id_len(cred));
    340 	consume_str(fido_cred_user_name(cred));
    341 	consume_str(fido_cred_display_name(cred));
    342 	consume(fido_cred_largeblob_key_ptr(cred),
    343 	    fido_cred_largeblob_key_len(cred));
    344 
    345 	flags = fido_cred_flags(cred);
    346 	consume(&flags, sizeof(flags));
    347 	sigcount = fido_cred_sigcount(cred);
    348 	consume(&sigcount, sizeof(sigcount));
    349 	type = fido_cred_type(cred);
    350 	consume(&type, sizeof(type));
    351 	minpinlen = fido_cred_pin_minlen(cred);
    352 	consume(&minpinlen, sizeof(minpinlen));
    353 
    354 	ea = fido_cred_entattest(cred);
    355 	consume(&ea, sizeof(ea));
    356 
    357 	fido_cred_free(&cred);
    358 }
    359 
    360 static void
    361 test_cred(const struct param *p)
    362 {
    363 	fido_cred_t *cred = NULL;
    364 	int cose_alg = 0;
    365 
    366 	if ((cred = fido_cred_new()) == NULL)
    367 		return;
    368 
    369 	switch (p->type & 3) {
    370 	case 0:
    371 		cose_alg = COSE_ES256;
    372 		break;
    373 	case 1:
    374 		cose_alg = COSE_RS256;
    375 		break;
    376 	case 2:
    377 		cose_alg = COSE_ES384;
    378 		break;
    379 	default:
    380 		cose_alg = COSE_EDDSA;
    381 		break;
    382 	}
    383 
    384 	set_wire_data(p->wire_data.body, p->wire_data.len);
    385 
    386 	make_cred(cred, p->opt, cose_alg, &p->cdh, p->rp_id, p->rp_name,
    387 	    &p->user_id, p->user_name, p->user_nick, p->user_icon, p->ext,
    388 	    p->rk, p->uv, p->pin, p->excl_count, &p->excl_cred);
    389 
    390 	verify_cred(cose_alg,
    391 	    fido_cred_clientdata_hash_ptr(cred),
    392 	    fido_cred_clientdata_hash_len(cred), fido_cred_rp_id(cred),
    393 	    fido_cred_rp_name(cred), fido_cred_authdata_ptr(cred),
    394 	    fido_cred_authdata_len(cred), fido_cred_authdata_raw_ptr(cred),
    395 	    fido_cred_authdata_raw_len(cred), p->ext, p->rk, p->uv,
    396 	    fido_cred_x5c_ptr(cred), fido_cred_x5c_len(cred),
    397 	    fido_cred_sig_ptr(cred), fido_cred_sig_len(cred),
    398 	    fido_cred_attstmt_ptr(cred), fido_cred_attstmt_len(cred),
    399 	    fido_cred_fmt(cred), fido_cred_prot(cred),
    400 	    fido_cred_pin_minlen(cred));
    401 
    402 	fido_cred_free(&cred);
    403 }
    404 
    405 static void
    406 test_touch(const struct param *p)
    407 {
    408 	fido_dev_t *dev;
    409 	int r;
    410 	int touched;
    411 
    412 	set_wire_data(p->wire_data.body, p->wire_data.len);
    413 
    414 	if ((dev = open_dev(p->opt & 2)) == NULL)
    415 		return;
    416 	if (p->opt & 1)
    417 		fido_dev_force_u2f(dev);
    418 
    419 	r = fido_dev_get_touch_begin(dev);
    420 	consume_str(fido_strerr(r));
    421 	r = fido_dev_get_touch_status(dev, &touched, -1);
    422 	consume_str(fido_strerr(r));
    423 	consume(&touched, sizeof(touched));
    424 
    425 	fido_dev_cancel(dev);
    426 	fido_dev_close(dev);
    427 	fido_dev_free(&dev);
    428 }
    429 
    430 static void
    431 test_misc(const struct param *p)
    432 {
    433 	fido_cred_t *cred = NULL;
    434 
    435 	if ((cred = fido_cred_new()) == NULL)
    436 		return;
    437 
    438 	/* reuse user id as credential id */
    439 	fido_cred_set_id(cred, p->user_id.body, p->user_id.len);
    440 	consume(fido_cred_id_ptr(cred), fido_cred_id_len(cred));
    441 	fido_cred_free(&cred);
    442 }
    443 
    444 void
    445 test(const struct param *p)
    446 {
    447 	prng_init((unsigned int)p->seed);
    448 	fuzz_clock_reset();
    449 	fido_init(FIDO_DEBUG);
    450 	fido_set_log_handler(consume_str);
    451 
    452 	test_cred(p);
    453 	test_touch(p);
    454 	test_misc(p);
    455 }
    456 
    457 void
    458 mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
    459 {
    460 	if (flags & MUTATE_SEED)
    461 		p->seed = (int)seed;
    462 
    463 	if (flags & MUTATE_PARAM) {
    464 		mutate_byte(&p->rk);
    465 		mutate_byte(&p->type);
    466 		mutate_byte(&p->opt);
    467 		mutate_byte(&p->uv);
    468 		mutate_byte(&p->excl_count);
    469 		mutate_int(&p->ext);
    470 		mutate_blob(&p->cdh);
    471 		mutate_blob(&p->user_id);
    472 		mutate_blob(&p->excl_cred);
    473 		mutate_string(p->pin);
    474 		mutate_string(p->user_icon);
    475 		mutate_string(p->user_name);
    476 		mutate_string(p->user_nick);
    477 		mutate_string(p->rp_id);
    478 		mutate_string(p->rp_name);
    479 	}
    480 
    481 	if (flags & MUTATE_WIREDATA) {
    482 		if (p->opt & 1) {
    483 			p->wire_data.len = sizeof(dummy_wire_data_u2f);
    484 			memcpy(&p->wire_data.body, &dummy_wire_data_u2f,
    485 			    p->wire_data.len);
    486 		} else {
    487 			p->wire_data.len = sizeof(dummy_wire_data_fido);
    488 			memcpy(&p->wire_data.body, &dummy_wire_data_fido,
    489 			    p->wire_data.len);
    490 		}
    491 		mutate_blob(&p->wire_data);
    492 	}
    493 }
    494