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