Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2019-2022 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 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <openssl/sha.h> |
9 | | |
10 | | #include "fido.h" |
11 | | #include "fido/credman.h" |
12 | | #include "fido/es256.h" |
13 | | |
14 | 256 | #define CMD_CRED_METADATA 0x01 |
15 | 375 | #define CMD_RP_BEGIN 0x02 |
16 | 375 | #define CMD_RP_NEXT 0x03 |
17 | 1.82k | #define CMD_RK_BEGIN 0x04 |
18 | 662 | #define CMD_RK_NEXT 0x05 |
19 | 1.46k | #define CMD_DELETE_CRED 0x06 |
20 | 1.54k | #define CMD_UPDATE_CRED 0x07 |
21 | | |
22 | | static int |
23 | | credman_grow_array(void **ptr, size_t *n_alloc, const size_t *n_rx, size_t n, |
24 | | size_t size) |
25 | 884 | { |
26 | 884 | void *new_ptr; |
27 | | |
28 | 884 | #ifdef FIDO_FUZZ |
29 | 884 | if (n > UINT8_MAX) { |
30 | 179 | fido_log_debug("%s: n > UINT8_MAX", __func__); |
31 | 179 | return (-1); |
32 | 179 | } |
33 | 705 | #endif |
34 | | |
35 | 705 | if (n < *n_alloc) |
36 | 0 | return (0); |
37 | | |
38 | | /* sanity check */ |
39 | 705 | if (*n_rx > 0 || *n_rx > *n_alloc || n < *n_alloc) { |
40 | 0 | fido_log_debug("%s: n=%zu, n_rx=%zu, n_alloc=%zu", __func__, n, |
41 | 0 | *n_rx, *n_alloc); |
42 | 0 | return (-1); |
43 | 0 | } |
44 | | |
45 | 705 | if ((new_ptr = recallocarray(*ptr, *n_alloc, n, size)) == NULL) |
46 | 1 | return (-1); |
47 | | |
48 | 704 | *ptr = new_ptr; |
49 | 704 | *n_alloc = n; |
50 | | |
51 | 704 | return (0); |
52 | 705 | } |
53 | | |
54 | | static int |
55 | | credman_prepare_hmac(uint8_t cmd, const void *body, cbor_item_t **param, |
56 | | fido_blob_t *hmac_data) |
57 | 2.00k | { |
58 | 2.00k | cbor_item_t *param_cbor[3]; |
59 | 2.00k | const fido_cred_t *cred; |
60 | 2.00k | size_t n; |
61 | 2.00k | int ok = -1; |
62 | | |
63 | 2.00k | memset(¶m_cbor, 0, sizeof(param_cbor)); |
64 | | |
65 | 2.00k | if (body == NULL) |
66 | 416 | return (fido_blob_set(hmac_data, &cmd, sizeof(cmd))); |
67 | | |
68 | 1.58k | switch (cmd) { |
69 | 856 | case CMD_RK_BEGIN: |
70 | 856 | n = 1; |
71 | 856 | if ((param_cbor[0] = fido_blob_encode(body)) == NULL) { |
72 | 1 | fido_log_debug("%s: cbor encode", __func__); |
73 | 1 | goto fail; |
74 | 1 | } |
75 | 855 | break; |
76 | 855 | case CMD_DELETE_CRED: |
77 | 342 | n = 2; |
78 | 342 | if ((param_cbor[1] = cbor_encode_pubkey(body)) == NULL) { |
79 | 12 | fido_log_debug("%s: cbor encode", __func__); |
80 | 12 | goto fail; |
81 | 12 | } |
82 | 330 | break; |
83 | 390 | case CMD_UPDATE_CRED: |
84 | 390 | n = 3; |
85 | 390 | cred = body; |
86 | 390 | param_cbor[1] = cbor_encode_pubkey(&cred->attcred.id); |
87 | 390 | param_cbor[2] = cbor_encode_user_entity(&cred->user); |
88 | 390 | if (param_cbor[1] == NULL || param_cbor[2] == NULL) { |
89 | 19 | fido_log_debug("%s: cbor encode", __func__); |
90 | 19 | goto fail; |
91 | 19 | } |
92 | 371 | break; |
93 | 371 | default: |
94 | 0 | fido_log_debug("%s: unknown cmd=0x%02x", __func__, cmd); |
95 | 0 | return (-1); |
96 | 1.58k | } |
97 | | |
98 | 1.55k | if ((*param = cbor_flatten_vector(param_cbor, n)) == NULL) { |
99 | 11 | fido_log_debug("%s: cbor_flatten_vector", __func__); |
100 | 11 | goto fail; |
101 | 11 | } |
102 | 1.54k | if (cbor_build_frame(cmd, param_cbor, n, hmac_data) < 0) { |
103 | 20 | fido_log_debug("%s: cbor_build_frame", __func__); |
104 | 20 | goto fail; |
105 | 20 | } |
106 | | |
107 | 1.52k | ok = 0; |
108 | 1.58k | fail: |
109 | 1.58k | cbor_vector_free(param_cbor, nitems(param_cbor)); |
110 | | |
111 | 1.58k | return (ok); |
112 | 1.52k | } |
113 | | |
114 | | static int |
115 | | credman_tx(fido_dev_t *dev, uint8_t subcmd, const void *param, const char *pin, |
116 | | const char *rp_id, fido_opt_t uv, int *ms) |
117 | 4.91k | { |
118 | 4.91k | fido_blob_t f; |
119 | 4.91k | fido_blob_t *ecdh = NULL; |
120 | 4.91k | fido_blob_t hmac; |
121 | 4.91k | es256_pk_t *pk = NULL; |
122 | 4.91k | cbor_item_t *argv[4]; |
123 | 4.91k | const uint8_t cmd = CTAP_CBOR_CRED_MGMT_PRE; |
124 | 4.91k | int r = FIDO_ERR_INTERNAL; |
125 | | |
126 | 4.91k | memset(&f, 0, sizeof(f)); |
127 | 4.91k | memset(&hmac, 0, sizeof(hmac)); |
128 | 4.91k | memset(&argv, 0, sizeof(argv)); |
129 | | |
130 | 4.91k | if (fido_dev_is_fido2(dev) == false) { |
131 | 1.87k | fido_log_debug("%s: fido_dev_is_fido2", __func__); |
132 | 1.87k | r = FIDO_ERR_INVALID_COMMAND; |
133 | 1.87k | goto fail; |
134 | 1.87k | } |
135 | | |
136 | | /* subCommand */ |
137 | 3.04k | if ((argv[0] = cbor_build_uint8(subcmd)) == NULL) { |
138 | 5 | fido_log_debug("%s: cbor encode", __func__); |
139 | 5 | goto fail; |
140 | 5 | } |
141 | | |
142 | | /* pinProtocol, pinAuth */ |
143 | 3.03k | if (pin != NULL || uv == FIDO_OPT_TRUE) { |
144 | 2.00k | if (credman_prepare_hmac(subcmd, param, &argv[1], &hmac) < 0) { |
145 | 63 | fido_log_debug("%s: credman_prepare_hmac", __func__); |
146 | 63 | goto fail; |
147 | 63 | } |
148 | 1.94k | if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) { |
149 | 555 | fido_log_debug("%s: fido_do_ecdh", __func__); |
150 | 555 | goto fail; |
151 | 555 | } |
152 | 1.38k | if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin, |
153 | 1.38k | rp_id, &argv[3], &argv[2], ms)) != FIDO_OK) { |
154 | 319 | fido_log_debug("%s: cbor_add_uv_params", __func__); |
155 | 319 | goto fail; |
156 | 319 | } |
157 | 1.38k | } |
158 | | |
159 | | /* framing and transmission */ |
160 | 2.10k | if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || |
161 | 2.10k | fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) { |
162 | 34 | fido_log_debug("%s: fido_tx", __func__); |
163 | 34 | r = FIDO_ERR_TX; |
164 | 34 | goto fail; |
165 | 34 | } |
166 | | |
167 | 2.06k | r = FIDO_OK; |
168 | 4.91k | fail: |
169 | 4.91k | es256_pk_free(&pk); |
170 | 4.91k | fido_blob_free(&ecdh); |
171 | 4.91k | cbor_vector_free(argv, nitems(argv)); |
172 | 4.91k | free(f.ptr); |
173 | 4.91k | free(hmac.ptr); |
174 | | |
175 | 4.91k | return (r); |
176 | 2.06k | } |
177 | | |
178 | | static int |
179 | | credman_parse_metadata(const cbor_item_t *key, const cbor_item_t *val, |
180 | | void *arg) |
181 | 42 | { |
182 | 42 | fido_credman_metadata_t *metadata = arg; |
183 | | |
184 | 42 | if (cbor_isa_uint(key) == false || |
185 | 42 | cbor_int_get_width(key) != CBOR_INT_8) { |
186 | 21 | fido_log_debug("%s: cbor type", __func__); |
187 | 21 | return (0); /* ignore */ |
188 | 21 | } |
189 | | |
190 | 21 | switch (cbor_get_uint8(key)) { |
191 | 1 | case 1: |
192 | 1 | return (cbor_decode_uint64(val, &metadata->rk_existing)); |
193 | 1 | case 2: |
194 | 1 | return (cbor_decode_uint64(val, &metadata->rk_remaining)); |
195 | 19 | default: |
196 | 19 | fido_log_debug("%s: cbor type", __func__); |
197 | 19 | return (0); /* ignore */ |
198 | 21 | } |
199 | 21 | } |
200 | | |
201 | | static int |
202 | | credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int *ms) |
203 | 28 | { |
204 | 28 | unsigned char *msg; |
205 | 28 | int msglen; |
206 | 28 | int r; |
207 | | |
208 | 28 | memset(metadata, 0, sizeof(*metadata)); |
209 | | |
210 | 28 | if ((msg = malloc(FIDO_MAXMSG)) == NULL) { |
211 | 1 | r = FIDO_ERR_INTERNAL; |
212 | 1 | goto out; |
213 | 1 | } |
214 | | |
215 | 27 | if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { |
216 | 1 | fido_log_debug("%s: fido_rx", __func__); |
217 | 1 | r = FIDO_ERR_RX; |
218 | 1 | goto out; |
219 | 1 | } |
220 | | |
221 | 26 | if ((r = cbor_parse_reply(msg, (size_t)msglen, metadata, |
222 | 26 | credman_parse_metadata)) != FIDO_OK) { |
223 | 21 | fido_log_debug("%s: credman_parse_metadata", __func__); |
224 | 21 | goto out; |
225 | 21 | } |
226 | | |
227 | 5 | r = FIDO_OK; |
228 | 28 | out: |
229 | 28 | freezero(msg, FIDO_MAXMSG); |
230 | | |
231 | 28 | return (r); |
232 | 5 | } |
233 | | |
234 | | static int |
235 | | credman_get_metadata_wait(fido_dev_t *dev, fido_credman_metadata_t *metadata, |
236 | | const char *pin, int *ms) |
237 | 256 | { |
238 | 256 | int r; |
239 | | |
240 | 256 | if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin, NULL, |
241 | 256 | FIDO_OPT_TRUE, ms)) != FIDO_OK || |
242 | 256 | (r = credman_rx_metadata(dev, metadata, ms)) != FIDO_OK) |
243 | 251 | return (r); |
244 | | |
245 | 5 | return (FIDO_OK); |
246 | 256 | } |
247 | | |
248 | | int |
249 | | fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, |
250 | | const char *pin) |
251 | 256 | { |
252 | 256 | int ms = dev->timeout_ms; |
253 | | |
254 | 256 | return (credman_get_metadata_wait(dev, metadata, pin, &ms)); |
255 | 256 | } |
256 | | |
257 | | static int |
258 | | credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg) |
259 | 4.32k | { |
260 | 4.32k | fido_cred_t *cred = arg; |
261 | 4.32k | uint64_t prot; |
262 | | |
263 | 4.32k | if (cbor_isa_uint(key) == false || |
264 | 4.32k | cbor_int_get_width(key) != CBOR_INT_8) { |
265 | 76 | fido_log_debug("%s: cbor type", __func__); |
266 | 76 | return (0); /* ignore */ |
267 | 76 | } |
268 | | |
269 | 4.25k | switch (cbor_get_uint8(key)) { |
270 | 1.02k | case 6: |
271 | 1.02k | return (cbor_decode_user(val, &cred->user)); |
272 | 977 | case 7: |
273 | 977 | return (cbor_decode_cred_id(val, &cred->attcred.id)); |
274 | 1.00k | case 8: |
275 | 1.00k | if (cbor_decode_pubkey(val, &cred->attcred.type, |
276 | 1.00k | &cred->attcred.pubkey) < 0) |
277 | 398 | return (-1); |
278 | 609 | cred->type = cred->attcred.type; /* XXX */ |
279 | 609 | return (0); |
280 | 530 | case 10: |
281 | 530 | if (cbor_decode_uint64(val, &prot) < 0 || prot > INT_MAX || |
282 | 530 | fido_cred_set_prot(cred, (int)prot) != FIDO_OK) |
283 | 100 | return (-1); |
284 | 430 | return (0); |
285 | 2 | case 11: |
286 | 2 | return (fido_blob_decode(val, &cred->largeblob_key)); |
287 | 710 | default: |
288 | 710 | fido_log_debug("%s: cbor type", __func__); |
289 | 710 | return (0); /* ignore */ |
290 | 4.25k | } |
291 | 4.25k | } |
292 | | |
293 | | static void |
294 | | credman_reset_rk(fido_credman_rk_t *rk) |
295 | 1.70k | { |
296 | 11.3k | for (size_t i = 0; i < rk->n_alloc; i++) { |
297 | 9.68k | fido_cred_reset_tx(&rk->ptr[i]); |
298 | 9.68k | fido_cred_reset_rx(&rk->ptr[i]); |
299 | 9.68k | } |
300 | | |
301 | 1.70k | free(rk->ptr); |
302 | 1.70k | rk->ptr = NULL; |
303 | 1.70k | memset(rk, 0, sizeof(*rk)); |
304 | 1.70k | } |
305 | | |
306 | | static int |
307 | | credman_parse_rk_count(const cbor_item_t *key, const cbor_item_t *val, |
308 | | void *arg) |
309 | 3.47k | { |
310 | 3.47k | fido_credman_rk_t *rk = arg; |
311 | 3.47k | uint64_t n; |
312 | | |
313 | | /* totalCredentials */ |
314 | 3.47k | if (cbor_isa_uint(key) == false || |
315 | 3.47k | cbor_int_get_width(key) != CBOR_INT_8 || |
316 | 3.47k | cbor_get_uint8(key) != 9) { |
317 | 2.76k | fido_log_debug("%s: cbor_type", __func__); |
318 | 2.76k | return (0); /* ignore */ |
319 | 2.76k | } |
320 | | |
321 | 703 | if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) { |
322 | 1 | fido_log_debug("%s: cbor_decode_uint64", __func__); |
323 | 1 | return (-1); |
324 | 1 | } |
325 | | |
326 | 702 | if (credman_grow_array((void **)&rk->ptr, &rk->n_alloc, &rk->n_rx, |
327 | 702 | (size_t)n, sizeof(*rk->ptr)) < 0) { |
328 | 86 | fido_log_debug("%s: credman_grow_array", __func__); |
329 | 86 | return (-1); |
330 | 86 | } |
331 | | |
332 | 616 | return (0); |
333 | 702 | } |
334 | | |
335 | | static int |
336 | | credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms) |
337 | 732 | { |
338 | 732 | unsigned char *msg; |
339 | 732 | int msglen; |
340 | 732 | int r; |
341 | | |
342 | 732 | credman_reset_rk(rk); |
343 | | |
344 | 732 | if ((msg = malloc(FIDO_MAXMSG)) == NULL) { |
345 | 1 | r = FIDO_ERR_INTERNAL; |
346 | 1 | goto out; |
347 | 1 | } |
348 | | |
349 | 731 | if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { |
350 | 1 | fido_log_debug("%s: fido_rx", __func__); |
351 | 1 | r = FIDO_ERR_RX; |
352 | 1 | goto out; |
353 | 1 | } |
354 | | |
355 | | /* adjust as needed */ |
356 | 730 | if ((r = cbor_parse_reply(msg, (size_t)msglen, rk, |
357 | 730 | credman_parse_rk_count)) != FIDO_OK) { |
358 | 113 | fido_log_debug("%s: credman_parse_rk_count", __func__); |
359 | 113 | goto out; |
360 | 113 | } |
361 | | |
362 | 617 | if (rk->n_alloc == 0) { |
363 | 5 | fido_log_debug("%s: n_alloc=0", __func__); |
364 | 5 | r = FIDO_OK; |
365 | 5 | goto out; |
366 | 5 | } |
367 | | |
368 | | /* parse the first rk */ |
369 | 612 | if ((r = cbor_parse_reply(msg, (size_t)msglen, &rk->ptr[0], |
370 | 612 | credman_parse_rk)) != FIDO_OK) { |
371 | 215 | fido_log_debug("%s: credman_parse_rk", __func__); |
372 | 215 | goto out; |
373 | 215 | } |
374 | 397 | rk->n_rx = 1; |
375 | | |
376 | 397 | r = FIDO_OK; |
377 | 732 | out: |
378 | 732 | freezero(msg, FIDO_MAXMSG); |
379 | | |
380 | 732 | return (r); |
381 | 397 | } |
382 | | |
383 | | static int |
384 | | credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms) |
385 | 657 | { |
386 | 657 | unsigned char *msg; |
387 | 657 | int msglen; |
388 | 657 | int r; |
389 | | |
390 | 657 | if ((msg = malloc(FIDO_MAXMSG)) == NULL) { |
391 | 1 | r = FIDO_ERR_INTERNAL; |
392 | 1 | goto out; |
393 | 1 | } |
394 | | |
395 | 656 | if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { |
396 | 48 | fido_log_debug("%s: fido_rx", __func__); |
397 | 48 | r = FIDO_ERR_RX; |
398 | 48 | goto out; |
399 | 48 | } |
400 | | |
401 | | /* sanity check */ |
402 | 608 | if (rk->n_rx >= rk->n_alloc) { |
403 | 0 | fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rk->n_rx, |
404 | 0 | rk->n_alloc); |
405 | 0 | r = FIDO_ERR_INTERNAL; |
406 | 0 | goto out; |
407 | 0 | } |
408 | | |
409 | 608 | if ((r = cbor_parse_reply(msg, (size_t)msglen, &rk->ptr[rk->n_rx], |
410 | 608 | credman_parse_rk)) != FIDO_OK) { |
411 | 335 | fido_log_debug("%s: credman_parse_rk", __func__); |
412 | 335 | goto out; |
413 | 335 | } |
414 | | |
415 | 273 | r = FIDO_OK; |
416 | 657 | out: |
417 | 657 | freezero(msg, FIDO_MAXMSG); |
418 | | |
419 | 657 | return (r); |
420 | 273 | } |
421 | | |
422 | | static int |
423 | | credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk, |
424 | | const char *pin, int *ms) |
425 | 975 | { |
426 | 975 | fido_blob_t rp_dgst; |
427 | 975 | uint8_t dgst[SHA256_DIGEST_LENGTH]; |
428 | 975 | int r; |
429 | | |
430 | 975 | if (SHA256((const unsigned char *)rp_id, strlen(rp_id), dgst) != dgst) { |
431 | 2 | fido_log_debug("%s: sha256", __func__); |
432 | 2 | return (FIDO_ERR_INTERNAL); |
433 | 2 | } |
434 | | |
435 | 973 | rp_dgst.ptr = dgst; |
436 | 973 | rp_dgst.len = sizeof(dgst); |
437 | | |
438 | 973 | if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin, rp_id, |
439 | 973 | FIDO_OPT_TRUE, ms)) != FIDO_OK || |
440 | 973 | (r = credman_rx_rk(dev, rk, ms)) != FIDO_OK) |
441 | 571 | return (r); |
442 | | |
443 | 675 | while (rk->n_rx < rk->n_alloc) { |
444 | 662 | if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL, NULL, |
445 | 662 | FIDO_OPT_FALSE, ms)) != FIDO_OK || |
446 | 662 | (r = credman_rx_next_rk(dev, rk, ms)) != FIDO_OK) |
447 | 389 | return (r); |
448 | 273 | rk->n_rx++; |
449 | 273 | } |
450 | | |
451 | 13 | return (FIDO_OK); |
452 | 402 | } |
453 | | |
454 | | int |
455 | | fido_credman_get_dev_rk(fido_dev_t *dev, const char *rp_id, |
456 | | fido_credman_rk_t *rk, const char *pin) |
457 | 975 | { |
458 | 975 | int ms = dev->timeout_ms; |
459 | | |
460 | 975 | return (credman_get_rk_wait(dev, rp_id, rk, pin, &ms)); |
461 | 975 | } |
462 | | |
463 | | static int |
464 | | credman_del_rk_wait(fido_dev_t *dev, const unsigned char *cred_id, |
465 | | size_t cred_id_len, const char *pin, int *ms) |
466 | 1.12k | { |
467 | 1.12k | fido_blob_t cred; |
468 | 1.12k | int r; |
469 | | |
470 | 1.12k | memset(&cred, 0, sizeof(cred)); |
471 | | |
472 | 1.12k | if (fido_blob_set(&cred, cred_id, cred_id_len) < 0) |
473 | 8 | return (FIDO_ERR_INVALID_ARGUMENT); |
474 | | |
475 | 1.11k | if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin, NULL, |
476 | 1.11k | FIDO_OPT_TRUE, ms)) != FIDO_OK || |
477 | 1.11k | (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) |
478 | 1.11k | goto fail; |
479 | | |
480 | 5 | r = FIDO_OK; |
481 | 1.11k | fail: |
482 | 1.11k | free(cred.ptr); |
483 | | |
484 | 1.11k | return (r); |
485 | 5 | } |
486 | | |
487 | | int |
488 | | fido_credman_del_dev_rk(fido_dev_t *dev, const unsigned char *cred_id, |
489 | | size_t cred_id_len, const char *pin) |
490 | 1.12k | { |
491 | 1.12k | int ms = dev->timeout_ms; |
492 | | |
493 | 1.12k | return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, &ms)); |
494 | 1.12k | } |
495 | | |
496 | | static int |
497 | | credman_parse_rp(const cbor_item_t *key, const cbor_item_t *val, void *arg) |
498 | 860 | { |
499 | 860 | struct fido_credman_single_rp *rp = arg; |
500 | | |
501 | 860 | if (cbor_isa_uint(key) == false || |
502 | 860 | cbor_int_get_width(key) != CBOR_INT_8) { |
503 | 148 | fido_log_debug("%s: cbor type", __func__); |
504 | 148 | return (0); /* ignore */ |
505 | 148 | } |
506 | | |
507 | 712 | switch (cbor_get_uint8(key)) { |
508 | 316 | case 3: |
509 | 316 | return (cbor_decode_rp_entity(val, &rp->rp_entity)); |
510 | 205 | case 4: |
511 | 205 | return (fido_blob_decode(val, &rp->rp_id_hash)); |
512 | 191 | default: |
513 | 191 | fido_log_debug("%s: cbor type", __func__); |
514 | 191 | return (0); /* ignore */ |
515 | 712 | } |
516 | 712 | } |
517 | | |
518 | | static void |
519 | | credman_reset_rp(fido_credman_rp_t *rp) |
520 | 588 | { |
521 | 5.33k | for (size_t i = 0; i < rp->n_alloc; i++) { |
522 | 4.74k | free(rp->ptr[i].rp_entity.id); |
523 | 4.74k | free(rp->ptr[i].rp_entity.name); |
524 | 4.74k | rp->ptr[i].rp_entity.id = NULL; |
525 | 4.74k | rp->ptr[i].rp_entity.name = NULL; |
526 | 4.74k | fido_blob_reset(&rp->ptr[i].rp_id_hash); |
527 | 4.74k | } |
528 | | |
529 | 588 | free(rp->ptr); |
530 | 588 | rp->ptr = NULL; |
531 | 588 | memset(rp, 0, sizeof(*rp)); |
532 | 588 | } |
533 | | |
534 | | static int |
535 | | credman_parse_rp_count(const cbor_item_t *key, const cbor_item_t *val, |
536 | | void *arg) |
537 | 825 | { |
538 | 825 | fido_credman_rp_t *rp = arg; |
539 | 825 | uint64_t n; |
540 | | |
541 | | /* totalRPs */ |
542 | 825 | if (cbor_isa_uint(key) == false || |
543 | 825 | cbor_int_get_width(key) != CBOR_INT_8 || |
544 | 825 | cbor_get_uint8(key) != 5) { |
545 | 642 | fido_log_debug("%s: cbor_type", __func__); |
546 | 642 | return (0); /* ignore */ |
547 | 642 | } |
548 | | |
549 | 183 | if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) { |
550 | 1 | fido_log_debug("%s: cbor_decode_uint64", __func__); |
551 | 1 | return (-1); |
552 | 1 | } |
553 | | |
554 | 182 | if (credman_grow_array((void **)&rp->ptr, &rp->n_alloc, &rp->n_rx, |
555 | 182 | (size_t)n, sizeof(*rp->ptr)) < 0) { |
556 | 94 | fido_log_debug("%s: credman_grow_array", __func__); |
557 | 94 | return (-1); |
558 | 94 | } |
559 | | |
560 | 88 | return (0); |
561 | 182 | } |
562 | | |
563 | | static int |
564 | | credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms) |
565 | 213 | { |
566 | 213 | unsigned char *msg; |
567 | 213 | int msglen; |
568 | 213 | int r; |
569 | | |
570 | 213 | credman_reset_rp(rp); |
571 | | |
572 | 213 | if ((msg = malloc(FIDO_MAXMSG)) == NULL) { |
573 | 1 | r = FIDO_ERR_INTERNAL; |
574 | 1 | goto out; |
575 | 1 | } |
576 | | |
577 | 212 | if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { |
578 | 1 | fido_log_debug("%s: fido_rx", __func__); |
579 | 1 | r = FIDO_ERR_RX; |
580 | 1 | goto out; |
581 | 1 | } |
582 | | |
583 | | /* adjust as needed */ |
584 | 211 | if ((r = cbor_parse_reply(msg, (size_t)msglen, rp, |
585 | 211 | credman_parse_rp_count)) != FIDO_OK) { |
586 | 116 | fido_log_debug("%s: credman_parse_rp_count", __func__); |
587 | 116 | goto out; |
588 | 116 | } |
589 | | |
590 | 95 | if (rp->n_alloc == 0) { |
591 | 8 | fido_log_debug("%s: n_alloc=0", __func__); |
592 | 8 | r = FIDO_OK; |
593 | 8 | goto out; |
594 | 8 | } |
595 | | |
596 | | /* parse the first rp */ |
597 | 87 | if ((r = cbor_parse_reply(msg, (size_t)msglen, &rp->ptr[0], |
598 | 87 | credman_parse_rp)) != FIDO_OK) { |
599 | 6 | fido_log_debug("%s: credman_parse_rp", __func__); |
600 | 6 | goto out; |
601 | 6 | } |
602 | 81 | rp->n_rx = 1; |
603 | | |
604 | 81 | r = FIDO_OK; |
605 | 213 | out: |
606 | 213 | freezero(msg, FIDO_MAXMSG); |
607 | | |
608 | 213 | return (r); |
609 | 81 | } |
610 | | |
611 | | static int |
612 | | credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms) |
613 | 371 | { |
614 | 371 | unsigned char *msg; |
615 | 371 | int msglen; |
616 | 371 | int r; |
617 | | |
618 | 371 | if ((msg = malloc(FIDO_MAXMSG)) == NULL) { |
619 | 1 | r = FIDO_ERR_INTERNAL; |
620 | 1 | goto out; |
621 | 1 | } |
622 | | |
623 | 370 | if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { |
624 | 46 | fido_log_debug("%s: fido_rx", __func__); |
625 | 46 | r = FIDO_ERR_RX; |
626 | 46 | goto out; |
627 | 46 | } |
628 | | |
629 | | /* sanity check */ |
630 | 324 | if (rp->n_rx >= rp->n_alloc) { |
631 | 0 | fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rp->n_rx, |
632 | 0 | rp->n_alloc); |
633 | 0 | r = FIDO_ERR_INTERNAL; |
634 | 0 | goto out; |
635 | 0 | } |
636 | | |
637 | 324 | if ((r = cbor_parse_reply(msg, (size_t)msglen, &rp->ptr[rp->n_rx], |
638 | 324 | credman_parse_rp)) != FIDO_OK) { |
639 | 24 | fido_log_debug("%s: credman_parse_rp", __func__); |
640 | 24 | goto out; |
641 | 24 | } |
642 | | |
643 | 300 | r = FIDO_OK; |
644 | 371 | out: |
645 | 371 | freezero(msg, FIDO_MAXMSG); |
646 | | |
647 | 371 | return (r); |
648 | 300 | } |
649 | | |
650 | | static int |
651 | | credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin, |
652 | | int *ms) |
653 | 375 | { |
654 | 375 | int r; |
655 | | |
656 | 375 | if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin, NULL, |
657 | 375 | FIDO_OPT_TRUE, ms)) != FIDO_OK || |
658 | 375 | (r = credman_rx_rp(dev, rp, ms)) != FIDO_OK) |
659 | 286 | return (r); |
660 | | |
661 | 389 | while (rp->n_rx < rp->n_alloc) { |
662 | 375 | if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL, NULL, |
663 | 375 | FIDO_OPT_FALSE, ms)) != FIDO_OK || |
664 | 375 | (r = credman_rx_next_rp(dev, rp, ms)) != FIDO_OK) |
665 | 75 | return (r); |
666 | 300 | rp->n_rx++; |
667 | 300 | } |
668 | | |
669 | 14 | return (FIDO_OK); |
670 | 89 | } |
671 | | |
672 | | int |
673 | | fido_credman_get_dev_rp(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin) |
674 | 375 | { |
675 | 375 | int ms = dev->timeout_ms; |
676 | | |
677 | 375 | return (credman_get_rp_wait(dev, rp, pin, &ms)); |
678 | 375 | } |
679 | | |
680 | | static int |
681 | | credman_set_dev_rk_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin, |
682 | | int *ms) |
683 | 1.15k | { |
684 | 1.15k | int r; |
685 | | |
686 | 1.15k | if ((r = credman_tx(dev, CMD_UPDATE_CRED, cred, pin, NULL, |
687 | 1.15k | FIDO_OPT_TRUE, ms)) != FIDO_OK || |
688 | 1.15k | (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) |
689 | 1.15k | return (r); |
690 | | |
691 | 3 | return (FIDO_OK); |
692 | 1.15k | } |
693 | | |
694 | | int |
695 | | fido_credman_set_dev_rk(fido_dev_t *dev, fido_cred_t *cred, const char *pin) |
696 | 1.15k | { |
697 | 1.15k | int ms = dev->timeout_ms; |
698 | | |
699 | 1.15k | return (credman_set_dev_rk_wait(dev, cred, pin, &ms)); |
700 | 1.15k | } |
701 | | |
702 | | fido_credman_rk_t * |
703 | | fido_credman_rk_new(void) |
704 | 976 | { |
705 | 976 | return (calloc(1, sizeof(fido_credman_rk_t))); |
706 | 976 | } |
707 | | |
708 | | void |
709 | | fido_credman_rk_free(fido_credman_rk_t **rk_p) |
710 | 975 | { |
711 | 975 | fido_credman_rk_t *rk; |
712 | | |
713 | 975 | if (rk_p == NULL || (rk = *rk_p) == NULL) |
714 | 0 | return; |
715 | | |
716 | 975 | credman_reset_rk(rk); |
717 | 975 | free(rk); |
718 | 975 | *rk_p = NULL; |
719 | 975 | } |
720 | | |
721 | | size_t |
722 | | fido_credman_rk_count(const fido_credman_rk_t *rk) |
723 | 2.98k | { |
724 | 2.98k | return (rk->n_rx); |
725 | 2.98k | } |
726 | | |
727 | | const fido_cred_t * |
728 | | fido_credman_rk(const fido_credman_rk_t *rk, size_t idx) |
729 | 1.64k | { |
730 | 1.64k | if (idx >= rk->n_alloc) |
731 | 368 | return (NULL); |
732 | | |
733 | 1.27k | return (&rk->ptr[idx]); |
734 | 1.64k | } |
735 | | |
736 | | fido_credman_metadata_t * |
737 | | fido_credman_metadata_new(void) |
738 | 257 | { |
739 | 257 | return (calloc(1, sizeof(fido_credman_metadata_t))); |
740 | 257 | } |
741 | | |
742 | | void |
743 | | fido_credman_metadata_free(fido_credman_metadata_t **metadata_p) |
744 | 256 | { |
745 | 256 | fido_credman_metadata_t *metadata; |
746 | | |
747 | 256 | if (metadata_p == NULL || (metadata = *metadata_p) == NULL) |
748 | 0 | return; |
749 | | |
750 | 256 | free(metadata); |
751 | 256 | *metadata_p = NULL; |
752 | 256 | } |
753 | | |
754 | | uint64_t |
755 | | fido_credman_rk_existing(const fido_credman_metadata_t *metadata) |
756 | 256 | { |
757 | 256 | return (metadata->rk_existing); |
758 | 256 | } |
759 | | |
760 | | uint64_t |
761 | | fido_credman_rk_remaining(const fido_credman_metadata_t *metadata) |
762 | 256 | { |
763 | 256 | return (metadata->rk_remaining); |
764 | 256 | } |
765 | | |
766 | | fido_credman_rp_t * |
767 | | fido_credman_rp_new(void) |
768 | 376 | { |
769 | 376 | return (calloc(1, sizeof(fido_credman_rp_t))); |
770 | 376 | } |
771 | | |
772 | | void |
773 | | fido_credman_rp_free(fido_credman_rp_t **rp_p) |
774 | 375 | { |
775 | 375 | fido_credman_rp_t *rp; |
776 | | |
777 | 375 | if (rp_p == NULL || (rp = *rp_p) == NULL) |
778 | 0 | return; |
779 | | |
780 | 375 | credman_reset_rp(rp); |
781 | 375 | free(rp); |
782 | 375 | *rp_p = NULL; |
783 | 375 | } |
784 | | |
785 | | size_t |
786 | | fido_credman_rp_count(const fido_credman_rp_t *rp) |
787 | 1.13k | { |
788 | 1.13k | return (rp->n_rx); |
789 | 1.13k | } |
790 | | |
791 | | const char * |
792 | | fido_credman_rp_id(const fido_credman_rp_t *rp, size_t idx) |
793 | 756 | { |
794 | 756 | if (idx >= rp->n_alloc) |
795 | 293 | return (NULL); |
796 | | |
797 | 463 | return (rp->ptr[idx].rp_entity.id); |
798 | 756 | } |
799 | | |
800 | | const char * |
801 | | fido_credman_rp_name(const fido_credman_rp_t *rp, size_t idx) |
802 | 756 | { |
803 | 756 | if (idx >= rp->n_alloc) |
804 | 293 | return (NULL); |
805 | | |
806 | 463 | return (rp->ptr[idx].rp_entity.name); |
807 | 756 | } |
808 | | |
809 | | size_t |
810 | | fido_credman_rp_id_hash_len(const fido_credman_rp_t *rp, size_t idx) |
811 | 756 | { |
812 | 756 | if (idx >= rp->n_alloc) |
813 | 293 | return (0); |
814 | | |
815 | 463 | return (rp->ptr[idx].rp_id_hash.len); |
816 | 756 | } |
817 | | |
818 | | const unsigned char * |
819 | | fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *rp, size_t idx) |
820 | 756 | { |
821 | 756 | if (idx >= rp->n_alloc) |
822 | 293 | return (NULL); |
823 | | |
824 | 463 | return (rp->ptr[idx].rp_id_hash.ptr); |
825 | 756 | } |