Coverage Report

Created: 2020-12-02 17:02

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