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