summaryrefslogtreecommitdiff
path: root/src/pin.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pin.c')
-rw-r--r--src/pin.c428
1 files changed, 428 insertions, 0 deletions
diff --git a/src/pin.c b/src/pin.c
new file mode 100644
index 0000000..1ed555c
--- /dev/null
+++ b/src/pin.c
@@ -0,0 +1,428 @@
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#include "fido.h"
9#include "fido/es256.h"
10
11static int
12parse_pintoken(const cbor_item_t *key, const cbor_item_t *val, void *arg)
13{
14 fido_blob_t *token = arg;
15
16 if (cbor_isa_uint(key) == false ||
17 cbor_int_get_width(key) != CBOR_INT_8 ||
18 cbor_get_uint8(key) != 2) {
19 fido_log_debug("%s: cbor type", __func__);
20 return (0); /* ignore */
21 }
22
23 return (fido_blob_decode(val, token));
24}
25
26static int
27fido_dev_get_pin_token_tx(fido_dev_t *dev, const char *pin,
28 const fido_blob_t *ecdh, const es256_pk_t *pk)
29{
30 fido_blob_t f;
31 fido_blob_t *p = NULL;
32 cbor_item_t *argv[6];
33 int r;
34
35 memset(&f, 0, sizeof(f));
36 memset(argv, 0, sizeof(argv));
37
38 if ((p = fido_blob_new()) == NULL || fido_blob_set(p,
39 (const unsigned char *)pin, strlen(pin)) < 0) {
40 fido_log_debug("%s: fido_blob_set", __func__);
41 r = FIDO_ERR_INVALID_ARGUMENT;
42 goto fail;
43 }
44
45 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
46 (argv[1] = cbor_build_uint8(5)) == NULL ||
47 (argv[2] = es256_pk_encode(pk, 0)) == NULL ||
48 (argv[5] = cbor_encode_pin_hash_enc(ecdh, p)) == NULL) {
49 fido_log_debug("%s: cbor encode", __func__);
50 r = FIDO_ERR_INTERNAL;
51 goto fail;
52 }
53
54 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 6, &f) < 0 ||
55 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
56 fido_log_debug("%s: fido_tx", __func__);
57 r = FIDO_ERR_TX;
58 goto fail;
59 }
60
61 r = FIDO_OK;
62fail:
63 cbor_vector_free(argv, nitems(argv));
64 fido_blob_free(&p);
65 free(f.ptr);
66
67 return (r);
68}
69
70static int
71fido_dev_get_pin_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh,
72 fido_blob_t *token, int ms)
73{
74 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
75 fido_blob_t *aes_token = NULL;
76 unsigned char reply[2048];
77 int reply_len;
78 int r;
79
80 if ((aes_token = fido_blob_new()) == NULL) {
81 r = FIDO_ERR_INTERNAL;
82 goto fail;
83 }
84
85 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
86 fido_log_debug("%s: fido_rx", __func__);
87 r = FIDO_ERR_RX;
88 goto fail;
89 }
90
91 if ((r = cbor_parse_reply(reply, (size_t)reply_len, aes_token,
92 parse_pintoken)) != FIDO_OK) {
93 fido_log_debug("%s: parse_pintoken", __func__);
94 goto fail;
95 }
96
97 if (aes256_cbc_dec(ecdh, aes_token, token) < 0) {
98 fido_log_debug("%s: aes256_cbc_dec", __func__);
99 r = FIDO_ERR_RX;
100 goto fail;
101 }
102
103 r = FIDO_OK;
104fail:
105 fido_blob_free(&aes_token);
106
107 return (r);
108}
109
110static int
111fido_dev_get_pin_token_wait(fido_dev_t *dev, const char *pin,
112 const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token, int ms)
113{
114 int r;
115
116 if ((r = fido_dev_get_pin_token_tx(dev, pin, ecdh, pk)) != FIDO_OK ||
117 (r = fido_dev_get_pin_token_rx(dev, ecdh, token, ms)) != FIDO_OK)
118 return (r);
119
120 return (FIDO_OK);
121}
122
123int
124fido_dev_get_pin_token(fido_dev_t *dev, const char *pin,
125 const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token)
126{
127 return (fido_dev_get_pin_token_wait(dev, pin, ecdh, pk, token, -1));
128}
129
130static int
131pad64(const char *pin, fido_blob_t **ppin)
132{
133 size_t pin_len;
134 size_t ppin_len;
135
136 pin_len = strlen(pin);
137 if (pin_len < 4 || pin_len > 255) {
138 fido_log_debug("%s: invalid pin length", __func__);
139 return (FIDO_ERR_PIN_POLICY_VIOLATION);
140 }
141
142 if ((*ppin = fido_blob_new()) == NULL)
143 return (FIDO_ERR_INTERNAL);
144
145 ppin_len = (pin_len + 63) & ~63;
146 if (ppin_len < pin_len || ((*ppin)->ptr = calloc(1, ppin_len)) == NULL) {
147 fido_blob_free(ppin);
148 return (FIDO_ERR_INTERNAL);
149 }
150
151 memcpy((*ppin)->ptr, pin, pin_len);
152 (*ppin)->len = ppin_len;
153
154 return (FIDO_OK);
155}
156
157static int
158fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin)
159{
160 fido_blob_t f;
161 fido_blob_t *ppin = NULL;
162 fido_blob_t *ecdh = NULL;
163 fido_blob_t *opin = NULL;
164 cbor_item_t *argv[6];
165 es256_pk_t *pk = NULL;
166 int r;
167
168 memset(&f, 0, sizeof(f));
169 memset(argv, 0, sizeof(argv));
170
171 if ((opin = fido_blob_new()) == NULL || fido_blob_set(opin,
172 (const unsigned char *)oldpin, strlen(oldpin)) < 0) {
173 fido_log_debug("%s: fido_blob_set", __func__);
174 r = FIDO_ERR_INVALID_ARGUMENT;
175 goto fail;
176 }
177
178 if ((r = pad64(pin, &ppin)) != FIDO_OK) {
179 fido_log_debug("%s: pad64", __func__);
180 goto fail;
181 }
182
183 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
184 fido_log_debug("%s: fido_do_ecdh", __func__);
185 goto fail;
186 }
187
188 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
189 (argv[1] = cbor_build_uint8(4)) == NULL ||
190 (argv[2] = es256_pk_encode(pk, 0)) == NULL ||
191 (argv[3] = cbor_encode_change_pin_auth(ecdh, ppin, opin)) == NULL ||
192 (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL ||
193 (argv[5] = cbor_encode_pin_hash_enc(ecdh, opin)) == NULL) {
194 fido_log_debug("%s: cbor encode", __func__);
195 r = FIDO_ERR_INTERNAL;
196 goto fail;
197 }
198
199 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 6, &f) < 0 ||
200 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
201 fido_log_debug("%s: fido_tx", __func__);
202 r = FIDO_ERR_TX;
203 goto fail;
204 }
205
206 r = FIDO_OK;
207fail:
208 cbor_vector_free(argv, nitems(argv));
209 es256_pk_free(&pk);
210 fido_blob_free(&ppin);
211 fido_blob_free(&ecdh);
212 fido_blob_free(&opin);
213 free(f.ptr);
214
215 return (r);
216
217}
218
219static int
220fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin)
221{
222 fido_blob_t f;
223 fido_blob_t *ppin = NULL;
224 fido_blob_t *ecdh = NULL;
225 cbor_item_t *argv[5];
226 es256_pk_t *pk = NULL;
227 int r;
228
229 memset(&f, 0, sizeof(f));
230 memset(argv, 0, sizeof(argv));
231
232 if ((r = pad64(pin, &ppin)) != FIDO_OK) {
233 fido_log_debug("%s: pad64", __func__);
234 goto fail;
235 }
236
237 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
238 fido_log_debug("%s: fido_do_ecdh", __func__);
239 goto fail;
240 }
241
242 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
243 (argv[1] = cbor_build_uint8(3)) == NULL ||
244 (argv[2] = es256_pk_encode(pk, 0)) == NULL ||
245 (argv[3] = cbor_encode_set_pin_auth(ecdh, ppin)) == NULL ||
246 (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL) {
247 fido_log_debug("%s: cbor encode", __func__);
248 r = FIDO_ERR_INTERNAL;
249 goto fail;
250 }
251
252 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 5, &f) < 0 ||
253 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
254 fido_log_debug("%s: fido_tx", __func__);
255 r = FIDO_ERR_TX;
256 goto fail;
257 }
258
259 r = FIDO_OK;
260fail:
261 cbor_vector_free(argv, nitems(argv));
262 es256_pk_free(&pk);
263 fido_blob_free(&ppin);
264 fido_blob_free(&ecdh);
265 free(f.ptr);
266
267 return (r);
268}
269
270static int
271fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin,
272 int ms)
273{
274 int r;
275
276 if (oldpin != NULL) {
277 if ((r = fido_dev_change_pin_tx(dev, pin, oldpin)) != FIDO_OK) {
278 fido_log_debug("%s: fido_dev_change_pin_tx", __func__);
279 return (r);
280 }
281 } else {
282 if ((r = fido_dev_set_pin_tx(dev, pin)) != FIDO_OK) {
283 fido_log_debug("%s: fido_dev_set_pin_tx", __func__);
284 return (r);
285 }
286 }
287
288 if ((r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
289 fido_log_debug("%s: fido_rx_cbor_status", __func__);
290 return (r);
291 }
292
293 return (FIDO_OK);
294}
295
296int
297fido_dev_set_pin(fido_dev_t *dev, const char *pin, const char *oldpin)
298{
299 return (fido_dev_set_pin_wait(dev, pin, oldpin, -1));
300}
301
302static int
303parse_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
304{
305 int *retries = arg;
306 uint64_t n;
307
308 if (cbor_isa_uint(key) == false ||
309 cbor_int_get_width(key) != CBOR_INT_8 ||
310 cbor_get_uint8(key) != 3) {
311 fido_log_debug("%s: cbor type", __func__);
312 return (0); /* ignore */
313 }
314
315 if (cbor_decode_uint64(val, &n) < 0 || n > INT_MAX) {
316 fido_log_debug("%s: cbor_decode_uint64", __func__);
317 return (-1);
318 }
319
320 *retries = (int)n;
321
322 return (0);
323}
324
325static int
326fido_dev_get_retry_count_tx(fido_dev_t *dev)
327{
328 fido_blob_t f;
329 cbor_item_t *argv[2];
330 int r;
331
332 memset(&f, 0, sizeof(f));
333 memset(argv, 0, sizeof(argv));
334
335 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
336 (argv[1] = cbor_build_uint8(1)) == NULL) {
337 r = FIDO_ERR_INTERNAL;
338 goto fail;
339 }
340
341 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 2, &f) < 0 ||
342 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
343 fido_log_debug("%s: fido_tx", __func__);
344 r = FIDO_ERR_TX;
345 goto fail;
346 }
347
348 r = FIDO_OK;
349fail:
350 cbor_vector_free(argv, nitems(argv));
351 free(f.ptr);
352
353 return (r);
354}
355
356static int
357fido_dev_get_retry_count_rx(fido_dev_t *dev, int *retries, int ms)
358{
359 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
360 unsigned char reply[512];
361 int reply_len;
362 int r;
363
364 *retries = 0;
365
366 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
367 fido_log_debug("%s: fido_rx", __func__);
368 return (FIDO_ERR_RX);
369 }
370
371 if ((r = cbor_parse_reply(reply, (size_t)reply_len, retries,
372 parse_retry_count)) != FIDO_OK) {
373 fido_log_debug("%s: parse_retry_count", __func__);
374 return (r);
375 }
376
377 return (FIDO_OK);
378}
379
380static int
381fido_dev_get_retry_count_wait(fido_dev_t *dev, int *retries, int ms)
382{
383 int r;
384
385 if ((r = fido_dev_get_retry_count_tx(dev)) != FIDO_OK ||
386 (r = fido_dev_get_retry_count_rx(dev, retries, ms)) != FIDO_OK)
387 return (r);
388
389 return (FIDO_OK);
390}
391
392int
393fido_dev_get_retry_count(fido_dev_t *dev, int *retries)
394{
395 return (fido_dev_get_retry_count_wait(dev, retries, -1));
396}
397
398int
399cbor_add_pin_params(fido_dev_t *dev, const fido_blob_t *hmac_data,
400 const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin,
401 cbor_item_t **auth, cbor_item_t **opt)
402{
403 fido_blob_t *token = NULL;
404 int r;
405
406 if ((token = fido_blob_new()) == NULL) {
407 r = FIDO_ERR_INTERNAL;
408 goto fail;
409 }
410
411 if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) {
412 fido_log_debug("%s: fido_dev_get_pin_token", __func__);
413 goto fail;
414 }
415
416 if ((*auth = cbor_encode_pin_auth(token, hmac_data)) == NULL ||
417 (*opt = cbor_encode_pin_opt()) == NULL) {
418 fido_log_debug("%s: cbor encode", __func__);
419 r = FIDO_ERR_INTERNAL;
420 goto fail;
421 }
422
423 r = FIDO_OK;
424fail:
425 fido_blob_free(&token);
426
427 return (r);
428}