diff options
author | nicoo <nicoo@debian.org> | 2020-02-12 13:42:22 +0100 |
---|---|---|
committer | Nicolas Braud-Santoni <nicolas@braud-santoni.eu> | 2020-02-12 13:42:22 +0100 |
commit | c79050aa44b8836d836c5dd22a383a073c28b74b (patch) | |
tree | 7bcca9fabd7718bf87ca600a6594f57b76d8de7d /src/cbor.c |
Import upstream release 1.3.0
Closes: #951184
Diffstat (limited to 'src/cbor.c')
-rw-r--r-- | src/cbor.c | 1520 |
1 files changed, 1520 insertions, 0 deletions
diff --git a/src/cbor.c b/src/cbor.c new file mode 100644 index 0000000..3e03592 --- /dev/null +++ b/src/cbor.c | |||
@@ -0,0 +1,1520 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2018 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/evp.h> | ||
8 | #include <openssl/hmac.h> | ||
9 | #include <openssl/sha.h> | ||
10 | |||
11 | #include <string.h> | ||
12 | #include "fido.h" | ||
13 | |||
14 | static int | ||
15 | check_key_type(cbor_item_t *item) | ||
16 | { | ||
17 | if (item->type == CBOR_TYPE_UINT || item->type == CBOR_TYPE_NEGINT || | ||
18 | item->type == CBOR_TYPE_STRING) | ||
19 | return (0); | ||
20 | |||
21 | fido_log_debug("%s: invalid type: %d", __func__, item->type); | ||
22 | |||
23 | return (-1); | ||
24 | } | ||
25 | |||
26 | /* | ||
27 | * Validate CTAP2 canonical CBOR encoding rules for maps. | ||
28 | */ | ||
29 | static int | ||
30 | ctap_check_cbor(cbor_item_t *prev, cbor_item_t *curr) | ||
31 | { | ||
32 | size_t curr_len; | ||
33 | size_t prev_len; | ||
34 | |||
35 | if (check_key_type(prev) < 0 || check_key_type(curr) < 0) | ||
36 | return (-1); | ||
37 | |||
38 | if (prev->type != curr->type) { | ||
39 | if (prev->type < curr->type) | ||
40 | return (0); | ||
41 | fido_log_debug("%s: unsorted types", __func__); | ||
42 | return (-1); | ||
43 | } | ||
44 | |||
45 | if (curr->type == CBOR_TYPE_UINT || curr->type == CBOR_TYPE_NEGINT) { | ||
46 | if (cbor_int_get_width(curr) >= cbor_int_get_width(prev) && | ||
47 | cbor_get_int(curr) > cbor_get_int(prev)) | ||
48 | return (0); | ||
49 | } else { | ||
50 | curr_len = cbor_string_length(curr); | ||
51 | prev_len = cbor_string_length(prev); | ||
52 | |||
53 | if (curr_len > prev_len || (curr_len == prev_len && | ||
54 | memcmp(cbor_string_handle(prev), cbor_string_handle(curr), | ||
55 | curr_len) < 0)) | ||
56 | return (0); | ||
57 | } | ||
58 | |||
59 | fido_log_debug("%s: invalid cbor", __func__); | ||
60 | |||
61 | return (-1); | ||
62 | } | ||
63 | |||
64 | int | ||
65 | cbor_map_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *, | ||
66 | const cbor_item_t *, void *)) | ||
67 | { | ||
68 | struct cbor_pair *v; | ||
69 | size_t n; | ||
70 | |||
71 | if ((v = cbor_map_handle(item)) == NULL) { | ||
72 | fido_log_debug("%s: cbor_map_handle", __func__); | ||
73 | return (-1); | ||
74 | } | ||
75 | |||
76 | n = cbor_map_size(item); | ||
77 | |||
78 | for (size_t i = 0; i < n; i++) { | ||
79 | if (v[i].key == NULL || v[i].value == NULL) { | ||
80 | fido_log_debug("%s: key=%p, value=%p for i=%zu", | ||
81 | __func__, (void *)v[i].key, (void *)v[i].value, i); | ||
82 | return (-1); | ||
83 | } | ||
84 | if (i && ctap_check_cbor(v[i - 1].key, v[i].key) < 0) { | ||
85 | fido_log_debug("%s: ctap_check_cbor", __func__); | ||
86 | return (-1); | ||
87 | } | ||
88 | if (f(v[i].key, v[i].value, arg) < 0) { | ||
89 | fido_log_debug("%s: iterator < 0 on i=%zu", __func__, | ||
90 | i); | ||
91 | return (-1); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | return (0); | ||
96 | } | ||
97 | |||
98 | int | ||
99 | cbor_array_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *, | ||
100 | void *)) | ||
101 | { | ||
102 | cbor_item_t **v; | ||
103 | size_t n; | ||
104 | |||
105 | if ((v = cbor_array_handle(item)) == NULL) { | ||
106 | fido_log_debug("%s: cbor_array_handle", __func__); | ||
107 | return (-1); | ||
108 | } | ||
109 | |||
110 | n = cbor_array_size(item); | ||
111 | |||
112 | for (size_t i = 0; i < n; i++) | ||
113 | if (v[i] == NULL || f(v[i], arg) < 0) { | ||
114 | fido_log_debug("%s: iterator < 0 on i=%zu,%p", | ||
115 | __func__, i, (void *)v[i]); | ||
116 | return (-1); | ||
117 | } | ||
118 | |||
119 | return (0); | ||
120 | } | ||
121 | |||
122 | int | ||
123 | cbor_parse_reply(const unsigned char *blob, size_t blob_len, void *arg, | ||
124 | int(*parser)(const cbor_item_t *, const cbor_item_t *, void *)) | ||
125 | { | ||
126 | cbor_item_t *item = NULL; | ||
127 | struct cbor_load_result cbor; | ||
128 | int r; | ||
129 | |||
130 | if (blob_len < 1) { | ||
131 | fido_log_debug("%s: blob_len=%zu", __func__, blob_len); | ||
132 | r = FIDO_ERR_RX; | ||
133 | goto fail; | ||
134 | } | ||
135 | |||
136 | if (blob[0] != FIDO_OK) { | ||
137 | fido_log_debug("%s: blob[0]=0x%02x", __func__, blob[0]); | ||
138 | r = blob[0]; | ||
139 | goto fail; | ||
140 | } | ||
141 | |||
142 | if ((item = cbor_load(blob + 1, blob_len - 1, &cbor)) == NULL) { | ||
143 | fido_log_debug("%s: cbor_load", __func__); | ||
144 | r = FIDO_ERR_RX_NOT_CBOR; | ||
145 | goto fail; | ||
146 | } | ||
147 | |||
148 | if (cbor_isa_map(item) == false || | ||
149 | cbor_map_is_definite(item) == false) { | ||
150 | fido_log_debug("%s: cbor type", __func__); | ||
151 | r = FIDO_ERR_RX_INVALID_CBOR; | ||
152 | goto fail; | ||
153 | } | ||
154 | |||
155 | if (cbor_map_iter(item, arg, parser) < 0) { | ||
156 | fido_log_debug("%s: cbor_map_iter", __func__); | ||
157 | r = FIDO_ERR_RX_INVALID_CBOR; | ||
158 | goto fail; | ||
159 | } | ||
160 | |||
161 | r = FIDO_OK; | ||
162 | fail: | ||
163 | if (item != NULL) | ||
164 | cbor_decref(&item); | ||
165 | |||
166 | return (r); | ||
167 | } | ||
168 | |||
169 | void | ||
170 | cbor_vector_free(cbor_item_t **item, size_t len) | ||
171 | { | ||
172 | for (size_t i = 0; i < len; i++) | ||
173 | if (item[i] != NULL) | ||
174 | cbor_decref(&item[i]); | ||
175 | } | ||
176 | |||
177 | int | ||
178 | cbor_bytestring_copy(const cbor_item_t *item, unsigned char **buf, size_t *len) | ||
179 | { | ||
180 | if (*buf != NULL || *len != 0) { | ||
181 | fido_log_debug("%s: dup", __func__); | ||
182 | return (-1); | ||
183 | } | ||
184 | |||
185 | if (cbor_isa_bytestring(item) == false || | ||
186 | cbor_bytestring_is_definite(item) == false) { | ||
187 | fido_log_debug("%s: cbor type", __func__); | ||
188 | return (-1); | ||
189 | } | ||
190 | |||
191 | *len = cbor_bytestring_length(item); | ||
192 | if ((*buf = malloc(*len)) == NULL) { | ||
193 | *len = 0; | ||
194 | return (-1); | ||
195 | } | ||
196 | |||
197 | memcpy(*buf, cbor_bytestring_handle(item), *len); | ||
198 | |||
199 | return (0); | ||
200 | } | ||
201 | |||
202 | int | ||
203 | cbor_string_copy(const cbor_item_t *item, char **str) | ||
204 | { | ||
205 | size_t len; | ||
206 | |||
207 | if (*str != NULL) { | ||
208 | fido_log_debug("%s: dup", __func__); | ||
209 | return (-1); | ||
210 | } | ||
211 | |||
212 | if (cbor_isa_string(item) == false || | ||
213 | cbor_string_is_definite(item) == false) { | ||
214 | fido_log_debug("%s: cbor type", __func__); | ||
215 | return (-1); | ||
216 | } | ||
217 | |||
218 | if ((len = cbor_string_length(item)) == SIZE_MAX || | ||
219 | (*str = malloc(len + 1)) == NULL) | ||
220 | return (-1); | ||
221 | |||
222 | memcpy(*str, cbor_string_handle(item), len); | ||
223 | (*str)[len] = '\0'; | ||
224 | |||
225 | return (0); | ||
226 | } | ||
227 | |||
228 | int | ||
229 | cbor_add_bytestring(cbor_item_t *item, const char *key, | ||
230 | const unsigned char *value, size_t value_len) | ||
231 | { | ||
232 | struct cbor_pair pair; | ||
233 | int ok = -1; | ||
234 | |||
235 | memset(&pair, 0, sizeof(pair)); | ||
236 | |||
237 | if ((pair.key = cbor_build_string(key)) == NULL || | ||
238 | (pair.value = cbor_build_bytestring(value, value_len)) == NULL) { | ||
239 | fido_log_debug("%s: cbor_build", __func__); | ||
240 | goto fail; | ||
241 | } | ||
242 | |||
243 | if (!cbor_map_add(item, pair)) { | ||
244 | fido_log_debug("%s: cbor_map_add", __func__); | ||
245 | goto fail; | ||
246 | } | ||
247 | |||
248 | ok = 0; | ||
249 | fail: | ||
250 | if (pair.key) | ||
251 | cbor_decref(&pair.key); | ||
252 | if (pair.value) | ||
253 | cbor_decref(&pair.value); | ||
254 | |||
255 | return (ok); | ||
256 | } | ||
257 | |||
258 | int | ||
259 | cbor_add_string(cbor_item_t *item, const char *key, const char *value) | ||
260 | { | ||
261 | struct cbor_pair pair; | ||
262 | int ok = -1; | ||
263 | |||
264 | memset(&pair, 0, sizeof(pair)); | ||
265 | |||
266 | if ((pair.key = cbor_build_string(key)) == NULL || | ||
267 | (pair.value = cbor_build_string(value)) == NULL) { | ||
268 | fido_log_debug("%s: cbor_build", __func__); | ||
269 | goto fail; | ||
270 | } | ||
271 | |||
272 | if (!cbor_map_add(item, pair)) { | ||
273 | fido_log_debug("%s: cbor_map_add", __func__); | ||
274 | goto fail; | ||
275 | } | ||
276 | |||
277 | ok = 0; | ||
278 | fail: | ||
279 | if (pair.key) | ||
280 | cbor_decref(&pair.key); | ||
281 | if (pair.value) | ||
282 | cbor_decref(&pair.value); | ||
283 | |||
284 | return (ok); | ||
285 | } | ||
286 | |||
287 | int | ||
288 | cbor_add_bool(cbor_item_t *item, const char *key, fido_opt_t value) | ||
289 | { | ||
290 | struct cbor_pair pair; | ||
291 | int ok = -1; | ||
292 | |||
293 | memset(&pair, 0, sizeof(pair)); | ||
294 | |||
295 | if ((pair.key = cbor_build_string(key)) == NULL || | ||
296 | (pair.value = cbor_build_bool(value == FIDO_OPT_TRUE)) == NULL) { | ||
297 | fido_log_debug("%s: cbor_build", __func__); | ||
298 | goto fail; | ||
299 | } | ||
300 | |||
301 | if (!cbor_map_add(item, pair)) { | ||
302 | fido_log_debug("%s: cbor_map_add", __func__); | ||
303 | goto fail; | ||
304 | } | ||
305 | |||
306 | ok = 0; | ||
307 | fail: | ||
308 | if (pair.key) | ||
309 | cbor_decref(&pair.key); | ||
310 | if (pair.value) | ||
311 | cbor_decref(&pair.value); | ||
312 | |||
313 | return (ok); | ||
314 | } | ||
315 | |||
316 | static int | ||
317 | cbor_add_arg(cbor_item_t *item, uint8_t n, cbor_item_t *arg) | ||
318 | { | ||
319 | struct cbor_pair pair; | ||
320 | int ok = -1; | ||
321 | |||
322 | memset(&pair, 0, sizeof(pair)); | ||
323 | |||
324 | if (arg == NULL) | ||
325 | return (0); /* empty argument */ | ||
326 | |||
327 | if ((pair.key = cbor_build_uint8(n)) == NULL) { | ||
328 | fido_log_debug("%s: cbor_build", __func__); | ||
329 | goto fail; | ||
330 | } | ||
331 | |||
332 | pair.value = arg; | ||
333 | |||
334 | if (!cbor_map_add(item, pair)) { | ||
335 | fido_log_debug("%s: cbor_map_add", __func__); | ||
336 | goto fail; | ||
337 | } | ||
338 | |||
339 | ok = 0; | ||
340 | fail: | ||
341 | if (pair.key) | ||
342 | cbor_decref(&pair.key); | ||
343 | |||
344 | return (ok); | ||
345 | } | ||
346 | |||
347 | cbor_item_t * | ||
348 | cbor_flatten_vector(cbor_item_t *argv[], size_t argc) | ||
349 | { | ||
350 | cbor_item_t *map; | ||
351 | uint8_t i; | ||
352 | |||
353 | if (argc > UINT8_MAX - 1) | ||
354 | return (NULL); | ||
355 | |||
356 | if ((map = cbor_new_definite_map(argc)) == NULL) | ||
357 | return (NULL); | ||
358 | |||
359 | for (i = 0; i < argc; i++) | ||
360 | if (cbor_add_arg(map, i + 1, argv[i]) < 0) | ||
361 | break; | ||
362 | |||
363 | if (i != argc) { | ||
364 | cbor_decref(&map); | ||
365 | map = NULL; | ||
366 | } | ||
367 | |||
368 | return (map); | ||
369 | } | ||
370 | |||
371 | int | ||
372 | cbor_build_frame(uint8_t cmd, cbor_item_t *argv[], size_t argc, fido_blob_t *f) | ||
373 | { | ||
374 | cbor_item_t *flat = NULL; | ||
375 | unsigned char *cbor = NULL; | ||
376 | size_t cbor_len; | ||
377 | size_t cbor_alloc_len; | ||
378 | int ok = -1; | ||
379 | |||
380 | if ((flat = cbor_flatten_vector(argv, argc)) == NULL) | ||
381 | goto fail; | ||
382 | |||
383 | cbor_len = cbor_serialize_alloc(flat, &cbor, &cbor_alloc_len); | ||
384 | if (cbor_len == 0 || cbor_len == SIZE_MAX) { | ||
385 | fido_log_debug("%s: cbor_len=%zu", __func__, cbor_len); | ||
386 | goto fail; | ||
387 | } | ||
388 | |||
389 | if ((f->ptr = malloc(cbor_len + 1)) == NULL) | ||
390 | goto fail; | ||
391 | |||
392 | f->len = cbor_len + 1; | ||
393 | f->ptr[0] = cmd; | ||
394 | memcpy(f->ptr + 1, cbor, f->len - 1); | ||
395 | |||
396 | ok = 0; | ||
397 | fail: | ||
398 | if (flat != NULL) | ||
399 | cbor_decref(&flat); | ||
400 | |||
401 | free(cbor); | ||
402 | |||
403 | return (ok); | ||
404 | } | ||
405 | |||
406 | cbor_item_t * | ||
407 | cbor_encode_rp_entity(const fido_rp_t *rp) | ||
408 | { | ||
409 | cbor_item_t *item = NULL; | ||
410 | |||
411 | if ((item = cbor_new_definite_map(2)) == NULL) | ||
412 | return (NULL); | ||
413 | |||
414 | if ((rp->id && cbor_add_string(item, "id", rp->id) < 0) || | ||
415 | (rp->name && cbor_add_string(item, "name", rp->name) < 0)) { | ||
416 | cbor_decref(&item); | ||
417 | return (NULL); | ||
418 | } | ||
419 | |||
420 | return (item); | ||
421 | } | ||
422 | |||
423 | cbor_item_t * | ||
424 | cbor_encode_user_entity(const fido_user_t *user) | ||
425 | { | ||
426 | cbor_item_t *item = NULL; | ||
427 | const fido_blob_t *id = &user->id; | ||
428 | const char *display = user->display_name; | ||
429 | |||
430 | if ((item = cbor_new_definite_map(4)) == NULL) | ||
431 | return (NULL); | ||
432 | |||
433 | if ((id->ptr && cbor_add_bytestring(item, "id", id->ptr, id->len) < 0) || | ||
434 | (user->icon && cbor_add_string(item, "icon", user->icon) < 0) || | ||
435 | (user->name && cbor_add_string(item, "name", user->name) < 0) || | ||
436 | (display && cbor_add_string(item, "displayName", display) < 0)) { | ||
437 | cbor_decref(&item); | ||
438 | return (NULL); | ||
439 | } | ||
440 | |||
441 | return (item); | ||
442 | } | ||
443 | |||
444 | cbor_item_t * | ||
445 | cbor_encode_pubkey_param(int cose_alg) | ||
446 | { | ||
447 | cbor_item_t *item = NULL; | ||
448 | cbor_item_t *body = NULL; | ||
449 | struct cbor_pair alg; | ||
450 | int ok = -1; | ||
451 | |||
452 | memset(&alg, 0, sizeof(alg)); | ||
453 | |||
454 | if ((item = cbor_new_definite_array(1)) == NULL || | ||
455 | (body = cbor_new_definite_map(2)) == NULL || | ||
456 | cose_alg > -1 || cose_alg < INT16_MIN) | ||
457 | goto fail; | ||
458 | |||
459 | alg.key = cbor_build_string("alg"); | ||
460 | |||
461 | if (-cose_alg - 1 > UINT8_MAX) | ||
462 | alg.value = cbor_build_negint16((uint16_t)(-cose_alg - 1)); | ||
463 | else | ||
464 | alg.value = cbor_build_negint8((uint8_t)(-cose_alg - 1)); | ||
465 | |||
466 | if (alg.key == NULL || alg.value == NULL) { | ||
467 | fido_log_debug("%s: cbor_build", __func__); | ||
468 | goto fail; | ||
469 | } | ||
470 | |||
471 | if (cbor_map_add(body, alg) == false || | ||
472 | cbor_add_string(body, "type", "public-key") < 0 || | ||
473 | cbor_array_push(item, body) == false) | ||
474 | goto fail; | ||
475 | |||
476 | ok = 0; | ||
477 | fail: | ||
478 | if (ok < 0) { | ||
479 | if (item != NULL) { | ||
480 | cbor_decref(&item); | ||
481 | item = NULL; | ||
482 | } | ||
483 | } | ||
484 | |||
485 | if (body != NULL) | ||
486 | cbor_decref(&body); | ||
487 | if (alg.key != NULL) | ||
488 | cbor_decref(&alg.key); | ||
489 | if (alg.value != NULL) | ||
490 | cbor_decref(&alg.value); | ||
491 | |||
492 | return (item); | ||
493 | } | ||
494 | |||
495 | cbor_item_t * | ||
496 | cbor_encode_pubkey(const fido_blob_t *pubkey) | ||
497 | { | ||
498 | cbor_item_t *cbor_key = NULL; | ||
499 | |||
500 | if ((cbor_key = cbor_new_definite_map(2)) == NULL || | ||
501 | cbor_add_bytestring(cbor_key, "id", pubkey->ptr, pubkey->len) < 0 || | ||
502 | cbor_add_string(cbor_key, "type", "public-key") < 0) { | ||
503 | if (cbor_key) | ||
504 | cbor_decref(&cbor_key); | ||
505 | return (NULL); | ||
506 | } | ||
507 | |||
508 | return (cbor_key); | ||
509 | } | ||
510 | |||
511 | cbor_item_t * | ||
512 | cbor_encode_pubkey_list(const fido_blob_array_t *list) | ||
513 | { | ||
514 | cbor_item_t *array = NULL; | ||
515 | cbor_item_t *key = NULL; | ||
516 | |||
517 | if ((array = cbor_new_definite_array(list->len)) == NULL) | ||
518 | goto fail; | ||
519 | |||
520 | for (size_t i = 0; i < list->len; i++) { | ||
521 | if ((key = cbor_encode_pubkey(&list->ptr[i])) == NULL || | ||
522 | cbor_array_push(array, key) == false) | ||
523 | goto fail; | ||
524 | cbor_decref(&key); | ||
525 | } | ||
526 | |||
527 | return (array); | ||
528 | fail: | ||
529 | if (key != NULL) | ||
530 | cbor_decref(&key); | ||
531 | if (array != NULL) | ||
532 | cbor_decref(&array); | ||
533 | |||
534 | return (NULL); | ||
535 | } | ||
536 | |||
537 | cbor_item_t * | ||
538 | cbor_encode_extensions(int ext) | ||
539 | { | ||
540 | cbor_item_t *item = NULL; | ||
541 | |||
542 | if (ext == 0 || ext != FIDO_EXT_HMAC_SECRET) | ||
543 | return (NULL); | ||
544 | |||
545 | if ((item = cbor_new_definite_map(1)) == NULL) | ||
546 | return (NULL); | ||
547 | |||
548 | if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) { | ||
549 | cbor_decref(&item); | ||
550 | return (NULL); | ||
551 | } | ||
552 | |||
553 | return (item); | ||
554 | } | ||
555 | |||
556 | cbor_item_t * | ||
557 | cbor_encode_options(fido_opt_t rk, fido_opt_t uv) | ||
558 | { | ||
559 | cbor_item_t *item = NULL; | ||
560 | |||
561 | if ((item = cbor_new_definite_map(2)) == NULL) | ||
562 | return (NULL); | ||
563 | |||
564 | if ((rk != FIDO_OPT_OMIT && cbor_add_bool(item, "rk", rk) < 0) || | ||
565 | (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) { | ||
566 | cbor_decref(&item); | ||
567 | return (NULL); | ||
568 | } | ||
569 | |||
570 | return (item); | ||
571 | } | ||
572 | |||
573 | cbor_item_t * | ||
574 | cbor_encode_assert_options(fido_opt_t up, fido_opt_t uv) | ||
575 | { | ||
576 | cbor_item_t *item = NULL; | ||
577 | |||
578 | if ((item = cbor_new_definite_map(2)) == NULL) | ||
579 | return (NULL); | ||
580 | |||
581 | if ((up != FIDO_OPT_OMIT && cbor_add_bool(item, "up", up) < 0) || | ||
582 | (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) { | ||
583 | cbor_decref(&item); | ||
584 | return (NULL); | ||
585 | } | ||
586 | |||
587 | return (item); | ||
588 | } | ||
589 | |||
590 | cbor_item_t * | ||
591 | cbor_encode_pin_auth(const fido_blob_t *hmac_key, const fido_blob_t *data) | ||
592 | { | ||
593 | const EVP_MD *md = NULL; | ||
594 | unsigned char dgst[SHA256_DIGEST_LENGTH]; | ||
595 | unsigned int dgst_len; | ||
596 | |||
597 | if ((md = EVP_sha256()) == NULL || HMAC(md, hmac_key->ptr, | ||
598 | (int)hmac_key->len, data->ptr, (int)data->len, dgst, | ||
599 | &dgst_len) == NULL || dgst_len != SHA256_DIGEST_LENGTH) | ||
600 | return (NULL); | ||
601 | |||
602 | return (cbor_build_bytestring(dgst, 16)); | ||
603 | } | ||
604 | |||
605 | cbor_item_t * | ||
606 | cbor_encode_pin_opt(void) | ||
607 | { | ||
608 | return (cbor_build_uint8(1)); | ||
609 | } | ||
610 | |||
611 | cbor_item_t * | ||
612 | cbor_encode_pin_enc(const fido_blob_t *key, const fido_blob_t *pin) | ||
613 | { | ||
614 | fido_blob_t pe; | ||
615 | cbor_item_t *item = NULL; | ||
616 | |||
617 | if (aes256_cbc_enc(key, pin, &pe) < 0) | ||
618 | return (NULL); | ||
619 | |||
620 | item = cbor_build_bytestring(pe.ptr, pe.len); | ||
621 | free(pe.ptr); | ||
622 | |||
623 | return (item); | ||
624 | } | ||
625 | |||
626 | static int | ||
627 | sha256(const unsigned char *data, size_t data_len, fido_blob_t *digest) | ||
628 | { | ||
629 | if ((digest->ptr = calloc(1, SHA256_DIGEST_LENGTH)) == NULL) | ||
630 | return (-1); | ||
631 | |||
632 | digest->len = SHA256_DIGEST_LENGTH; | ||
633 | |||
634 | if (SHA256(data, data_len, digest->ptr) != digest->ptr) { | ||
635 | free(digest->ptr); | ||
636 | digest->ptr = NULL; | ||
637 | digest->len = 0; | ||
638 | return (-1); | ||
639 | } | ||
640 | |||
641 | return (0); | ||
642 | } | ||
643 | |||
644 | cbor_item_t * | ||
645 | cbor_encode_change_pin_auth(const fido_blob_t *key, const fido_blob_t *new_pin, | ||
646 | const fido_blob_t *pin) | ||
647 | { | ||
648 | unsigned char dgst[SHA256_DIGEST_LENGTH]; | ||
649 | unsigned int dgst_len; | ||
650 | cbor_item_t *item = NULL; | ||
651 | const EVP_MD *md = NULL; | ||
652 | #if OPENSSL_VERSION_NUMBER < 0x10100000L | ||
653 | HMAC_CTX ctx; | ||
654 | #else | ||
655 | HMAC_CTX *ctx = NULL; | ||
656 | #endif | ||
657 | fido_blob_t *npe = NULL; /* new pin, encrypted */ | ||
658 | fido_blob_t *ph = NULL; /* pin hash */ | ||
659 | fido_blob_t *phe = NULL; /* pin hash, encrypted */ | ||
660 | int ok = -1; | ||
661 | |||
662 | if ((npe = fido_blob_new()) == NULL || | ||
663 | (ph = fido_blob_new()) == NULL || | ||
664 | (phe = fido_blob_new()) == NULL) | ||
665 | goto fail; | ||
666 | |||
667 | if (aes256_cbc_enc(key, new_pin, npe) < 0) { | ||
668 | fido_log_debug("%s: aes256_cbc_enc 1", __func__); | ||
669 | goto fail; | ||
670 | } | ||
671 | |||
672 | if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) { | ||
673 | fido_log_debug("%s: sha256", __func__); | ||
674 | goto fail; | ||
675 | } | ||
676 | |||
677 | ph->len = 16; /* first 16 bytes */ | ||
678 | |||
679 | if (aes256_cbc_enc(key, ph, phe) < 0) { | ||
680 | fido_log_debug("%s: aes256_cbc_enc 2", __func__); | ||
681 | goto fail; | ||
682 | } | ||
683 | |||
684 | #if OPENSSL_VERSION_NUMBER < 0x10100000L | ||
685 | HMAC_CTX_init(&ctx); | ||
686 | |||
687 | if ((md = EVP_sha256()) == NULL || | ||
688 | HMAC_Init_ex(&ctx, key->ptr, (int)key->len, md, NULL) == 0 || | ||
689 | HMAC_Update(&ctx, npe->ptr, (int)npe->len) == 0 || | ||
690 | HMAC_Update(&ctx, phe->ptr, (int)phe->len) == 0 || | ||
691 | HMAC_Final(&ctx, dgst, &dgst_len) == 0 || dgst_len != 32) { | ||
692 | fido_log_debug("%s: HMAC", __func__); | ||
693 | goto fail; | ||
694 | } | ||
695 | #else | ||
696 | if ((ctx = HMAC_CTX_new()) == NULL || | ||
697 | (md = EVP_sha256()) == NULL || | ||
698 | HMAC_Init_ex(ctx, key->ptr, (int)key->len, md, NULL) == 0 || | ||
699 | HMAC_Update(ctx, npe->ptr, (int)npe->len) == 0 || | ||
700 | HMAC_Update(ctx, phe->ptr, (int)phe->len) == 0 || | ||
701 | HMAC_Final(ctx, dgst, &dgst_len) == 0 || dgst_len != 32) { | ||
702 | fido_log_debug("%s: HMAC", __func__); | ||
703 | goto fail; | ||
704 | } | ||
705 | #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ | ||
706 | |||
707 | if ((item = cbor_build_bytestring(dgst, 16)) == NULL) { | ||
708 | fido_log_debug("%s: cbor_build_bytestring", __func__); | ||
709 | goto fail; | ||
710 | } | ||
711 | |||
712 | ok = 0; | ||
713 | fail: | ||
714 | fido_blob_free(&npe); | ||
715 | fido_blob_free(&ph); | ||
716 | fido_blob_free(&phe); | ||
717 | |||
718 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L | ||
719 | if (ctx != NULL) | ||
720 | HMAC_CTX_free(ctx); | ||
721 | #endif | ||
722 | |||
723 | if (ok < 0) { | ||
724 | if (item != NULL) { | ||
725 | cbor_decref(&item); | ||
726 | item = NULL; | ||
727 | } | ||
728 | } | ||
729 | |||
730 | return (item); | ||
731 | } | ||
732 | |||
733 | cbor_item_t * | ||
734 | cbor_encode_set_pin_auth(const fido_blob_t *key, const fido_blob_t *pin) | ||
735 | { | ||
736 | const EVP_MD *md = NULL; | ||
737 | unsigned char dgst[SHA256_DIGEST_LENGTH]; | ||
738 | unsigned int dgst_len; | ||
739 | cbor_item_t *item = NULL; | ||
740 | fido_blob_t *pe = NULL; | ||
741 | |||
742 | if ((pe = fido_blob_new()) == NULL) | ||
743 | goto fail; | ||
744 | |||
745 | if (aes256_cbc_enc(key, pin, pe) < 0) { | ||
746 | fido_log_debug("%s: aes256_cbc_enc", __func__); | ||
747 | goto fail; | ||
748 | } | ||
749 | |||
750 | if ((md = EVP_sha256()) == NULL || key->len != 32 || HMAC(md, key->ptr, | ||
751 | (int)key->len, pe->ptr, (int)pe->len, dgst, &dgst_len) == NULL || | ||
752 | dgst_len != SHA256_DIGEST_LENGTH) { | ||
753 | fido_log_debug("%s: HMAC", __func__); | ||
754 | goto fail; | ||
755 | } | ||
756 | |||
757 | item = cbor_build_bytestring(dgst, 16); | ||
758 | fail: | ||
759 | fido_blob_free(&pe); | ||
760 | |||
761 | return (item); | ||
762 | } | ||
763 | |||
764 | cbor_item_t * | ||
765 | cbor_encode_pin_hash_enc(const fido_blob_t *shared, const fido_blob_t *pin) | ||
766 | { | ||
767 | cbor_item_t *item = NULL; | ||
768 | fido_blob_t *ph = NULL; | ||
769 | fido_blob_t *phe = NULL; | ||
770 | |||
771 | if ((ph = fido_blob_new()) == NULL || (phe = fido_blob_new()) == NULL) | ||
772 | goto fail; | ||
773 | |||
774 | if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) { | ||
775 | fido_log_debug("%s: SHA256", __func__); | ||
776 | goto fail; | ||
777 | } | ||
778 | |||
779 | ph->len = 16; /* first 16 bytes */ | ||
780 | |||
781 | if (aes256_cbc_enc(shared, ph, phe) < 0) { | ||
782 | fido_log_debug("%s: aes256_cbc_enc", __func__); | ||
783 | goto fail; | ||
784 | } | ||
785 | |||
786 | item = cbor_build_bytestring(phe->ptr, phe->len); | ||
787 | fail: | ||
788 | fido_blob_free(&ph); | ||
789 | fido_blob_free(&phe); | ||
790 | |||
791 | return (item); | ||
792 | } | ||
793 | |||
794 | cbor_item_t * | ||
795 | cbor_encode_hmac_secret_param(const fido_blob_t *ecdh, const es256_pk_t *pk, | ||
796 | const fido_blob_t *hmac_salt) | ||
797 | { | ||
798 | cbor_item_t *item = NULL; | ||
799 | cbor_item_t *param = NULL; | ||
800 | cbor_item_t *argv[3]; | ||
801 | struct cbor_pair pair; | ||
802 | |||
803 | memset(argv, 0, sizeof(argv)); | ||
804 | memset(&pair, 0, sizeof(pair)); | ||
805 | |||
806 | if (ecdh == NULL || pk == NULL || hmac_salt->ptr == NULL) { | ||
807 | fido_log_debug("%s: ecdh=%p, pk=%p, hmac_salt->ptr=%p", | ||
808 | __func__, (const void *)ecdh, (const void *)pk, | ||
809 | (const void *)hmac_salt->ptr); | ||
810 | goto fail; | ||
811 | } | ||
812 | |||
813 | if (hmac_salt->len != 32 && hmac_salt->len != 64) { | ||
814 | fido_log_debug("%s: hmac_salt->len=%zu", __func__, | ||
815 | hmac_salt->len); | ||
816 | goto fail; | ||
817 | } | ||
818 | |||
819 | /* XXX not pin, but salt */ | ||
820 | if ((argv[0] = es256_pk_encode(pk, 1)) == NULL || | ||
821 | (argv[1] = cbor_encode_pin_enc(ecdh, hmac_salt)) == NULL || | ||
822 | (argv[2] = cbor_encode_set_pin_auth(ecdh, hmac_salt)) == NULL) { | ||
823 | fido_log_debug("%s: cbor encode", __func__); | ||
824 | goto fail; | ||
825 | } | ||
826 | |||
827 | if ((param = cbor_flatten_vector(argv, 3)) == NULL) { | ||
828 | fido_log_debug("%s: cbor_flatten_vector", __func__); | ||
829 | goto fail; | ||
830 | } | ||
831 | |||
832 | if ((item = cbor_new_definite_map(1)) == NULL) { | ||
833 | fido_log_debug("%s: cbor_new_definite_map", __func__); | ||
834 | goto fail; | ||
835 | } | ||
836 | |||
837 | if ((pair.key = cbor_build_string("hmac-secret")) == NULL) { | ||
838 | fido_log_debug("%s: cbor_build", __func__); | ||
839 | goto fail; | ||
840 | } | ||
841 | |||
842 | pair.value = param; | ||
843 | |||
844 | if (!cbor_map_add(item, pair)) { | ||
845 | fido_log_debug("%s: cbor_map_add", __func__); | ||
846 | cbor_decref(&item); | ||
847 | item = NULL; | ||
848 | goto fail; | ||
849 | } | ||
850 | |||
851 | fail: | ||
852 | for (size_t i = 0; i < 3; i++) | ||
853 | if (argv[i] != NULL) | ||
854 | cbor_decref(&argv[i]); | ||
855 | |||
856 | if (param != NULL) | ||
857 | cbor_decref(¶m); | ||
858 | if (pair.key != NULL) | ||
859 | cbor_decref(&pair.key); | ||
860 | |||
861 | return (item); | ||
862 | } | ||
863 | |||
864 | int | ||
865 | cbor_decode_fmt(const cbor_item_t *item, char **fmt) | ||
866 | { | ||
867 | char *type = NULL; | ||
868 | |||
869 | if (cbor_string_copy(item, &type) < 0) { | ||
870 | fido_log_debug("%s: cbor_string_copy", __func__); | ||
871 | return (-1); | ||
872 | } | ||
873 | |||
874 | if (strcmp(type, "packed") && strcmp(type, "fido-u2f")) { | ||
875 | fido_log_debug("%s: type=%s", __func__, type); | ||
876 | free(type); | ||
877 | return (-1); | ||
878 | } | ||
879 | |||
880 | *fmt = type; | ||
881 | |||
882 | return (0); | ||
883 | } | ||
884 | |||
885 | struct cose_key { | ||
886 | int kty; | ||
887 | int alg; | ||
888 | int crv; | ||
889 | }; | ||
890 | |||
891 | static int | ||
892 | find_cose_alg(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
893 | { | ||
894 | struct cose_key *cose_key = arg; | ||
895 | |||
896 | if (cbor_isa_uint(key) == true && | ||
897 | cbor_int_get_width(key) == CBOR_INT_8) { | ||
898 | switch (cbor_get_uint8(key)) { | ||
899 | case 1: | ||
900 | if (cbor_isa_uint(val) == false || | ||
901 | cbor_get_int(val) > INT_MAX || cose_key->kty != 0) { | ||
902 | fido_log_debug("%s: kty", __func__); | ||
903 | return (-1); | ||
904 | } | ||
905 | |||
906 | cose_key->kty = (int)cbor_get_int(val); | ||
907 | |||
908 | break; | ||
909 | case 3: | ||
910 | if (cbor_isa_negint(val) == false || | ||
911 | cbor_get_int(val) > INT_MAX || cose_key->alg != 0) { | ||
912 | fido_log_debug("%s: alg", __func__); | ||
913 | return (-1); | ||
914 | } | ||
915 | |||
916 | cose_key->alg = -(int)cbor_get_int(val) - 1; | ||
917 | |||
918 | break; | ||
919 | } | ||
920 | } else if (cbor_isa_negint(key) == true && | ||
921 | cbor_int_get_width(key) == CBOR_INT_8) { | ||
922 | if (cbor_get_uint8(key) == 0) { | ||
923 | /* get crv if not rsa, otherwise ignore */ | ||
924 | if (cbor_isa_uint(val) == true && | ||
925 | cbor_get_int(val) <= INT_MAX && | ||
926 | cose_key->crv == 0) | ||
927 | cose_key->crv = (int)cbor_get_int(val); | ||
928 | } | ||
929 | } | ||
930 | |||
931 | return (0); | ||
932 | } | ||
933 | |||
934 | static int | ||
935 | get_cose_alg(const cbor_item_t *item, int *cose_alg) | ||
936 | { | ||
937 | struct cose_key cose_key; | ||
938 | |||
939 | memset(&cose_key, 0, sizeof(cose_key)); | ||
940 | |||
941 | *cose_alg = 0; | ||
942 | |||
943 | if (cbor_isa_map(item) == false || | ||
944 | cbor_map_is_definite(item) == false || | ||
945 | cbor_map_iter(item, &cose_key, find_cose_alg) < 0) { | ||
946 | fido_log_debug("%s: cbor type", __func__); | ||
947 | return (-1); | ||
948 | } | ||
949 | |||
950 | switch (cose_key.alg) { | ||
951 | case COSE_ES256: | ||
952 | if (cose_key.kty != COSE_KTY_EC2 || | ||
953 | cose_key.crv != COSE_P256) { | ||
954 | fido_log_debug("%s: invalid kty/crv", __func__); | ||
955 | return (-1); | ||
956 | } | ||
957 | |||
958 | break; | ||
959 | case COSE_EDDSA: | ||
960 | if (cose_key.kty != COSE_KTY_OKP || | ||
961 | cose_key.crv != COSE_ED25519) { | ||
962 | fido_log_debug("%s: invalid kty/crv", __func__); | ||
963 | return (-1); | ||
964 | } | ||
965 | |||
966 | break; | ||
967 | case COSE_RS256: | ||
968 | if (cose_key.kty != COSE_KTY_RSA) { | ||
969 | fido_log_debug("%s: invalid kty/crv", __func__); | ||
970 | return (-1); | ||
971 | } | ||
972 | |||
973 | break; | ||
974 | default: | ||
975 | fido_log_debug("%s: unknown alg %d", __func__, cose_key.alg); | ||
976 | |||
977 | return (-1); | ||
978 | } | ||
979 | |||
980 | *cose_alg = cose_key.alg; | ||
981 | |||
982 | return (0); | ||
983 | } | ||
984 | |||
985 | int | ||
986 | cbor_decode_pubkey(const cbor_item_t *item, int *type, void *key) | ||
987 | { | ||
988 | if (get_cose_alg(item, type) < 0) { | ||
989 | fido_log_debug("%s: get_cose_alg", __func__); | ||
990 | return (-1); | ||
991 | } | ||
992 | |||
993 | switch (*type) { | ||
994 | case COSE_ES256: | ||
995 | if (es256_pk_decode(item, key) < 0) { | ||
996 | fido_log_debug("%s: es256_pk_decode", __func__); | ||
997 | return (-1); | ||
998 | } | ||
999 | break; | ||
1000 | case COSE_RS256: | ||
1001 | if (rs256_pk_decode(item, key) < 0) { | ||
1002 | fido_log_debug("%s: rs256_pk_decode", __func__); | ||
1003 | return (-1); | ||
1004 | } | ||
1005 | break; | ||
1006 | case COSE_EDDSA: | ||
1007 | if (eddsa_pk_decode(item, key) < 0) { | ||
1008 | fido_log_debug("%s: eddsa_pk_decode", __func__); | ||
1009 | return (-1); | ||
1010 | } | ||
1011 | break; | ||
1012 | default: | ||
1013 | fido_log_debug("%s: invalid cose_alg %d", __func__, *type); | ||
1014 | return (-1); | ||
1015 | } | ||
1016 | |||
1017 | return (0); | ||
1018 | } | ||
1019 | |||
1020 | static int | ||
1021 | decode_attcred(const unsigned char **buf, size_t *len, int cose_alg, | ||
1022 | fido_attcred_t *attcred) | ||
1023 | { | ||
1024 | cbor_item_t *item = NULL; | ||
1025 | struct cbor_load_result cbor; | ||
1026 | uint16_t id_len; | ||
1027 | int ok = -1; | ||
1028 | |||
1029 | fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf, | ||
1030 | *len); | ||
1031 | |||
1032 | if (fido_buf_read(buf, len, &attcred->aaguid, | ||
1033 | sizeof(attcred->aaguid)) < 0) { | ||
1034 | fido_log_debug("%s: fido_buf_read aaguid", __func__); | ||
1035 | return (-1); | ||
1036 | } | ||
1037 | |||
1038 | if (fido_buf_read(buf, len, &id_len, sizeof(id_len)) < 0) { | ||
1039 | fido_log_debug("%s: fido_buf_read id_len", __func__); | ||
1040 | return (-1); | ||
1041 | } | ||
1042 | |||
1043 | attcred->id.len = (size_t)be16toh(id_len); | ||
1044 | if ((attcred->id.ptr = malloc(attcred->id.len)) == NULL) | ||
1045 | return (-1); | ||
1046 | |||
1047 | fido_log_debug("%s: attcred->id.len=%zu", __func__, attcred->id.len); | ||
1048 | |||
1049 | if (fido_buf_read(buf, len, attcred->id.ptr, attcred->id.len) < 0) { | ||
1050 | fido_log_debug("%s: fido_buf_read id", __func__); | ||
1051 | return (-1); | ||
1052 | } | ||
1053 | |||
1054 | if ((item = cbor_load(*buf, *len, &cbor)) == NULL) { | ||
1055 | fido_log_debug("%s: cbor_load", __func__); | ||
1056 | fido_log_xxd(*buf, *len); | ||
1057 | goto fail; | ||
1058 | } | ||
1059 | |||
1060 | if (cbor_decode_pubkey(item, &attcred->type, &attcred->pubkey) < 0) { | ||
1061 | fido_log_debug("%s: cbor_decode_pubkey", __func__); | ||
1062 | goto fail; | ||
1063 | } | ||
1064 | |||
1065 | if (attcred->type != cose_alg) { | ||
1066 | fido_log_debug("%s: cose_alg mismatch (%d != %d)", __func__, | ||
1067 | attcred->type, cose_alg); | ||
1068 | goto fail; | ||
1069 | } | ||
1070 | |||
1071 | *buf += cbor.read; | ||
1072 | *len -= cbor.read; | ||
1073 | |||
1074 | ok = 0; | ||
1075 | fail: | ||
1076 | if (item != NULL) | ||
1077 | cbor_decref(&item); | ||
1078 | |||
1079 | return (ok); | ||
1080 | } | ||
1081 | |||
1082 | static int | ||
1083 | decode_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
1084 | { | ||
1085 | int *authdata_ext = arg; | ||
1086 | char *type = NULL; | ||
1087 | int ok = -1; | ||
1088 | |||
1089 | if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) { | ||
1090 | fido_log_debug("%s: cbor type", __func__); | ||
1091 | ok = 0; /* ignore */ | ||
1092 | goto out; | ||
1093 | } | ||
1094 | |||
1095 | if (cbor_isa_float_ctrl(val) == false || | ||
1096 | cbor_float_get_width(val) != CBOR_FLOAT_0 || | ||
1097 | cbor_is_bool(val) == false || *authdata_ext != 0) { | ||
1098 | fido_log_debug("%s: cbor type", __func__); | ||
1099 | goto out; | ||
1100 | } | ||
1101 | |||
1102 | if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE) | ||
1103 | *authdata_ext |= FIDO_EXT_HMAC_SECRET; | ||
1104 | |||
1105 | ok = 0; | ||
1106 | out: | ||
1107 | free(type); | ||
1108 | |||
1109 | return (ok); | ||
1110 | } | ||
1111 | |||
1112 | static int | ||
1113 | decode_extensions(const unsigned char **buf, size_t *len, int *authdata_ext) | ||
1114 | { | ||
1115 | cbor_item_t *item = NULL; | ||
1116 | struct cbor_load_result cbor; | ||
1117 | int ok = -1; | ||
1118 | |||
1119 | fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf, | ||
1120 | *len); | ||
1121 | |||
1122 | *authdata_ext = 0; | ||
1123 | |||
1124 | if ((item = cbor_load(*buf, *len, &cbor)) == NULL) { | ||
1125 | fido_log_debug("%s: cbor_load", __func__); | ||
1126 | fido_log_xxd(*buf, *len); | ||
1127 | goto fail; | ||
1128 | } | ||
1129 | |||
1130 | if (cbor_isa_map(item) == false || | ||
1131 | cbor_map_is_definite(item) == false || | ||
1132 | cbor_map_size(item) != 1 || | ||
1133 | cbor_map_iter(item, authdata_ext, decode_extension) < 0) { | ||
1134 | fido_log_debug("%s: cbor type", __func__); | ||
1135 | goto fail; | ||
1136 | } | ||
1137 | |||
1138 | *buf += cbor.read; | ||
1139 | *len -= cbor.read; | ||
1140 | |||
1141 | ok = 0; | ||
1142 | fail: | ||
1143 | if (item != NULL) | ||
1144 | cbor_decref(&item); | ||
1145 | |||
1146 | return (ok); | ||
1147 | } | ||
1148 | |||
1149 | static int | ||
1150 | decode_hmac_secret_aux(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
1151 | { | ||
1152 | fido_blob_t *out = arg; | ||
1153 | char *type = NULL; | ||
1154 | int ok = -1; | ||
1155 | |||
1156 | if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) { | ||
1157 | fido_log_debug("%s: cbor type", __func__); | ||
1158 | ok = 0; /* ignore */ | ||
1159 | goto out; | ||
1160 | } | ||
1161 | |||
1162 | ok = cbor_bytestring_copy(val, &out->ptr, &out->len); | ||
1163 | out: | ||
1164 | free(type); | ||
1165 | |||
1166 | return (ok); | ||
1167 | } | ||
1168 | |||
1169 | static int | ||
1170 | decode_hmac_secret(const unsigned char **buf, size_t *len, fido_blob_t *out) | ||
1171 | { | ||
1172 | cbor_item_t *item = NULL; | ||
1173 | struct cbor_load_result cbor; | ||
1174 | int ok = -1; | ||
1175 | |||
1176 | fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf, | ||
1177 | *len); | ||
1178 | |||
1179 | if ((item = cbor_load(*buf, *len, &cbor)) == NULL) { | ||
1180 | fido_log_debug("%s: cbor_load", __func__); | ||
1181 | fido_log_xxd(*buf, *len); | ||
1182 | goto fail; | ||
1183 | } | ||
1184 | |||
1185 | if (cbor_isa_map(item) == false || | ||
1186 | cbor_map_is_definite(item) == false || | ||
1187 | cbor_map_size(item) != 1 || | ||
1188 | cbor_map_iter(item, out, decode_hmac_secret_aux) < 0) { | ||
1189 | fido_log_debug("%s: cbor type", __func__); | ||
1190 | goto fail; | ||
1191 | } | ||
1192 | |||
1193 | *buf += cbor.read; | ||
1194 | *len -= cbor.read; | ||
1195 | |||
1196 | ok = 0; | ||
1197 | fail: | ||
1198 | if (item != NULL) | ||
1199 | cbor_decref(&item); | ||
1200 | |||
1201 | return (ok); | ||
1202 | } | ||
1203 | |||
1204 | int | ||
1205 | cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg, | ||
1206 | fido_blob_t *authdata_cbor, fido_authdata_t *authdata, | ||
1207 | fido_attcred_t *attcred, int *authdata_ext) | ||
1208 | { | ||
1209 | const unsigned char *buf = NULL; | ||
1210 | size_t len; | ||
1211 | size_t alloc_len; | ||
1212 | |||
1213 | if (cbor_isa_bytestring(item) == false || | ||
1214 | cbor_bytestring_is_definite(item) == false) { | ||
1215 | fido_log_debug("%s: cbor type", __func__); | ||
1216 | return (-1); | ||
1217 | } | ||
1218 | |||
1219 | if (authdata_cbor->ptr != NULL || | ||
1220 | (authdata_cbor->len = cbor_serialize_alloc(item, | ||
1221 | &authdata_cbor->ptr, &alloc_len)) == 0) { | ||
1222 | fido_log_debug("%s: cbor_serialize_alloc", __func__); | ||
1223 | return (-1); | ||
1224 | } | ||
1225 | |||
1226 | buf = cbor_bytestring_handle(item); | ||
1227 | len = cbor_bytestring_length(item); | ||
1228 | |||
1229 | fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len); | ||
1230 | |||
1231 | if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) { | ||
1232 | fido_log_debug("%s: fido_buf_read", __func__); | ||
1233 | return (-1); | ||
1234 | } | ||
1235 | |||
1236 | authdata->sigcount = be32toh(authdata->sigcount); | ||
1237 | |||
1238 | if (attcred != NULL) { | ||
1239 | if ((authdata->flags & CTAP_AUTHDATA_ATT_CRED) == 0 || | ||
1240 | decode_attcred(&buf, &len, cose_alg, attcred) < 0) | ||
1241 | return (-1); | ||
1242 | } | ||
1243 | |||
1244 | if (authdata_ext != NULL) { | ||
1245 | if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 && | ||
1246 | decode_extensions(&buf, &len, authdata_ext) < 0) | ||
1247 | return (-1); | ||
1248 | } | ||
1249 | |||
1250 | /* XXX we should probably ensure that len == 0 at this point */ | ||
1251 | |||
1252 | return (FIDO_OK); | ||
1253 | } | ||
1254 | |||
1255 | int | ||
1256 | cbor_decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor, | ||
1257 | fido_authdata_t *authdata, int *authdata_ext, fido_blob_t *hmac_secret_enc) | ||
1258 | { | ||
1259 | const unsigned char *buf = NULL; | ||
1260 | size_t len; | ||
1261 | size_t alloc_len; | ||
1262 | |||
1263 | if (cbor_isa_bytestring(item) == false || | ||
1264 | cbor_bytestring_is_definite(item) == false) { | ||
1265 | fido_log_debug("%s: cbor type", __func__); | ||
1266 | return (-1); | ||
1267 | } | ||
1268 | |||
1269 | if (authdata_cbor->ptr != NULL || | ||
1270 | (authdata_cbor->len = cbor_serialize_alloc(item, | ||
1271 | &authdata_cbor->ptr, &alloc_len)) == 0) { | ||
1272 | fido_log_debug("%s: cbor_serialize_alloc", __func__); | ||
1273 | return (-1); | ||
1274 | } | ||
1275 | |||
1276 | buf = cbor_bytestring_handle(item); | ||
1277 | len = cbor_bytestring_length(item); | ||
1278 | |||
1279 | fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len); | ||
1280 | |||
1281 | if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) { | ||
1282 | fido_log_debug("%s: fido_buf_read", __func__); | ||
1283 | return (-1); | ||
1284 | } | ||
1285 | |||
1286 | authdata->sigcount = be32toh(authdata->sigcount); | ||
1287 | |||
1288 | *authdata_ext = 0; | ||
1289 | if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0) { | ||
1290 | /* XXX semantic leap: extensions -> hmac_secret */ | ||
1291 | if (decode_hmac_secret(&buf, &len, hmac_secret_enc) < 0) { | ||
1292 | fido_log_debug("%s: decode_hmac_secret", __func__); | ||
1293 | return (-1); | ||
1294 | } | ||
1295 | *authdata_ext = FIDO_EXT_HMAC_SECRET; | ||
1296 | } | ||
1297 | |||
1298 | /* XXX we should probably ensure that len == 0 at this point */ | ||
1299 | |||
1300 | return (FIDO_OK); | ||
1301 | } | ||
1302 | |||
1303 | static int | ||
1304 | decode_x5c(const cbor_item_t *item, void *arg) | ||
1305 | { | ||
1306 | fido_blob_t *x5c = arg; | ||
1307 | |||
1308 | if (x5c->len) | ||
1309 | return (0); /* ignore */ | ||
1310 | |||
1311 | return (cbor_bytestring_copy(item, &x5c->ptr, &x5c->len)); | ||
1312 | } | ||
1313 | |||
1314 | static int | ||
1315 | decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
1316 | { | ||
1317 | fido_attstmt_t *attstmt = arg; | ||
1318 | char *name = NULL; | ||
1319 | int ok = -1; | ||
1320 | |||
1321 | if (cbor_string_copy(key, &name) < 0) { | ||
1322 | fido_log_debug("%s: cbor type", __func__); | ||
1323 | ok = 0; /* ignore */ | ||
1324 | goto out; | ||
1325 | } | ||
1326 | |||
1327 | if (!strcmp(name, "alg")) { | ||
1328 | if (cbor_isa_negint(val) == false || | ||
1329 | cbor_int_get_width(val) != CBOR_INT_8 || | ||
1330 | cbor_get_uint8(val) != -COSE_ES256 - 1) { | ||
1331 | fido_log_debug("%s: alg", __func__); | ||
1332 | goto out; | ||
1333 | } | ||
1334 | } else if (!strcmp(name, "sig")) { | ||
1335 | if (cbor_bytestring_copy(val, &attstmt->sig.ptr, | ||
1336 | &attstmt->sig.len) < 0) { | ||
1337 | fido_log_debug("%s: sig", __func__); | ||
1338 | goto out; | ||
1339 | } | ||
1340 | } else if (!strcmp(name, "x5c")) { | ||
1341 | if (cbor_isa_array(val) == false || | ||
1342 | cbor_array_is_definite(val) == false || | ||
1343 | cbor_array_iter(val, &attstmt->x5c, decode_x5c) < 0) { | ||
1344 | fido_log_debug("%s: x5c", __func__); | ||
1345 | goto out; | ||
1346 | } | ||
1347 | } | ||
1348 | |||
1349 | ok = 0; | ||
1350 | out: | ||
1351 | free(name); | ||
1352 | |||
1353 | return (ok); | ||
1354 | } | ||
1355 | |||
1356 | int | ||
1357 | cbor_decode_attstmt(const cbor_item_t *item, fido_attstmt_t *attstmt) | ||
1358 | { | ||
1359 | if (cbor_isa_map(item) == false || | ||
1360 | cbor_map_is_definite(item) == false || | ||
1361 | cbor_map_iter(item, attstmt, decode_attstmt_entry) < 0) { | ||
1362 | fido_log_debug("%s: cbor type", __func__); | ||
1363 | return (-1); | ||
1364 | } | ||
1365 | |||
1366 | return (0); | ||
1367 | } | ||
1368 | |||
1369 | int | ||
1370 | cbor_decode_uint64(const cbor_item_t *item, uint64_t *n) | ||
1371 | { | ||
1372 | if (cbor_isa_uint(item) == false) { | ||
1373 | fido_log_debug("%s: cbor type", __func__); | ||
1374 | return (-1); | ||
1375 | } | ||
1376 | |||
1377 | *n = cbor_get_int(item); | ||
1378 | |||
1379 | return (0); | ||
1380 | } | ||
1381 | |||
1382 | static int | ||
1383 | decode_cred_id_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
1384 | { | ||
1385 | fido_blob_t *id = arg; | ||
1386 | char *name = NULL; | ||
1387 | int ok = -1; | ||
1388 | |||
1389 | if (cbor_string_copy(key, &name) < 0) { | ||
1390 | fido_log_debug("%s: cbor type", __func__); | ||
1391 | ok = 0; /* ignore */ | ||
1392 | goto out; | ||
1393 | } | ||
1394 | |||
1395 | if (!strcmp(name, "id")) | ||
1396 | if (cbor_bytestring_copy(val, &id->ptr, &id->len) < 0) { | ||
1397 | fido_log_debug("%s: cbor_bytestring_copy", __func__); | ||
1398 | goto out; | ||
1399 | } | ||
1400 | |||
1401 | ok = 0; | ||
1402 | out: | ||
1403 | free(name); | ||
1404 | |||
1405 | return (ok); | ||
1406 | } | ||
1407 | |||
1408 | int | ||
1409 | cbor_decode_cred_id(const cbor_item_t *item, fido_blob_t *id) | ||
1410 | { | ||
1411 | if (cbor_isa_map(item) == false || | ||
1412 | cbor_map_is_definite(item) == false || | ||
1413 | cbor_map_iter(item, id, decode_cred_id_entry) < 0) { | ||
1414 | fido_log_debug("%s: cbor type", __func__); | ||
1415 | return (-1); | ||
1416 | } | ||
1417 | |||
1418 | return (0); | ||
1419 | } | ||
1420 | |||
1421 | static int | ||
1422 | decode_user_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
1423 | { | ||
1424 | fido_user_t *user = arg; | ||
1425 | char *name = NULL; | ||
1426 | int ok = -1; | ||
1427 | |||
1428 | if (cbor_string_copy(key, &name) < 0) { | ||
1429 | fido_log_debug("%s: cbor type", __func__); | ||
1430 | ok = 0; /* ignore */ | ||
1431 | goto out; | ||
1432 | } | ||
1433 | |||
1434 | if (!strcmp(name, "icon")) { | ||
1435 | if (cbor_string_copy(val, &user->icon) < 0) { | ||
1436 | fido_log_debug("%s: icon", __func__); | ||
1437 | goto out; | ||
1438 | } | ||
1439 | } else if (!strcmp(name, "name")) { | ||
1440 | if (cbor_string_copy(val, &user->name) < 0) { | ||
1441 | fido_log_debug("%s: name", __func__); | ||
1442 | goto out; | ||
1443 | } | ||
1444 | } else if (!strcmp(name, "displayName")) { | ||
1445 | if (cbor_string_copy(val, &user->display_name) < 0) { | ||
1446 | fido_log_debug("%s: display_name", __func__); | ||
1447 | goto out; | ||
1448 | } | ||
1449 | } else if (!strcmp(name, "id")) { | ||
1450 | if (cbor_bytestring_copy(val, &user->id.ptr, &user->id.len) < 0) { | ||
1451 | fido_log_debug("%s: id", __func__); | ||
1452 | goto out; | ||
1453 | } | ||
1454 | } | ||
1455 | |||
1456 | ok = 0; | ||
1457 | out: | ||
1458 | free(name); | ||
1459 | |||
1460 | return (ok); | ||
1461 | } | ||
1462 | |||
1463 | int | ||
1464 | cbor_decode_user(const cbor_item_t *item, fido_user_t *user) | ||
1465 | { | ||
1466 | if (cbor_isa_map(item) == false || | ||
1467 | cbor_map_is_definite(item) == false || | ||
1468 | cbor_map_iter(item, user, decode_user_entry) < 0) { | ||
1469 | fido_log_debug("%s: cbor type", __func__); | ||
1470 | return (-1); | ||
1471 | } | ||
1472 | |||
1473 | return (0); | ||
1474 | } | ||
1475 | |||
1476 | static int | ||
1477 | decode_rp_entity_entry(const cbor_item_t *key, const cbor_item_t *val, | ||
1478 | void *arg) | ||
1479 | { | ||
1480 | fido_rp_t *rp = arg; | ||
1481 | char *name = NULL; | ||
1482 | int ok = -1; | ||
1483 | |||
1484 | if (cbor_string_copy(key, &name) < 0) { | ||
1485 | fido_log_debug("%s: cbor type", __func__); | ||
1486 | ok = 0; /* ignore */ | ||
1487 | goto out; | ||
1488 | } | ||
1489 | |||
1490 | if (!strcmp(name, "id")) { | ||
1491 | if (cbor_string_copy(val, &rp->id) < 0) { | ||
1492 | fido_log_debug("%s: id", __func__); | ||
1493 | goto out; | ||
1494 | } | ||
1495 | } else if (!strcmp(name, "name")) { | ||
1496 | if (cbor_string_copy(val, &rp->name) < 0) { | ||
1497 | fido_log_debug("%s: name", __func__); | ||
1498 | goto out; | ||
1499 | } | ||
1500 | } | ||
1501 | |||
1502 | ok = 0; | ||
1503 | out: | ||
1504 | free(name); | ||
1505 | |||
1506 | return (ok); | ||
1507 | } | ||
1508 | |||
1509 | int | ||
1510 | cbor_decode_rp_entity(const cbor_item_t *item, fido_rp_t *rp) | ||
1511 | { | ||
1512 | if (cbor_isa_map(item) == false || | ||
1513 | cbor_map_is_definite(item) == false || | ||
1514 | cbor_map_iter(item, rp, decode_rp_entity_entry) < 0) { | ||
1515 | fido_log_debug("%s: cbor type", __func__); | ||
1516 | return (-1); | ||
1517 | } | ||
1518 | |||
1519 | return (0); | ||
1520 | } | ||