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