Coverage Report

Created: 2020-09-01 07:05

/libfido2/src/pin.c
Line
Count
Source
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
2.42k
{
15
2.42k
        fido_blob_t *token = arg;
16
2.42k
17
2.42k
        if (cbor_isa_uint(key) == false ||
18
2.42k
            cbor_int_get_width(key) != CBOR_INT_8 ||
19
2.42k
            cbor_get_uint8(key) != 2) {
20
756
                fido_log_debug("%s: cbor type", __func__);
21
756
                return (0); /* ignore */
22
756
        }
23
1.66k
24
1.66k
        return (fido_blob_decode(val, token));
25
1.66k
}
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
2.52k
{
39
2.52k
        fido_blob_t      f;
40
2.52k
        fido_blob_t     *p = NULL;
41
2.52k
        cbor_item_t     *argv[6];
42
2.52k
        int              r;
43
2.52k
44
2.52k
        memset(&f, 0, sizeof(f));
45
2.52k
        memset(argv, 0, sizeof(argv));
46
2.52k
47
2.52k
        if ((p = fido_blob_new()) == NULL || fido_blob_set(p,
48
2.51k
            (const unsigned char *)pin, strlen(pin)) < 0) {
49
26
                fido_log_debug("%s: fido_blob_set", __func__);
50
26
                r = FIDO_ERR_INVALID_ARGUMENT;
51
26
                goto fail;
52
26
        }
53
2.49k
54
2.49k
        if ((argv[0] = cbor_build_uint8(1)) == NULL ||
55
2.49k
            (argv[1] = cbor_build_uint8(5)) == NULL ||
56
2.49k
            (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
57
2.49k
            (argv[5] = cbor_encode_pin_hash_enc(ecdh, p)) == NULL) {
58
156
                fido_log_debug("%s: cbor encode", __func__);
59
156
                r = FIDO_ERR_INTERNAL;
60
156
                goto fail;
61
156
        }
62
2.33k
63
2.33k
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
64
2.33k
            &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
65
31
                fido_log_debug("%s: fido_tx", __func__);
66
31
                r = FIDO_ERR_TX;
67
31
                goto fail;
68
31
        }
69
2.30k
70
2.30k
        r = FIDO_OK;
71
2.52k
fail:
72
2.52k
        cbor_vector_free(argv, nitems(argv));
73
2.52k
        fido_blob_free(&p);
74
2.52k
        free(f.ptr);
75
2.52k
76
2.52k
        return (r);
77
2.30k
}
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
2.30k
{
118
2.30k
        fido_blob_t     *aes_token = NULL;
119
2.30k
        unsigned char    reply[FIDO_MAXMSG];
120
2.30k
        int              reply_len;
121
2.30k
        int              r;
122
2.30k
123
2.30k
        if ((aes_token = fido_blob_new()) == NULL) {
124
6
                r = FIDO_ERR_INTERNAL;
125
6
                goto fail;
126
6
        }
127
2.30k
128
2.30k
        if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
129
2.30k
            ms)) < 0) {
130
93
                fido_log_debug("%s: fido_rx", __func__);
131
93
                r = FIDO_ERR_RX;
132
93
                goto fail;
133
93
        }
134
2.20k
135
2.20k
        if ((r = cbor_parse_reply(reply, (size_t)reply_len, aes_token,
136
2.20k
            parse_pintoken)) != FIDO_OK) {
137
130
                fido_log_debug("%s: parse_pintoken", __func__);
138
130
                goto fail;
139
130
        }
140
2.07k
141
2.07k
        if  (aes256_cbc_dec(ecdh, aes_token, token) < 0) {
142
53
                fido_log_debug("%s: aes256_cbc_dec", __func__);
143
53
                r = FIDO_ERR_RX;
144
53
                goto fail;
145
53
        }
146
2.02k
147
2.02k
        r = FIDO_OK;
148
2.30k
fail:
149
2.30k
        fido_blob_free(&aes_token);
