Coverage Report

Created: 2020-03-07 10:10

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