xref: /freebsd/contrib/libfido2/src/u2f.c (revision 2ccfa855b2fc331819953e3de1b1c15ce5b95a7e)
1 /*
2  * Copyright (c) 2018-2022 Yubico AB. All rights reserved.
3  * Use of this source code is governed by a BSD-style
4  * license that can be found in the LICENSE file.
5  * SPDX-License-Identifier: BSD-2-Clause
6  */
7 
8 #include <openssl/sha.h>
9 #include <openssl/x509.h>
10 
11 #ifdef HAVE_UNISTD_H
12 #include <unistd.h>
13 #endif
14 #include <errno.h>
15 
16 #include "fido.h"
17 #include "fido/es256.h"
18 #include "fallthrough.h"
19 
20 #define U2F_PACE_MS (100)
21 
22 #if defined(_MSC_VER)
23 static int
usleep(unsigned int usec)24 usleep(unsigned int usec)
25 {
26 	Sleep(usec / 1000);
27 
28 	return (0);
29 }
30 #endif
31 
32 static int
delay_ms(unsigned int ms,int * ms_remain)33 delay_ms(unsigned int ms, int *ms_remain)
34 {
35 	if (*ms_remain > -1 && (unsigned int)*ms_remain < ms)
36 		ms = (unsigned int)*ms_remain;
37 
38 	if (ms > UINT_MAX / 1000) {
39 		fido_log_debug("%s: ms=%u", __func__, ms);
40 		return (-1);
41 	}
42 
43 	if (usleep(ms * 1000) < 0) {
44 		fido_log_error(errno, "%s: usleep", __func__);
45 		return (-1);
46 	}
47 
48 	if (*ms_remain > -1)
49 		*ms_remain -= (int)ms;
50 
51 	return (0);
52 }
53 
54 static int
sig_get(fido_blob_t * sig,const unsigned char ** buf,size_t * len)55 sig_get(fido_blob_t *sig, const unsigned char **buf, size_t *len)
56 {
57 	sig->len = *len; /* consume the whole buffer */
58 	if ((sig->ptr = calloc(1, sig->len)) == NULL ||
59 	    fido_buf_read(buf, len, sig->ptr, sig->len) < 0) {
60 		fido_log_debug("%s: fido_buf_read", __func__);
61 		fido_blob_reset(sig);
62 		return (-1);
63 	}
64 
65 	return (0);
66 }
67 
68 static int
x5c_get(fido_blob_t * x5c,const unsigned char ** buf,size_t * len)69 x5c_get(fido_blob_t *x5c, const unsigned char **buf, size_t *len)
70 {
71 	X509	*cert = NULL;
72 	int	 ok = -1;
73 
74 	if (*len > LONG_MAX) {
75 		fido_log_debug("%s: invalid len %zu", __func__, *len);
76 		goto fail;
77 	}
78 
79 	/* find out the certificate's length */
80 	const unsigned char *end = *buf;
81 	if ((cert = d2i_X509(NULL, &end, (long)*len)) == NULL || end <= *buf ||
82 	    (x5c->len = (size_t)(end - *buf)) >= *len) {
83 		fido_log_debug("%s: d2i_X509", __func__);
84 		goto fail;
85 	}
86 
87 	/* read accordingly */
88 	if ((x5c->ptr = calloc(1, x5c->len)) == NULL ||
89 	    fido_buf_read(buf, len, x5c->ptr, x5c->len) < 0) {
90 		fido_log_debug("%s: fido_buf_read", __func__);
91 		goto fail;
92 	}
93 
94 	ok = 0;
95 fail:
96 	if (cert != NULL)
97 		X509_free(cert);
98 
99 	if (ok < 0)
100 		fido_blob_reset(x5c);
101 
102 	return (ok);
103 }
104 
105 static int
authdata_fake(const char * rp_id,uint8_t flags,uint32_t sigcount,fido_blob_t * fake_cbor_ad)106 authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount,
107     fido_blob_t *fake_cbor_ad)
108 {
109 	fido_authdata_t	 ad;
110 	cbor_item_t	*item = NULL;
111 	size_t		 alloc_len;
112 
113 	memset(&ad, 0, sizeof(ad));
114 
115 	if (SHA256((const void *)rp_id, strlen(rp_id),
116 	    ad.rp_id_hash) != ad.rp_id_hash) {
117 		fido_log_debug("%s: sha256", __func__);
118 		return (-1);
119 	}
120 
121 	ad.flags = flags; /* XXX translate? */
122 	ad.sigcount = sigcount;
123 
124 	if ((item = cbor_build_bytestring((const unsigned char *)&ad,
125 	    sizeof(ad))) == NULL) {
126 		fido_log_debug("%s: cbor_build_bytestring", __func__);
127 		return (-1);
128 	}
129 
130 	if (fake_cbor_ad->ptr != NULL ||
131 	    (fake_cbor_ad->len = cbor_serialize_alloc(item, &fake_cbor_ad->ptr,
132 	    &alloc_len)) == 0) {
133 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
134 		cbor_decref(&item);
135 		return (-1);
136 	}
137 
138 	cbor_decref(&item);
139 
140 	return (0);
141 }
142 
143 /* TODO: use u2f_get_touch_begin & u2f_get_touch_status instead */
144 static int
send_dummy_register(fido_dev_t * dev,int * ms)145 send_dummy_register(fido_dev_t *dev, int *ms)
146 {
147 	iso7816_apdu_t	*apdu = NULL;
148 	unsigned char	*reply = NULL;
149 	unsigned char	 challenge[SHA256_DIGEST_LENGTH];
150 	unsigned char	 application[SHA256_DIGEST_LENGTH];
151 	int		 r;
152 
153 	/* dummy challenge & application */
154 	memset(&challenge, 0xff, sizeof(challenge));
155 	memset(&application, 0xff, sizeof(application));
156 
157 	if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
158 	    SHA256_DIGEST_LENGTH)) == NULL ||
159 	    iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
160 	    iso7816_add(apdu, &application, sizeof(application)) < 0) {
161 		fido_log_debug("%s: iso7816", __func__);
162 		r = FIDO_ERR_INTERNAL;
163 		goto fail;
164 	}
165 
166 	if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
167 		fido_log_debug("%s: malloc", __func__);
168 		r = FIDO_ERR_INTERNAL;
169 		goto fail;
170 	}
171 
172 	do {
173 		if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
174 		    iso7816_len(apdu), ms) < 0) {
175 			fido_log_debug("%s: fido_tx", __func__);
176 			r = FIDO_ERR_TX;
177 			goto fail;
178 		}
179 		if (fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, ms) < 2) {
180 			fido_log_debug("%s: fido_rx", __func__);
181 			r = FIDO_ERR_RX;
182 			goto fail;
183 		}
184 		if (delay_ms(U2F_PACE_MS, ms) != 0) {
185 			fido_log_debug("%s: delay_ms", __func__);
186 			r = FIDO_ERR_RX;
187 			goto fail;
188 		}
189 	} while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
190 
191 	r = FIDO_OK;
192 fail:
193 	iso7816_free(&apdu);
194 	freezero(reply, FIDO_MAXMSG);
195 
196 	return (r);
197 }
198 
199 static int
key_lookup(fido_dev_t * dev,const char * rp_id,const fido_blob_t * key_id,int * found,int * ms)200 key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id,
201     int *found, int *ms)
202 {
203 	iso7816_apdu_t	*apdu = NULL;
204 	unsigned char	*reply = NULL;
205 	unsigned char	 challenge[SHA256_DIGEST_LENGTH];
206 	unsigned char	 rp_id_hash[SHA256_DIGEST_LENGTH];
207 	uint8_t		 key_id_len;
208 	int		 r;
209 
210 	if (key_id->len > UINT8_MAX || rp_id == NULL) {
211 		fido_log_debug("%s: key_id->len=%zu, rp_id=%p", __func__,
212 		    key_id->len, (const void *)rp_id);
213 		r = FIDO_ERR_INVALID_ARGUMENT;
214 		goto fail;
215 	}
216 
217 	memset(&challenge, 0xff, sizeof(challenge));
218 	memset(&rp_id_hash, 0, sizeof(rp_id_hash));
219 
220 	if (SHA256((const void *)rp_id, strlen(rp_id),
221 	    rp_id_hash) != rp_id_hash) {
222 		fido_log_debug("%s: sha256", __func__);
223 		r = FIDO_ERR_INTERNAL;
224 		goto fail;
225 	}
226 
227 	key_id_len = (uint8_t)key_id->len;
228 
229 	if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_CHECK, (uint16_t)(2 *
230 	    SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
231 	    iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
232 	    iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
233 	    iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
234 	    iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
235 		fido_log_debug("%s: iso7816", __func__);
236 		r = FIDO_ERR_INTERNAL;
237 		goto fail;
238 	}
239 
240 	if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
241 		fido_log_debug("%s: malloc", __func__);
242 		r = FIDO_ERR_INTERNAL;
243 		goto fail;
244 	}
245 
246 	if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
247 	    iso7816_len(apdu), ms) < 0) {
248 		fido_log_debug("%s: fido_tx", __func__);
249 		r = FIDO_ERR_TX;
250 		goto fail;
251 	}
252 	if (fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, ms) != 2) {
253 		fido_log_debug("%s: fido_rx", __func__);
254 		r = FIDO_ERR_RX;
255 		goto fail;
256 	}
257 
258 	switch ((reply[0] << 8) | reply[1]) {
259 	case SW_CONDITIONS_NOT_SATISFIED:
260 		*found = 1; /* key exists */
261 		break;
262 	case SW_WRONG_DATA:
263 		*found = 0; /* key does not exist */
264 		break;
265 	default:
266 		/* unexpected sw */
267 		r = FIDO_ERR_INTERNAL;
268 		goto fail;
269 	}
270 
271 	r = FIDO_OK;
272 fail:
273 	iso7816_free(&apdu);
274 	freezero(reply, FIDO_MAXMSG);
275 
276 	return (r);
277 }
278 
279 static int
parse_auth_reply(fido_blob_t * sig,fido_blob_t * ad,const char * rp_id,const unsigned char * reply,size_t len)280 parse_auth_reply(fido_blob_t *sig, fido_blob_t *ad, const char *rp_id,
281     const unsigned char *reply, size_t len)
282 {
283 	uint8_t		flags;
284 	uint32_t	sigcount;
285 
286 	if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
287 		fido_log_debug("%s: unexpected sw", __func__);
288 		return (FIDO_ERR_RX);
289 	}
290 
291 	len -= 2;
292 
293 	if (fido_buf_read(&reply, &len, &flags, sizeof(flags)) < 0 ||
294 	    fido_buf_read(&reply, &len, &sigcount, sizeof(sigcount)) < 0) {
295 		fido_log_debug("%s: fido_buf_read", __func__);
296 		return (FIDO_ERR_RX);
297 	}
298 
299 	if (sig_get(sig, &reply, &len) < 0) {
300 		fido_log_debug("%s: sig_get", __func__);
301 		return (FIDO_ERR_RX);
302 	}
303 
304 	if (authdata_fake(rp_id, flags, sigcount, ad) < 0) {
305 		fido_log_debug("%s; authdata_fake", __func__);
306 		return (FIDO_ERR_RX);
307 	}
308 
309 	return (FIDO_OK);
310 }
311 
312 static int
do_auth(fido_dev_t * dev,const fido_blob_t * cdh,const char * rp_id,const fido_blob_t * key_id,fido_blob_t * sig,fido_blob_t * ad,int * ms)313 do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
314     const fido_blob_t *key_id, fido_blob_t *sig, fido_blob_t *ad, int *ms)
315 {
316 	iso7816_apdu_t	*apdu = NULL;
317 	unsigned char	*reply = NULL;
318 	unsigned char	 rp_id_hash[SHA256_DIGEST_LENGTH];
319 	int		 reply_len;
320 	uint8_t		 key_id_len;
321 	int		 r;
322 
323 #ifdef FIDO_FUZZ
324 	*ms = 0; /* XXX */
325 #endif
326 
327 	if (cdh->len != SHA256_DIGEST_LENGTH || key_id->len > UINT8_MAX ||
328 	    rp_id == NULL) {
329 		r = FIDO_ERR_INVALID_ARGUMENT;
330 		goto fail;
331 	}
332 
333 	memset(&rp_id_hash, 0, sizeof(rp_id_hash));
334 
335 	if (SHA256((const void *)rp_id, strlen(rp_id),
336 	    rp_id_hash) != rp_id_hash) {
337 		fido_log_debug("%s: sha256", __func__);
338 		r = FIDO_ERR_INTERNAL;
339 		goto fail;
340 	}
341 
342 	key_id_len = (uint8_t)key_id->len;
343 
344 	if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_SIGN, (uint16_t)(2 *
345 	    SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
346 	    iso7816_add(apdu, cdh->ptr, cdh->len) < 0 ||
347 	    iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
348 	    iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
349 	    iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
350 		fido_log_debug("%s: iso7816", __func__);
351 		r = FIDO_ERR_INTERNAL;
352 		goto fail;
353 	}
354 
355 	if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
356 		fido_log_debug("%s: malloc", __func__);
357 		r = FIDO_ERR_INTERNAL;
358 		goto fail;
359 	}
360 
361 	do {
362 		if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
363 		    iso7816_len(apdu), ms) < 0) {
364 			fido_log_debug("%s: fido_tx", __func__);
365 			r = FIDO_ERR_TX;
366 			goto fail;
367 		}
368 		if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply,
369 		    FIDO_MAXMSG, ms)) < 2) {
370 			fido_log_debug("%s: fido_rx", __func__);
371 			r = FIDO_ERR_RX;
372 			goto fail;
373 		}
374 		if (delay_ms(U2F_PACE_MS, ms) != 0) {
375 			fido_log_debug("%s: delay_ms", __func__);
376 			r = FIDO_ERR_RX;
377 			goto fail;
378 		}
379 	} while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
380 
381 	if ((r = parse_auth_reply(sig, ad, rp_id, reply,
382 	    (size_t)reply_len)) != FIDO_OK) {
383 		fido_log_debug("%s: parse_auth_reply", __func__);
384 		goto fail;
385 	}
386 
387 fail:
388 	iso7816_free(&apdu);
389 	freezero(reply, FIDO_MAXMSG);
390 
391 	return (r);
392 }
393 
394 static int
cbor_blob_from_ec_point(const uint8_t * ec_point,size_t ec_point_len,fido_blob_t * cbor_blob)395 cbor_blob_from_ec_point(const uint8_t *ec_point, size_t ec_point_len,
396     fido_blob_t *cbor_blob)
397 {
398 	es256_pk_t	*pk = NULL;
399 	cbor_item_t	*pk_cbor = NULL;
400 	size_t		 alloc_len;
401 	int		 ok = -1;
402 
403 	/* only handle uncompressed points */
404 	if (ec_point_len != 65 || ec_point[0] != 0x04) {
405 		fido_log_debug("%s: unexpected format", __func__);
406 		goto fail;
407 	}
408 
409 	if ((pk = es256_pk_new()) == NULL ||
410 	    es256_pk_set_x(pk, &ec_point[1]) < 0 ||
411 	    es256_pk_set_y(pk, &ec_point[33]) < 0) {
412 		fido_log_debug("%s: es256_pk_set", __func__);
413 		goto fail;
414 	}
415 
416 	if ((pk_cbor = es256_pk_encode(pk, 0)) == NULL) {
417 		fido_log_debug("%s: es256_pk_encode", __func__);
418 		goto fail;
419 	}
420 
421 	if ((cbor_blob->len = cbor_serialize_alloc(pk_cbor, &cbor_blob->ptr,
422 	    &alloc_len)) != 77) {
423 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
424 		goto fail;
425 	}
426 
427 	ok = 0;
428 fail:
429 	es256_pk_free(&pk);
430 
431 	if (pk_cbor)
432 		cbor_decref(&pk_cbor);
433 
434 	return (ok);
435 }
436 
437 static int
encode_cred_attstmt(int cose_alg,const fido_blob_t * x5c,const fido_blob_t * sig,fido_blob_t * out)438 encode_cred_attstmt(int cose_alg, const fido_blob_t *x5c,
439     const fido_blob_t *sig, fido_blob_t *out)
440 {
441 	cbor_item_t		*item = NULL;
442 	cbor_item_t		*x5c_cbor = NULL;
443 	const uint8_t		 alg_cbor = (uint8_t)(-cose_alg - 1);
444 	struct cbor_pair	 kv[3];
445 	size_t			 alloc_len;
446 	int			 ok = -1;
447 
448 	memset(&kv, 0, sizeof(kv));
449 	memset(out, 0, sizeof(*out));
450 
451 	if ((item = cbor_new_definite_map(3)) == NULL) {
452 		fido_log_debug("%s: cbor_new_definite_map", __func__);
453 		goto fail;
454 	}
455 
456 	if ((kv[0].key = cbor_build_string("alg")) == NULL ||
457 	    (kv[0].value = cbor_build_negint8(alg_cbor)) == NULL ||
458 	    !cbor_map_add(item, kv[0])) {
459 		fido_log_debug("%s: alg", __func__);
460 		goto fail;
461 	}
462 
463 	if ((kv[1].key = cbor_build_string("sig")) == NULL ||
464 	    (kv[1].value = fido_blob_encode(sig)) == NULL ||
465 	    !cbor_map_add(item, kv[1])) {
466 		fido_log_debug("%s: sig", __func__);
467 		goto fail;
468 	}
469 
470 	if ((kv[2].key = cbor_build_string("x5c")) == NULL ||
471 	    (kv[2].value = cbor_new_definite_array(1)) == NULL ||
472 	    (x5c_cbor = fido_blob_encode(x5c)) == NULL ||
473 	    !cbor_array_push(kv[2].value, x5c_cbor) ||
474 	    !cbor_map_add(item, kv[2])) {
475 		fido_log_debug("%s: x5c", __func__);
476 		goto fail;
477 	}
478 
479 	if ((out->len = cbor_serialize_alloc(item, &out->ptr,
480 	    &alloc_len)) == 0) {
481 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
482 		goto fail;
483 	}
484 
485 	ok = 0;
486 fail:
487 	if (item != NULL)
488 		cbor_decref(&item);
489 	if (x5c_cbor != NULL)
490 		cbor_decref(&x5c_cbor);
491 
492 	for (size_t i = 0; i < nitems(kv); i++) {
493 		if (kv[i].key)
494 			cbor_decref(&kv[i].key);
495 		if (kv[i].value)
496 			cbor_decref(&kv[i].value);
497 	}
498 
499 	return (ok);
500 }
501 
502 static int
encode_cred_authdata(const char * rp_id,const uint8_t * kh,uint8_t kh_len,const uint8_t * pubkey,size_t pubkey_len,fido_blob_t * out)503 encode_cred_authdata(const char *rp_id, const uint8_t *kh, uint8_t kh_len,
504     const uint8_t *pubkey, size_t pubkey_len, fido_blob_t *out)
505 {
506 	fido_authdata_t	 	 authdata;
507 	fido_attcred_raw_t	 attcred_raw;
508 	fido_blob_t		 pk_blob;
509 	fido_blob_t		 authdata_blob;
510 	cbor_item_t		*authdata_cbor = NULL;
511 	unsigned char		*ptr;
512 	size_t			 len;
513 	size_t			 alloc_len;
514 	int			 ok = -1;
515 
516 	memset(&pk_blob, 0, sizeof(pk_blob));
517 	memset(&authdata, 0, sizeof(authdata));
518 	memset(&authdata_blob, 0, sizeof(authdata_blob));
519 	memset(out, 0, sizeof(*out));
520 
521 	if (rp_id == NULL) {
522 		fido_log_debug("%s: NULL rp_id", __func__);
523 		goto fail;
524 	}
525 
526 	if (cbor_blob_from_ec_point(pubkey, pubkey_len, &pk_blob) < 0) {
527 		fido_log_debug("%s: cbor_blob_from_ec_point", __func__);
528 		goto fail;
529 	}
530 
531 	if (SHA256((const void *)rp_id, strlen(rp_id),
532 	    authdata.rp_id_hash) != authdata.rp_id_hash) {
533 		fido_log_debug("%s: sha256", __func__);
534 		goto fail;
535 	}
536 
537 	authdata.flags = (CTAP_AUTHDATA_ATT_CRED | CTAP_AUTHDATA_USER_PRESENT);
538 	authdata.sigcount = 0;
539 
540 	memset(&attcred_raw.aaguid, 0, sizeof(attcred_raw.aaguid));
541 	attcred_raw.id_len = htobe16(kh_len);
542 
543 	len = authdata_blob.len = sizeof(authdata) + sizeof(attcred_raw) +
544 	    kh_len + pk_blob.len;
545 	ptr = authdata_blob.ptr = calloc(1, authdata_blob.len);
546 
547 	fido_log_debug("%s: ptr=%p, len=%zu", __func__, (void *)ptr, len);
548 
549 	if (authdata_blob.ptr == NULL)
550 		goto fail;
551 
552 	if (fido_buf_write(&ptr, &len, &authdata, sizeof(authdata)) < 0 ||
553 	    fido_buf_write(&ptr, &len, &attcred_raw, sizeof(attcred_raw)) < 0 ||
554 	    fido_buf_write(&ptr, &len, kh, kh_len) < 0 ||
555 	    fido_buf_write(&ptr, &len, pk_blob.ptr, pk_blob.len) < 0) {
556 		fido_log_debug("%s: fido_buf_write", __func__);
557 		goto fail;
558 	}
559 
560 	if ((authdata_cbor = fido_blob_encode(&authdata_blob)) == NULL) {
561 		fido_log_debug("%s: fido_blob_encode", __func__);
562 		goto fail;
563 	}
564 
565 	if ((out->len = cbor_serialize_alloc(authdata_cbor, &out->ptr,
566 	    &alloc_len)) == 0) {
567 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
568 		goto fail;
569 	}
570 
571 	ok = 0;
572 fail:
573 	if (authdata_cbor)
574 		cbor_decref(&authdata_cbor);
575 
576 	fido_blob_reset(&pk_blob);
577 	fido_blob_reset(&authdata_blob);
578 
579 	return (ok);
580 }
581 
582 static int
parse_register_reply(fido_cred_t * cred,const unsigned char * reply,size_t len)583 parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len)
584 {
585 	fido_blob_t	 x5c;
586 	fido_blob_t	 sig;
587 	fido_blob_t	 ad;
588 	fido_blob_t	 stmt;
589 	uint8_t		 dummy;
590 	uint8_t		 pubkey[65];
591 	uint8_t		 kh_len = 0;
592 	uint8_t		*kh = NULL;
593 	int		 r;
594 
595 	memset(&x5c, 0, sizeof(x5c));
596 	memset(&sig, 0, sizeof(sig));
597 	memset(&ad, 0, sizeof(ad));
598 	memset(&stmt, 0, sizeof(stmt));
599 	r = FIDO_ERR_RX;
600 
601 	/* status word */
602 	if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
603 		fido_log_debug("%s: unexpected sw", __func__);
604 		goto fail;
605 	}
606 
607 	len -= 2;
608 
609 	/* reserved byte */
610 	if (fido_buf_read(&reply, &len, &dummy, sizeof(dummy)) < 0 ||
611 	    dummy != 0x05) {
612 		fido_log_debug("%s: reserved byte", __func__);
613 		goto fail;
614 	}
615 
616 	/* pubkey + key handle */
617 	if (fido_buf_read(&reply, &len, &pubkey, sizeof(pubkey)) < 0 ||
618 	    fido_buf_read(&reply, &len, &kh_len, sizeof(kh_len)) < 0 ||
619 	    (kh = calloc(1, kh_len)) == NULL ||
620 	    fido_buf_read(&reply, &len, kh, kh_len) < 0) {
621 		fido_log_debug("%s: fido_buf_read", __func__);
622 		goto fail;
623 	}
624 
625 	/* x5c + sig */
626 	if (x5c_get(&x5c, &reply, &len) < 0 ||
627 	    sig_get(&sig, &reply, &len) < 0) {
628 		fido_log_debug("%s: x5c || sig", __func__);
629 		goto fail;
630 	}
631 
632 	/* attstmt */
633 	if (encode_cred_attstmt(COSE_ES256, &x5c, &sig, &stmt) < 0) {
634 		fido_log_debug("%s: encode_cred_attstmt", __func__);
635 		goto fail;
636 	}
637 
638 	/* authdata */
639 	if (encode_cred_authdata(cred->rp.id, kh, kh_len, pubkey,
640 	    sizeof(pubkey), &ad) < 0) {
641 		fido_log_debug("%s: encode_cred_authdata", __func__);
642 		goto fail;
643 	}
644 
645 	if (fido_cred_set_fmt(cred, "fido-u2f") != FIDO_OK ||
646 	    fido_cred_set_authdata(cred, ad.ptr, ad.len) != FIDO_OK ||
647 	    fido_cred_set_attstmt(cred, stmt.ptr, stmt.len) != FIDO_OK) {
648 		fido_log_debug("%s: fido_cred_set", __func__);
649 		r = FIDO_ERR_INTERNAL;
650 		goto fail;
651 	}
652 
653 	r = FIDO_OK;
654 fail:
655 	freezero(kh, kh_len);
656 	fido_blob_reset(&x5c);
657 	fido_blob_reset(&sig);
658 	fido_blob_reset(&ad);
659 	fido_blob_reset(&stmt);
660 
661 	return (r);
662 }
663 
664 int
u2f_register(fido_dev_t * dev,fido_cred_t * cred,int * ms)665 u2f_register(fido_dev_t *dev, fido_cred_t *cred, int *ms)
666 {
667 	iso7816_apdu_t	*apdu = NULL;
668 	unsigned char	 rp_id_hash[SHA256_DIGEST_LENGTH];
669 	unsigned char	*reply = NULL;
670 	int		 reply_len;
671 	int		 found;
672 	int		 r;
673 
674 	if (cred->rk == FIDO_OPT_TRUE || cred->uv == FIDO_OPT_TRUE) {
675 		fido_log_debug("%s: rk=%d, uv=%d", __func__, cred->rk,
676 		    cred->uv);
677 		return (FIDO_ERR_UNSUPPORTED_OPTION);
678 	}
679 
680 	if (cred->type != COSE_ES256 || cred->cdh.ptr == NULL ||
681 	    cred->rp.id == NULL || cred->cdh.len != SHA256_DIGEST_LENGTH) {
682 		fido_log_debug("%s: type=%d, cdh=(%p,%zu)" , __func__,
683 		    cred->type, (void *)cred->cdh.ptr, cred->cdh.len);
684 		return (FIDO_ERR_INVALID_ARGUMENT);
685 	}
686 
687 	for (size_t i = 0; i < cred->excl.len; i++) {
688 		if ((r = key_lookup(dev, cred->rp.id, &cred->excl.ptr[i],
689 		    &found, ms)) != FIDO_OK) {
690 			fido_log_debug("%s: key_lookup", __func__);
691 			return (r);
692 		}
693 		if (found) {
694 			if ((r = send_dummy_register(dev, ms)) != FIDO_OK) {
695 				fido_log_debug("%s: send_dummy_register",
696 				    __func__);
697 				return (r);
698 			}
699 			return (FIDO_ERR_CREDENTIAL_EXCLUDED);
700 		}
701 	}
702 
703 	memset(&rp_id_hash, 0, sizeof(rp_id_hash));
704 
705 	if (SHA256((const void *)cred->rp.id, strlen(cred->rp.id),
706 	    rp_id_hash) != rp_id_hash) {
707 		fido_log_debug("%s: sha256", __func__);
708 		return (FIDO_ERR_INTERNAL);
709 	}
710 
711 	if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
712 	    SHA256_DIGEST_LENGTH)) == NULL ||
713 	    iso7816_add(apdu, cred->cdh.ptr, cred->cdh.len) < 0 ||
714 	    iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
715 		fido_log_debug("%s: iso7816", __func__);
716 		r = FIDO_ERR_INTERNAL;
717 		goto fail;
718 	}
719 
720 	if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
721 		fido_log_debug("%s: malloc", __func__);
722 		r = FIDO_ERR_INTERNAL;
723 		goto fail;
724 	}
725 
726 	do {
727 		if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
728 		    iso7816_len(apdu), ms) < 0) {
729 			fido_log_debug("%s: fido_tx", __func__);
730 			r = FIDO_ERR_TX;
731 			goto fail;
732 		}
733 		if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply,
734 		    FIDO_MAXMSG, ms)) < 2) {
735 			fido_log_debug("%s: fido_rx", __func__);
736 			r = FIDO_ERR_RX;
737 			goto fail;
738 		}
739 		if (delay_ms(U2F_PACE_MS, ms) != 0) {
740 			fido_log_debug("%s: delay_ms", __func__);
741 			r = FIDO_ERR_RX;
742 			goto fail;
743 		}
744 	} while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
745 
746 	if ((r = parse_register_reply(cred, reply,
747 	    (size_t)reply_len)) != FIDO_OK) {
748 		fido_log_debug("%s: parse_register_reply", __func__);
749 		goto fail;
750 	}
751 fail:
752 	iso7816_free(&apdu);
753 	freezero(reply, FIDO_MAXMSG);
754 
755 	return (r);
756 }
757 
758 static int
u2f_authenticate_single(fido_dev_t * dev,const fido_blob_t * key_id,fido_assert_t * fa,size_t idx,int * ms)759 u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id,
760     fido_assert_t *fa, size_t idx, int *ms)
761 {
762 	fido_blob_t	sig;
763 	fido_blob_t	ad;
764 	int		found;
765 	int		r;
766 
767 	memset(&sig, 0, sizeof(sig));
768 	memset(&ad, 0, sizeof(ad));
769 
770 	if ((r = key_lookup(dev, fa->rp_id, key_id, &found, ms)) != FIDO_OK) {
771 		fido_log_debug("%s: key_lookup", __func__);
772 		goto fail;
773 	}
774 
775 	if (!found) {
776 		fido_log_debug("%s: not found", __func__);
777 		r = FIDO_ERR_CREDENTIAL_EXCLUDED;
778 		goto fail;
779 	}
780 
781 	if (fido_blob_set(&fa->stmt[idx].id, key_id->ptr, key_id->len) < 0) {
782 		fido_log_debug("%s: fido_blob_set", __func__);
783 		r = FIDO_ERR_INTERNAL;
784 		goto fail;
785 	}
786 
787 	if (fa->up == FIDO_OPT_FALSE) {
788 		fido_log_debug("%s: checking for key existence only", __func__);
789 		r = FIDO_ERR_USER_PRESENCE_REQUIRED;
790 		goto fail;
791 	}
792 
793 	if ((r = do_auth(dev, &fa->cdh, fa->rp_id, key_id, &sig, &ad,
794 	    ms)) != FIDO_OK) {
795 		fido_log_debug("%s: do_auth", __func__);
796 		goto fail;
797 	}
798 
799 	if (fido_assert_set_authdata(fa, idx, ad.ptr, ad.len) != FIDO_OK ||
800 	    fido_assert_set_sig(fa, idx, sig.ptr, sig.len) != FIDO_OK) {
801 		fido_log_debug("%s: fido_assert_set", __func__);
802 		r = FIDO_ERR_INTERNAL;
803 		goto fail;
804 	}
805 
806 	r = FIDO_OK;
807 fail:
808 	fido_blob_reset(&sig);
809 	fido_blob_reset(&ad);
810 
811 	return (r);
812 }
813 
814 int
u2f_authenticate(fido_dev_t * dev,fido_assert_t * fa,int * ms)815 u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int *ms)
816 {
817 	size_t	nfound = 0;
818 	size_t	nauth_ok = 0;
819 	int	r;
820 
821 	if (fa->uv == FIDO_OPT_TRUE || fa->allow_list.ptr == NULL) {
822 		fido_log_debug("%s: uv=%d, allow_list=%p", __func__, fa->uv,
823 		    (void *)fa->allow_list.ptr);
824 		return (FIDO_ERR_UNSUPPORTED_OPTION);
825 	}
826 
827 	if ((r = fido_assert_set_count(fa, fa->allow_list.len)) != FIDO_OK) {
828 		fido_log_debug("%s: fido_assert_set_count", __func__);
829 		return (r);
830 	}
831 
832 	for (size_t i = 0; i < fa->allow_list.len; i++) {
833 		switch ((r = u2f_authenticate_single(dev,
834 		    &fa->allow_list.ptr[i], fa, nfound, ms))) {
835 		case FIDO_OK:
836 			nauth_ok++;
837 			FALLTHROUGH
838 		case FIDO_ERR_USER_PRESENCE_REQUIRED:
839 			nfound++;
840 			break;
841 		default:
842 			if (r != FIDO_ERR_CREDENTIAL_EXCLUDED) {
843 				fido_log_debug("%s: u2f_authenticate_single",
844 				    __func__);
845 				return (r);
846 			}
847 			/* ignore credentials that don't exist */
848 		}
849 	}
850 
851 	fa->stmt_len = nfound;
852 
853 	if (nfound == 0)
854 		return (FIDO_ERR_NO_CREDENTIALS);
855 	if (nauth_ok == 0)
856 		return (FIDO_ERR_USER_PRESENCE_REQUIRED);
857 
858 	return (FIDO_OK);
859 }
860 
861 int
u2f_get_touch_begin(fido_dev_t * dev,int * ms)862 u2f_get_touch_begin(fido_dev_t *dev, int *ms)
863 {
864 	iso7816_apdu_t	*apdu = NULL;
865 	const char	*clientdata = FIDO_DUMMY_CLIENTDATA;
866 	const char	*rp_id = FIDO_DUMMY_RP_ID;
867 	unsigned char	*reply = NULL;
868 	unsigned char	 clientdata_hash[SHA256_DIGEST_LENGTH];
869 	unsigned char	 rp_id_hash[SHA256_DIGEST_LENGTH];
870 	int		 r;
871 
872 	memset(&clientdata_hash, 0, sizeof(clientdata_hash));
873 	memset(&rp_id_hash, 0, sizeof(rp_id_hash));
874 
875 	if (SHA256((const void *)clientdata, strlen(clientdata),
876 	    clientdata_hash) != clientdata_hash || SHA256((const void *)rp_id,
877 	    strlen(rp_id), rp_id_hash) != rp_id_hash) {
878 		fido_log_debug("%s: sha256", __func__);
879 		return (FIDO_ERR_INTERNAL);
880 	}
881 
882 	if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
883 	    SHA256_DIGEST_LENGTH)) == NULL ||
884 	    iso7816_add(apdu, clientdata_hash, sizeof(clientdata_hash)) < 0 ||
885 	    iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
886 		fido_log_debug("%s: iso7816", __func__);
887 		r = FIDO_ERR_INTERNAL;
888 		goto fail;
889 	}
890 
891 	if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
892 		fido_log_debug("%s: malloc", __func__);
893 		r =  FIDO_ERR_INTERNAL;
894 		goto fail;
895 	}
896 
897 	if (dev->attr.flags & FIDO_CAP_WINK) {
898 		fido_tx(dev, CTAP_CMD_WINK, NULL, 0, ms);
899 		fido_rx(dev, CTAP_CMD_WINK, reply, FIDO_MAXMSG, ms);
900 	}
901 
902 	if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
903 	    iso7816_len(apdu), ms) < 0) {
904 		fido_log_debug("%s: fido_tx", __func__);
905 		r = FIDO_ERR_TX;
906 		goto fail;
907 	}
908 
909 	r = FIDO_OK;
910 fail:
911 	iso7816_free(&apdu);
912 	freezero(reply, FIDO_MAXMSG);
913 
914 	return (r);
915 }
916 
917 int
u2f_get_touch_status(fido_dev_t * dev,int * touched,int * ms)918 u2f_get_touch_status(fido_dev_t *dev, int *touched, int *ms)
919 {
920 	unsigned char	*reply;
921 	int		 reply_len;
922 	int		 r;
923 
924 	if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
925 		fido_log_debug("%s: malloc", __func__);
926 		r =  FIDO_ERR_INTERNAL;
927 		goto out;
928 	}
929 
930 	if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG,
931 	    ms)) < 2) {
932 		fido_log_debug("%s: fido_rx", __func__);
933 		r = FIDO_OK; /* ignore */
934 		goto out;
935 	}
936 
937 	switch ((reply[reply_len - 2] << 8) | reply[reply_len - 1]) {
938 	case SW_CONDITIONS_NOT_SATISFIED:
939 		if ((r = u2f_get_touch_begin(dev, ms)) != FIDO_OK) {
940 			fido_log_debug("%s: u2f_get_touch_begin", __func__);
941 			goto out;
942 		}
943 		*touched = 0;
944 		break;
945 	case SW_NO_ERROR:
946 		*touched = 1;
947 		break;
948 	default:
949 		fido_log_debug("%s: unexpected sw", __func__);
950 		r = FIDO_ERR_RX;
951 		goto out;
952 	}
953 
954 	r = FIDO_OK;
955 out:
956 	freezero(reply, FIDO_MAXMSG);
957 
958 	return (r);
959 }
960