xref: /openbsd/lib/libfido2/src/pin.c (revision 771fbea0)
1 /*
2  * Copyright (c) 2018 Yubico AB. All rights reserved.
3  * Use of this source code is governed by a BSD-style
4  * license that can be found in the LICENSE file.
5  */
6 
7 #include <string.h>
8 
9 #include "fido.h"
10 #include "fido/es256.h"
11 
12 static int
13 parse_pintoken(const cbor_item_t *key, const cbor_item_t *val, void *arg)
14 {
15 	fido_blob_t *token = arg;
16 
17 	if (cbor_isa_uint(key) == false ||
18 	    cbor_int_get_width(key) != CBOR_INT_8 ||
19 	    cbor_get_uint8(key) != 2) {
20 		fido_log_debug("%s: cbor type", __func__);
21 		return (0); /* ignore */
22 	}
23 
24 	return (fido_blob_decode(val, token));
25 }
26 
27 #ifdef FIDO_UVTOKEN
28 static int
29 parse_uvtoken(const cbor_item_t *key, const cbor_item_t *val, void *arg)
30 {
31 	return (parse_pintoken(key, val, arg));
32 }
33 #endif /* FIDO_UVTOKEN */
34 
35 static int
36 fido_dev_get_pin_token_tx(fido_dev_t *dev, const char *pin,
37     const fido_blob_t *ecdh, const es256_pk_t *pk)
38 {
39 	fido_blob_t	 f;
40 	fido_blob_t	*p = NULL;
41 	cbor_item_t	*argv[6];
42 	int		 r;
43 
44 	memset(&f, 0, sizeof(f));
45 	memset(argv, 0, sizeof(argv));
46 
47 	if ((p = fido_blob_new()) == NULL || fido_blob_set(p,
48 	    (const unsigned char *)pin, strlen(pin)) < 0) {
49 		fido_log_debug("%s: fido_blob_set", __func__);
50 		r = FIDO_ERR_INVALID_ARGUMENT;
51 		goto fail;
52 	}
53 
54 	if ((argv[0] = cbor_build_uint8(1)) == NULL ||
55 	    (argv[1] = cbor_build_uint8(5)) == NULL ||
56 	    (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
57 	    (argv[5] = cbor_encode_pin_hash_enc(ecdh, p)) == NULL) {
58 		fido_log_debug("%s: cbor encode", __func__);
59 		r = FIDO_ERR_INTERNAL;
60 		goto fail;
61 	}
62 
63 	if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
64 	    &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
65 		fido_log_debug("%s: fido_tx", __func__);
66 		r = FIDO_ERR_TX;
67 		goto fail;
68 	}
69 
70 	r = FIDO_OK;
71 fail:
72 	cbor_vector_free(argv, nitems(argv));
73 	fido_blob_free(&p);
74 	free(f.ptr);
75 
76 	return (r);
77 }
78 
79 #ifdef FIDO_UVTOKEN
80 static int
81 fido_dev_get_uv_token_tx(fido_dev_t *dev, const es256_pk_t *pk)
82 {
83 	fido_blob_t	 f;
84 	cbor_item_t	*argv[3];
85 	int		 r;
86 
87 	memset(&f, 0, sizeof(f));
88 	memset(argv, 0, sizeof(argv));
89 
90 	if ((argv[0] = cbor_build_uint8(1)) == NULL ||
91 	    (argv[1] = cbor_build_uint8(6)) == NULL ||
92 	    (argv[2] = es256_pk_encode(pk, 1)) == NULL) {
93 		fido_log_debug("%s: cbor encode", __func__);
94 		r = FIDO_ERR_INTERNAL;
95 		goto fail;
96 	}
97 
98 	if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
99 	    &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
100 		fido_log_debug("%s:  fido_tx", __func__);
101 		r = FIDO_ERR_TX;
102 		goto fail;
103 	}
104 
105 	r = FIDO_OK;
106 fail:
107 	cbor_vector_free(argv, nitems(argv));
108 	free(f.ptr);
109 
110 	return (r);
111 }
112 #endif /* FIDO_UVTOKEN */
113 
114 static int
115 fido_dev_get_pin_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh,
116     fido_blob_t *token, int ms)
117 {
118 	fido_blob_t	*aes_token = NULL;
119 	unsigned char	 reply[FIDO_MAXMSG];
120 	int		 reply_len;
121 	int		 r;
122 
123 	if ((aes_token = fido_blob_new()) == NULL) {
124 		r = FIDO_ERR_INTERNAL;
125 		goto fail;
126 	}
127 
128 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
129 	    ms)) < 0) {
130 		fido_log_debug("%s: fido_rx", __func__);
131 		r = FIDO_ERR_RX;
132 		goto fail;
133 	}
134 
135 	if ((r = cbor_parse_reply(reply, (size_t)reply_len, aes_token,
136 	    parse_pintoken)) != FIDO_OK) {
137 		fido_log_debug("%s: parse_pintoken", __func__);
138 		goto fail;
139 	}
140 
141 	if  (aes256_cbc_dec(ecdh, aes_token, token) < 0) {
142 		fido_log_debug("%s: aes256_cbc_dec", __func__);
143 		r = FIDO_ERR_RX;
144 		goto fail;
145 	}
146 
147 	r = FIDO_OK;
148 fail:
149 	fido_blob_free(&aes_token);
150 
151 	return (r);
152 }
153 
154 #ifdef FIDO_UVTOKEN
155 static int
156 fido_dev_get_uv_token_rx(fido_dev_t *dev, const  fido_blob_t *ecdh,
157     fido_blob_t *token, int ms)
158 {
159 	fido_blob_t	*aes_token = NULL;
160 	unsigned char	 reply[FIDO_MAXMSG];
161 	int		 reply_len;
162 	int		 r;
163 
164 	if ((aes_token = fido_blob_new()) == NULL) {
165 		r = FIDO_ERR_INTERNAL;
166 		goto fail;
167 	}
168 
169 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
170 	    ms)) < 0) {
171 		fido_log_debug("%s: fido_rx", __func__);
172 		r = FIDO_ERR_RX;
173 		goto fail;
174 	}
175 
176 	if ((r = cbor_parse_reply(reply, (size_t)reply_len, aes_token,
177 	    parse_uvtoken)) != FIDO_OK) {
178 		fido_log_debug("%s: parse_uvtoken", __func__);
179 		goto fail;
180 	}
181 
182 	if (aes256_cbc_dec(ecdh, aes_token, token) < 0) {
183 		fido_log_debug("%s: aes256_cbc_dec", __func__);
184 		r = FIDO_ERR_RX;
185 		goto fail;
186 	}
187 
188 	r = FIDO_OK;
189 fail:
190 	fido_blob_free(&aes_token);
191 
192 	return (r);
193 }
194 #endif /* FIDO_UVTOKEN */
195 
196 static int
197 fido_dev_get_pin_token_wait(fido_dev_t *dev, const char *pin,
198     const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token, int ms)
199 {
200 	int r;
201 
202 #ifdef FIDO_UVTOKEN
203 	if (getenv("FIDO_UVTOKEN") != NULL) {
204 		if ((r = fido_dev_get_uv_token_tx(dev, pk)) != FIDO_OK ||
205 		    (r = fido_dev_get_uv_token_rx(dev, ecdh, token, ms)) != FIDO_OK)
206 			return (r);
207 	} else {
208 		if ((r = fido_dev_get_pin_token_tx(dev, pin, ecdh, pk)) != FIDO_OK ||
209 		    (r = fido_dev_get_pin_token_rx(dev, ecdh, token, ms)) != FIDO_OK)
210 			return (r);
211 	}
212 #else
213 	if ((r = fido_dev_get_pin_token_tx(dev, pin, ecdh, pk)) != FIDO_OK ||
214 	    (r = fido_dev_get_pin_token_rx(dev, ecdh, token, ms)) != FIDO_OK)
215 		return (r);
216 #endif
217 
218 	return (FIDO_OK);
219 }
220 
221 int
222 fido_dev_get_pin_token(fido_dev_t *dev, const char *pin,
223     const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token)
224 {
225 	return (fido_dev_get_pin_token_wait(dev, pin, ecdh, pk, token, -1));
226 }
227 
228 static int
229 pad64(const char *pin, fido_blob_t **ppin)
230 {
231 	size_t	pin_len;
232 	size_t	ppin_len;
233 
234 	pin_len = strlen(pin);
235 	if (pin_len < 4 || pin_len > 255) {
236 		fido_log_debug("%s: invalid pin length", __func__);
237 		return (FIDO_ERR_PIN_POLICY_VIOLATION);
238 	}
239 
240 	if ((*ppin = fido_blob_new()) == NULL)
241 		return (FIDO_ERR_INTERNAL);
242 
243 	ppin_len = (pin_len + 63U) & ~63U;
244 	if (ppin_len < pin_len || ((*ppin)->ptr = calloc(1, ppin_len)) == NULL) {
245 		fido_blob_free(ppin);
246 		return (FIDO_ERR_INTERNAL);
247 	}
248 
249 	memcpy((*ppin)->ptr, pin, pin_len);
250 	(*ppin)->len = ppin_len;
251 
252 	return (FIDO_OK);
253 }
254 
255 static int
256 fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin)
257 {
258 	fido_blob_t	 f;
259 	fido_blob_t	*ppin = NULL;
260 	fido_blob_t	*ecdh = NULL;
261 	fido_blob_t	*opin = NULL;
262 	cbor_item_t	*argv[6];
263 	es256_pk_t	*pk = NULL;
264 	int r;
265 
266 	memset(&f, 0, sizeof(f));
267 	memset(argv, 0, sizeof(argv));
268 
269 	if ((opin = fido_blob_new()) == NULL || fido_blob_set(opin,
270 	    (const unsigned char *)oldpin, strlen(oldpin)) < 0) {
271 		fido_log_debug("%s: fido_blob_set", __func__);
272 		r = FIDO_ERR_INVALID_ARGUMENT;
273 		goto fail;
274 	}
275 
276 	if ((r = pad64(pin, &ppin)) != FIDO_OK) {
277 		fido_log_debug("%s: pad64", __func__);
278 		goto fail;
279 	}
280 
281 	if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
282 		fido_log_debug("%s: fido_do_ecdh", __func__);
283 		goto fail;
284 	}
285 
286 	if ((argv[0] = cbor_build_uint8(1)) == NULL ||
287 	    (argv[1] = cbor_build_uint8(4)) == NULL ||
288 	    (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
289 	    (argv[3] = cbor_encode_change_pin_auth(ecdh, ppin, opin)) == NULL ||
290 	    (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL ||
291 	    (argv[5] = cbor_encode_pin_hash_enc(ecdh, opin)) == NULL) {
292 		fido_log_debug("%s: cbor encode", __func__);
293 		r = FIDO_ERR_INTERNAL;
294 		goto fail;
295 	}
296 
297 	if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
298 	    &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
299 		fido_log_debug("%s: fido_tx", __func__);
300 		r = FIDO_ERR_TX;
301 		goto fail;
302 	}
303 
304 	r = FIDO_OK;
305 fail:
306 	cbor_vector_free(argv, nitems(argv));
307 	es256_pk_free(&pk);
308 	fido_blob_free(&ppin);
309 	fido_blob_free(&ecdh);
310 	fido_blob_free(&opin);
311 	free(f.ptr);
312 
313 	return (r);
314 
315 }
316 
317 static int
318 fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin)
319 {
320 	fido_blob_t	 f;
321 	fido_blob_t	*ppin = NULL;
322 	fido_blob_t	*ecdh = NULL;
323 	cbor_item_t	*argv[5];
324 	es256_pk_t	*pk = NULL;
325 	int		 r;
326 
327 	memset(&f, 0, sizeof(f));
328 	memset(argv, 0, sizeof(argv));
329 
330 	if ((r = pad64(pin, &ppin)) != FIDO_OK) {
331 		fido_log_debug("%s: pad64", __func__);
332 		goto fail;
333 	}
334 
335 	if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
336 		fido_log_debug("%s: fido_do_ecdh", __func__);
337 		goto fail;
338 	}
339 
340 	if ((argv[0] = cbor_build_uint8(1)) == NULL ||
341 	    (argv[1] = cbor_build_uint8(3)) == NULL ||
342 	    (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
343 	    (argv[3] = cbor_encode_set_pin_auth(ecdh, ppin)) == NULL ||
344 	    (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL) {
345 		fido_log_debug("%s: cbor encode", __func__);
346 		r = FIDO_ERR_INTERNAL;
347 		goto fail;
348 	}
349 
350 	if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
351 	    &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
352 		fido_log_debug("%s: fido_tx", __func__);
353 		r = FIDO_ERR_TX;
354 		goto fail;
355 	}
356 
357 	r = FIDO_OK;
358 fail:
359 	cbor_vector_free(argv, nitems(argv));
360 	es256_pk_free(&pk);
361 	fido_blob_free(&ppin);
362 	fido_blob_free(&ecdh);
363 	free(f.ptr);
364 
365 	return (r);
366 }
367 
368 static int
369 fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin,
370     int ms)
371 {
372 	int r;
373 
374 	if (oldpin != NULL) {
375 		if ((r = fido_dev_change_pin_tx(dev, pin, oldpin)) != FIDO_OK) {
376 			fido_log_debug("%s: fido_dev_change_pin_tx", __func__);
377 			return (r);
378 		}
379 	} else {
380 		if ((r = fido_dev_set_pin_tx(dev, pin)) != FIDO_OK) {
381 			fido_log_debug("%s: fido_dev_set_pin_tx", __func__);
382 			return (r);
383 		}
384 	}
385 
386 	if ((r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
387 		fido_log_debug("%s: fido_rx_cbor_status", __func__);
388 		return (r);
389 	}
390 
391 	return (FIDO_OK);
392 }
393 
394 int
395 fido_dev_set_pin(fido_dev_t *dev, const char *pin, const char *oldpin)
396 {
397 	return (fido_dev_set_pin_wait(dev, pin, oldpin, -1));
398 }
399 
400 static int
401 parse_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
402 {
403 	int		*retries = arg;
404 	uint64_t	 n;
405 
406 	if (cbor_isa_uint(key) == false ||
407 	    cbor_int_get_width(key) != CBOR_INT_8 ||
408 	    cbor_get_uint8(key) != 3) {
409 		fido_log_debug("%s: cbor type", __func__);
410 		return (0); /* ignore */
411 	}
412 
413 	if (cbor_decode_uint64(val, &n) < 0 || n > INT_MAX) {
414 		fido_log_debug("%s: cbor_decode_uint64", __func__);
415 		return (-1);
416 	}
417 
418 	*retries = (int)n;
419 
420 	return (0);
421 }
422 
423 static int
424 fido_dev_get_retry_count_tx(fido_dev_t *dev)
425 {
426 	fido_blob_t	 f;
427 	cbor_item_t	*argv[2];
428 	int		 r;
429 
430 	memset(&f, 0, sizeof(f));
431 	memset(argv, 0, sizeof(argv));
432 
433 	if ((argv[0] = cbor_build_uint8(1)) == NULL ||
434 	    (argv[1] = cbor_build_uint8(1)) == NULL) {
435 		r = FIDO_ERR_INTERNAL;
436 		goto fail;
437 	}
438 
439 	if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
440 	    &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
441 		fido_log_debug("%s: fido_tx", __func__);
442 		r = FIDO_ERR_TX;
443 		goto fail;
444 	}
445 
446 	r = FIDO_OK;
447 fail:
448 	cbor_vector_free(argv, nitems(argv));
449 	free(f.ptr);
450 
451 	return (r);
452 }
453 
454 static int
455 fido_dev_get_retry_count_rx(fido_dev_t *dev, int *retries, int ms)
456 {
457 	unsigned char	reply[FIDO_MAXMSG];
458 	int		reply_len;
459 	int		r;
460 
461 	*retries = 0;
462 
463 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
464 	    ms)) < 0) {
465 		fido_log_debug("%s: fido_rx", __func__);
466 		return (FIDO_ERR_RX);
467 	}
468 
469 	if ((r = cbor_parse_reply(reply, (size_t)reply_len, retries,
470 	    parse_retry_count)) != FIDO_OK) {
471 		fido_log_debug("%s: parse_retry_count", __func__);
472 		return (r);
473 	}
474 
475 	return (FIDO_OK);
476 }
477 
478 static int
479 fido_dev_get_retry_count_wait(fido_dev_t *dev, int *retries, int ms)
480 {
481 	int r;
482 
483 	if ((r = fido_dev_get_retry_count_tx(dev)) != FIDO_OK ||
484 	    (r = fido_dev_get_retry_count_rx(dev, retries, ms)) != FIDO_OK)
485 		return (r);
486 
487 	return (FIDO_OK);
488 }
489 
490 int
491 fido_dev_get_retry_count(fido_dev_t *dev, int *retries)
492 {
493 	return (fido_dev_get_retry_count_wait(dev, retries, -1));
494 }
495 
496 int
497 cbor_add_pin_params(fido_dev_t *dev, const fido_blob_t *hmac_data,
498     const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin,
499     cbor_item_t **auth, cbor_item_t **opt)
500 {
501 	fido_blob_t	*token = NULL;
502 	int		 r;
503 
504 	if ((token = fido_blob_new()) == NULL) {
505 		r = FIDO_ERR_INTERNAL;
506 		goto fail;
507 	}
508 
509 	if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) {
510 		fido_log_debug("%s: fido_dev_get_pin_token", __func__);
511 		goto fail;
512 	}
513 
514 	if ((*auth = cbor_encode_pin_auth(token, hmac_data)) == NULL ||
515 	    (*opt = cbor_encode_pin_opt()) == NULL) {
516 		fido_log_debug("%s: cbor encode", __func__);
517 		r = FIDO_ERR_INTERNAL;
518 		goto fail;
519 	}
520 
521 	r = FIDO_OK;
522 fail:
523 	fido_blob_free(&token);
524 
525 	return (r);
526 }
527