150
2.30k
151
2.30k
        return (r);
152
2.02k
}
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
2.52k
{
200
2.52k
        int r;
201
2.52k
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
2.52k
        if ((r = fido_dev_get_pin_token_tx(dev, pin, ecdh, pk)) != FIDO_OK ||
214
2.52k
            (r = fido_dev_get_pin_token_rx(dev, ecdh, token, ms)) != FIDO_OK)
215
2.52k
                return (r);
216
2.02k
#endif
217
2.02k
218
2.02k
        return (FIDO_OK);
219
2.02k
}
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
2.52k
{
225
2.52k
        return (fido_dev_get_pin_token_wait(dev, pin, ecdh, pk, token, -1));
226
2.52k
}
227
228
static int
229
pad64(const char *pin, fido_blob_t **ppin)
230
693
{
231
693
        size_t  pin_len;
232
693
        size_t  ppin_len;
233
693
234
693
        pin_len = strlen(pin);
235
693
        if (pin_len < 4 || pin_len > 255) {
236
148
                fido_log_debug("%s: invalid pin length", __func__);
237
148
                return (FIDO_ERR_PIN_POLICY_VIOLATION);
238
148
        }
239
545
240
545
        if ((*ppin = fido_blob_new()) == NULL)
241
545
                return (FIDO_ERR_INTERNAL);
242
542
243
542
        ppin_len = (pin_len + 63U) & ~63U;
244
542
        if (ppin_len < pin_len || ((*ppin)->ptr = calloc(1, ppin_len)) == NULL) {
245
4
                fido_blob_free(ppin);
246
4
                return (FIDO_ERR_INTERNAL);
247
4
        }
248
538
249
538
        memcpy((*ppin)->ptr, pin, pin_len);
250
538
        (*ppin)->len = ppin_len;
251
538
252
538
        return (FIDO_OK);
253
538
}
254
255
static int
256
fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin)
257
368
{
258
368
        fido_blob_t      f;
259
368
        fido_blob_t     *ppin = NULL;
260
368
        fido_blob_t     *ecdh = NULL;
261
368
        fido_blob_t     *opin = NULL;
262
368
        cbor_item_t     *argv[6];
263
368
        es256_pk_t      *pk = NULL;
264
368
        int r;
265
368
266
368
        memset(&f, 0, sizeof(f));
267
368
        memset(argv, 0, sizeof(argv));
268
368
269
368
        if ((opin = fido_blob_new()) == NULL || fido_blob_set(opin,
270
367
            (const unsigned char *)oldpin, strlen(oldpin)) < 0) {
271
47
                fido_log_debug("%s: fido_blob_set", __func__);
272
47
                r = FIDO_ERR_INVALID_ARGUMENT;
273
47
                goto fail;
274
47
        }
275
321
276
321
        if ((r = pad64(pin, &ppin)) != FIDO_OK) {
277
34
                fido_log_debug("%s: pad64", __func__);
278
34
                goto fail;
279
34
        }
280
287
281
287
        if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
282
203
                fido_log_debug("%s: fido_do_ecdh", __func__);
283
203
                goto fail;
284
203
        }
