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