285
84
286
84
        if ((argv[0] = cbor_build_uint8(1)) == NULL ||
287
84
            (argv[1] = cbor_build_uint8(4)) == NULL ||
288
84
            (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
289
84
            (argv[3] = cbor_encode_change_pin_auth(ecdh, ppin, opin)) == NULL ||
290
84
            (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL ||
291
84
            (argv[5] = cbor_encode_pin_hash_enc(ecdh, opin)) == NULL) {
292
43
                fido_log_debug("%s: cbor encode", __func__);
293
43
                r = FIDO_ERR_INTERNAL;
294
43
                goto fail;
295
43
        }
296
41
297
41
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
298
41
            &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
299
2
                fido_log_debug("%s: fido_tx", __func__);
300
2
                r = FIDO_ERR_TX;
301
2
                goto fail;
302
2
        }
303
39
304
39
        r = FIDO_OK;
305
368
fail:
306
368
        cbor_vector_free(argv, nitems(argv));
307
368
        es256_pk_free(&pk);
308
368
        fido_blob_free(&ppin);
309
368
        fido_blob_free(&ecdh);
310
368
        fido_blob_free(&opin);
311
368
        free(f.ptr);
312
368
313
368
        return (r);
314
39
315
39
}
316
317
static int
318
fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin)
319
372
{
320
372
        fido_blob_t      f;
321
372
        fido_blob_t     *ppin = NULL;
322
372
        fido_blob_t     *ecdh = NULL;
323
372
        cbor_item_t     *argv[5];
324
372
        es256_pk_t      *pk = NULL;
325
372
        int              r;
326
372
327
372
        memset(&f, 0, sizeof(f));
328
372
        memset(argv, 0, sizeof(argv));
329
372
330
372
        if ((r = pad64(pin, &ppin)) != FIDO_OK) {
331
121
                fido_log_debug("%s: pad64", __func__);
332
121
                goto fail;
333
121
        }
334
251
335
251
        if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
336
169
                fido_log_debug("%s: fido_do_ecdh", __func__);
337
169
                goto fail;
338
169
        }
339
82
340
82
        if ((argv[0] = cbor_build_uint8(1)) == NULL ||
341
82
            (argv[1] = cbor_build_uint8(3)) == NULL ||
342
82
            (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
343
82
            (argv[3] = cbor_encode_set_pin_auth(ecdh, ppin)) == NULL ||
344
82
            (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL) {
345
33
                fido_log_debug("%s: cbor encode", __func__);
346
33
                r = FIDO_ERR_INTERNAL;
347
33
                goto fail;
348
33
        }
349
49
350
49
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
351
49
            &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
352
2
                fido_log_debug("%s: fido_tx", __func__);
353
2
                r = FIDO_ERR_TX;
354
2
                goto fail;
355
2
        }
356
47
357
47
        r = FIDO_OK;
358
372
fail:
359
372
        cbor_vector_free(argv, nitems(argv));
360
372
        es256_pk_free(&pk);
361
372
        fido_blob_free(&ppin);
362
372
        fido_blob_free(&ecdh);
363
372
        free(f.ptr);
364
372
365
372
        return (r);
366
47
}
367
368
static int
369
fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin,
370
    int ms)
371
740
{
372
740
        int r;
373
740
374
740
        if (oldpin != NULL) {
375
368
                if ((r = fido_dev_change_pin_tx(dev, pin, oldpin)) != FIDO_OK) {
376
329
                        fido_log_debug("%s: fido_dev_change_pin_tx", __func__);
377
329
                        return (r);
378
329
                }
379
372
        } else {
380
372
                if ((r = fido_dev_set_pin_tx(dev, pin)) != FIDO_OK) {
381
325
                        fido_log_debug("%s: fido_dev_set_pin_tx", __func__);
382
325
                        return (r);
383
325
                }
384
86
        }
385
86
386
86
        if ((r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
387
80
                fido_log_debug("%s: fido_rx_cbor_status", __func__);
388
80
                return (r);
389
80
        }
390
6
391
6
        return (FIDO_OK);
392
6
}
393
394
int
395
fido_dev_set_pin(fido_dev_t *dev, const char *pin, const char *oldpin)
396
740
{
397
740
        return (fido_dev_set_pin_wait(dev, pin, oldpin, -1));
398
740
}
399
400
static int
401
parse_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
402
183
{
403
183
        int             *retries = arg;
404
183
        uint64_t         n;
405
183
406
183
        if (cbor_isa_uint(key) == false ||
407
183
            cbor_int_get_width(key) != CBOR_INT_8 ||
408
183
            cbor_get_uint8(key) != 3) {
409
90
                fido_log_debug("%s: cbor type", __func__);
410
90
                return (0); /* ignore */
411
90
        }
412
93
413
93
        if (cbor_decode_uint64(val, &n) < 0 || n > INT_MAX) {
414
57
                fido_log_debug("%s: cbor_decode_uint64", __func__);
415
57
                return (-1);
416
57
        }
417
36
418
36
        *retries = (int)n;
419
36
420
36
        return (0);
421
36
}
422
423
static int
424
fido_dev_get_retry_count_tx(fido_dev_t *dev)
425
233
{
426
233
        fido_blob_t      f;
427
233
        cbor_item_t     *argv[2];
428
233
        int              r;
429
233
430
233
        memset(&f, 0, sizeof(f));
431
233
        memset(argv, 0, sizeof(argv));
432
233
433
233
        if ((argv[0] = cbor_build_uint8(1)) == NULL ||
434
233
            (argv[1] = cbor_build_uint8(1)) == NULL) {
435
3
                r = FIDO_ERR_INTERNAL;
436
3
                goto fail;
437
3
        }
438
230
439
230
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
440
230
            &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
441
12
                fido_log_debug("%s: fido_tx", __func__);
442
12
                r = FIDO_ERR_TX;
443
12
                goto fail;
444
12
        }
445
218
446
218
        r = FIDO_OK;
447
233
fail:
448
233
        cbor_vector_free(argv, nitems(argv));
449
233
        free(f.ptr);
450
233
451
233
        return (r);
452
218
}
453
454
static int
455
fido_dev_get_retry_count_rx(fido_dev_t *dev, int *retries, int ms)
456
218
{
457
218
        unsigned char   reply[FIDO_MAXMSG];
458
218
        int             reply_len;
459
218
        int             r;
460
218
461
218
        *retries = 0;
462
218
463
218
        if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
464
218
            ms)) < 0) {
465
66
                fido_log_debug("%s: fido_rx", __func__);
466
66
                return (FIDO_ERR_RX);
467
66
        }
468
152
469
152
        if ((r = cbor_parse_reply(reply, (size_t)reply_len, retries,
470
152
            parse_retry_count)) != FIDO_OK) {
471
114
                fido_log_debug("%s: parse_retry_count", __func__);
472
114
                return (r);
473
114
        }
474
38
475
38
        return (FIDO_OK);
476
38
}
477
478
static int
479
fido_dev_get_retry_count_wait(fido_dev_t *dev, int *retries, int ms)
480
233
{
481
233
        int r;
482
233
483
233
        if ((r = fido_dev_get_retry_count_tx(dev)) != FIDO_OK ||
484
233
            (r = fido_dev_get_retry_count_rx(dev, retries, ms)) != FIDO_OK)
485
233
                return (r);
486
38
487
38
        return (FIDO_OK);
488
38
}
489
490
int
491
fido_dev_get_retry_count(fido_dev_t *dev, int *retries)
492
233
{
493
233
        return (fido_dev_get_retry_count_wait(dev, retries, -1));
494
233
}
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
2.17k
{
501
2.17k
        fido_blob_t     *token = NULL;
502
2.17k
        int              r;
503
2.17k
504
2.17k
        if ((token = fido_blob_new()) == NULL) {
505
4
                r = FIDO_ERR_INTERNAL;
506
4
                goto fail;
507
4
        }
508
2.16k
509
2.16k
        if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) {
510
421
                fido_log_debug("%s: fido_dev_get_pin_token", __func__);
511
421
                goto fail;
512
421
        }
513
1.74k
514
1.74k
        if ((*auth = cbor_encode_pin_auth(token, hmac_data)) == NULL ||
515
1.74k
            (*opt = cbor_encode_pin_opt()) == NULL) {
516
17
                fido_log_debug("%s: cbor encode", __func__);
517
17
                r = FIDO_ERR_INTERNAL;
518
17
                goto fail;
519
17
        }
520
1.72k
521
1.72k
        r = FIDO_OK;
522
2.17k
fail:
523
2.17k
        fido_blob_free(&token);
524
2.17k
525
2.17k
        return (r);
526
1.72k
}