1ca37791eSchristos /*
2ca37791eSchristos * Copyright (c) 2018 Yubico AB. All rights reserved.
3ca37791eSchristos * Use of this source code is governed by a BSD-style
4ca37791eSchristos * license that can be found in the LICENSE file.
5ca37791eSchristos */
6ca37791eSchristos
7ca37791eSchristos #include <openssl/hmac.h>
8ca37791eSchristos #include <openssl/sha.h>
9ca37791eSchristos #include "fido.h"
10ca37791eSchristos
11ca37791eSchristos static int
check_key_type(cbor_item_t * item)12ca37791eSchristos check_key_type(cbor_item_t *item)
13ca37791eSchristos {
14ca37791eSchristos if (item->type == CBOR_TYPE_UINT || item->type == CBOR_TYPE_NEGINT ||
15ca37791eSchristos item->type == CBOR_TYPE_STRING)
16ca37791eSchristos return (0);
17ca37791eSchristos
18ca37791eSchristos fido_log_debug("%s: invalid type: %d", __func__, item->type);
19ca37791eSchristos
20ca37791eSchristos return (-1);
21ca37791eSchristos }
22ca37791eSchristos
23ca37791eSchristos /*
24ca37791eSchristos * Validate CTAP2 canonical CBOR encoding rules for maps.
25ca37791eSchristos */
26ca37791eSchristos static int
ctap_check_cbor(cbor_item_t * prev,cbor_item_t * curr)27ca37791eSchristos ctap_check_cbor(cbor_item_t *prev, cbor_item_t *curr)
28ca37791eSchristos {
29ca37791eSchristos size_t curr_len;
30ca37791eSchristos size_t prev_len;
31ca37791eSchristos
32ca37791eSchristos if (check_key_type(prev) < 0 || check_key_type(curr) < 0)
33ca37791eSchristos return (-1);
34ca37791eSchristos
35ca37791eSchristos if (prev->type != curr->type) {
36ca37791eSchristos if (prev->type < curr->type)
37ca37791eSchristos return (0);
38ca37791eSchristos fido_log_debug("%s: unsorted types", __func__);
39ca37791eSchristos return (-1);
40ca37791eSchristos }
41ca37791eSchristos
42ca37791eSchristos if (curr->type == CBOR_TYPE_UINT || curr->type == CBOR_TYPE_NEGINT) {
43ca37791eSchristos if (cbor_int_get_width(curr) >= cbor_int_get_width(prev) &&
44ca37791eSchristos cbor_get_int(curr) > cbor_get_int(prev))
45ca37791eSchristos return (0);
46ca37791eSchristos } else {
47ca37791eSchristos curr_len = cbor_string_length(curr);
48ca37791eSchristos prev_len = cbor_string_length(prev);
49ca37791eSchristos
50ca37791eSchristos if (curr_len > prev_len || (curr_len == prev_len &&
51ca37791eSchristos memcmp(cbor_string_handle(prev), cbor_string_handle(curr),
52ca37791eSchristos curr_len) < 0))
53ca37791eSchristos return (0);
54ca37791eSchristos }
55ca37791eSchristos
56ca37791eSchristos fido_log_debug("%s: invalid cbor", __func__);
57ca37791eSchristos
58ca37791eSchristos return (-1);
59ca37791eSchristos }
60ca37791eSchristos
61ca37791eSchristos int
cbor_map_iter(const cbor_item_t * item,void * arg,int (* f)(const cbor_item_t *,const cbor_item_t *,void *))62ca37791eSchristos cbor_map_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *,
63ca37791eSchristos const cbor_item_t *, void *))
64ca37791eSchristos {
65ca37791eSchristos struct cbor_pair *v;
66ca37791eSchristos size_t n;
67ca37791eSchristos
68ca37791eSchristos if ((v = cbor_map_handle(item)) == NULL) {
69ca37791eSchristos fido_log_debug("%s: cbor_map_handle", __func__);
70ca37791eSchristos return (-1);
71ca37791eSchristos }
72ca37791eSchristos
73ca37791eSchristos n = cbor_map_size(item);
74ca37791eSchristos
75ca37791eSchristos for (size_t i = 0; i < n; i++) {
76ca37791eSchristos if (v[i].key == NULL || v[i].value == NULL) {
77ca37791eSchristos fido_log_debug("%s: key=%p, value=%p for i=%zu",
78ca37791eSchristos __func__, (void *)v[i].key, (void *)v[i].value, i);
79ca37791eSchristos return (-1);
80ca37791eSchristos }
81ca37791eSchristos if (i && ctap_check_cbor(v[i - 1].key, v[i].key) < 0) {
82ca37791eSchristos fido_log_debug("%s: ctap_check_cbor", __func__);
83ca37791eSchristos return (-1);
84ca37791eSchristos }
85ca37791eSchristos if (f(v[i].key, v[i].value, arg) < 0) {
86ca37791eSchristos fido_log_debug("%s: iterator < 0 on i=%zu", __func__,
87ca37791eSchristos i);
88ca37791eSchristos return (-1);
89ca37791eSchristos }
90ca37791eSchristos }
91ca37791eSchristos
92ca37791eSchristos return (0);
93ca37791eSchristos }
94ca37791eSchristos
95ca37791eSchristos int
cbor_array_iter(const cbor_item_t * item,void * arg,int (* f)(const cbor_item_t *,void *))96ca37791eSchristos cbor_array_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *,
97ca37791eSchristos void *))
98ca37791eSchristos {
99ca37791eSchristos cbor_item_t **v;
100ca37791eSchristos size_t n;
101ca37791eSchristos
102ca37791eSchristos if ((v = cbor_array_handle(item)) == NULL) {
103ca37791eSchristos fido_log_debug("%s: cbor_array_handle", __func__);
104ca37791eSchristos return (-1);
105ca37791eSchristos }
106ca37791eSchristos
107ca37791eSchristos n = cbor_array_size(item);
108ca37791eSchristos
109ca37791eSchristos for (size_t i = 0; i < n; i++)
110ca37791eSchristos if (v[i] == NULL || f(v[i], arg) < 0) {
111ca37791eSchristos fido_log_debug("%s: iterator < 0 on i=%zu,%p",
112ca37791eSchristos __func__, i, (void *)v[i]);
113ca37791eSchristos return (-1);
114ca37791eSchristos }
115ca37791eSchristos
116ca37791eSchristos return (0);
117ca37791eSchristos }
118ca37791eSchristos
119ca37791eSchristos int
cbor_parse_reply(const unsigned char * blob,size_t blob_len,void * arg,int (* parser)(const cbor_item_t *,const cbor_item_t *,void *))120ca37791eSchristos cbor_parse_reply(const unsigned char *blob, size_t blob_len, void *arg,
121ca37791eSchristos int(*parser)(const cbor_item_t *, const cbor_item_t *, void *))
122ca37791eSchristos {
123ca37791eSchristos cbor_item_t *item = NULL;
124ca37791eSchristos struct cbor_load_result cbor;
125ca37791eSchristos int r;
126ca37791eSchristos
127ca37791eSchristos if (blob_len < 1) {
128ca37791eSchristos fido_log_debug("%s: blob_len=%zu", __func__, blob_len);
129ca37791eSchristos r = FIDO_ERR_RX;
130ca37791eSchristos goto fail;
131ca37791eSchristos }
132ca37791eSchristos
133ca37791eSchristos if (blob[0] != FIDO_OK) {
134ca37791eSchristos fido_log_debug("%s: blob[0]=0x%02x", __func__, blob[0]);
135ca37791eSchristos r = blob[0];
136ca37791eSchristos goto fail;
137ca37791eSchristos }
138ca37791eSchristos
139ca37791eSchristos if ((item = cbor_load(blob + 1, blob_len - 1, &cbor)) == NULL) {
140ca37791eSchristos fido_log_debug("%s: cbor_load", __func__);
141ca37791eSchristos r = FIDO_ERR_RX_NOT_CBOR;
142ca37791eSchristos goto fail;
143ca37791eSchristos }
144ca37791eSchristos
145ca37791eSchristos if (cbor_isa_map(item) == false ||
146ca37791eSchristos cbor_map_is_definite(item) == false) {
147ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
148ca37791eSchristos r = FIDO_ERR_RX_INVALID_CBOR;
149ca37791eSchristos goto fail;
150ca37791eSchristos }
151ca37791eSchristos
152ca37791eSchristos if (cbor_map_iter(item, arg, parser) < 0) {
153ca37791eSchristos fido_log_debug("%s: cbor_map_iter", __func__);
154ca37791eSchristos r = FIDO_ERR_RX_INVALID_CBOR;
155ca37791eSchristos goto fail;
156ca37791eSchristos }
157ca37791eSchristos
158ca37791eSchristos r = FIDO_OK;
159ca37791eSchristos fail:
160ca37791eSchristos if (item != NULL)
161ca37791eSchristos cbor_decref(&item);
162ca37791eSchristos
163ca37791eSchristos return (r);
164ca37791eSchristos }
165ca37791eSchristos
166ca37791eSchristos void
cbor_vector_free(cbor_item_t ** item,size_t len)167ca37791eSchristos cbor_vector_free(cbor_item_t **item, size_t len)
168ca37791eSchristos {
169ca37791eSchristos for (size_t i = 0; i < len; i++)
170ca37791eSchristos if (item[i] != NULL)
171ca37791eSchristos cbor_decref(&item[i]);
172ca37791eSchristos }
173ca37791eSchristos
174ca37791eSchristos int
cbor_bytestring_copy(const cbor_item_t * item,unsigned char ** buf,size_t * len)175ca37791eSchristos cbor_bytestring_copy(const cbor_item_t *item, unsigned char **buf, size_t *len)
176ca37791eSchristos {
177ca37791eSchristos if (*buf != NULL || *len != 0) {
178ca37791eSchristos fido_log_debug("%s: dup", __func__);
179ca37791eSchristos return (-1);
180ca37791eSchristos }
181ca37791eSchristos
182ca37791eSchristos if (cbor_isa_bytestring(item) == false ||
183ca37791eSchristos cbor_bytestring_is_definite(item) == false) {
184ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
185ca37791eSchristos return (-1);
186ca37791eSchristos }
187ca37791eSchristos
188ca37791eSchristos *len = cbor_bytestring_length(item);
189ca37791eSchristos if ((*buf = malloc(*len)) == NULL) {
190ca37791eSchristos *len = 0;
191ca37791eSchristos return (-1);
192ca37791eSchristos }
193ca37791eSchristos
194ca37791eSchristos memcpy(*buf, cbor_bytestring_handle(item), *len);
195ca37791eSchristos
196ca37791eSchristos return (0);
197ca37791eSchristos }
198ca37791eSchristos
199ca37791eSchristos int
cbor_string_copy(const cbor_item_t * item,char ** str)200ca37791eSchristos cbor_string_copy(const cbor_item_t *item, char **str)
201ca37791eSchristos {
202ca37791eSchristos size_t len;
203ca37791eSchristos
204ca37791eSchristos if (*str != NULL) {
205ca37791eSchristos fido_log_debug("%s: dup", __func__);
206ca37791eSchristos return (-1);
207ca37791eSchristos }
208ca37791eSchristos
209ca37791eSchristos if (cbor_isa_string(item) == false ||
210ca37791eSchristos cbor_string_is_definite(item) == false) {
211ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
212ca37791eSchristos return (-1);
213ca37791eSchristos }
214ca37791eSchristos
215ca37791eSchristos if ((len = cbor_string_length(item)) == SIZE_MAX ||
216ca37791eSchristos (*str = malloc(len + 1)) == NULL)
217ca37791eSchristos return (-1);
218ca37791eSchristos
219ca37791eSchristos memcpy(*str, cbor_string_handle(item), len);
220ca37791eSchristos (*str)[len] = '\0';
221ca37791eSchristos
222ca37791eSchristos return (0);
223ca37791eSchristos }
224ca37791eSchristos
225ca37791eSchristos int
cbor_add_bytestring(cbor_item_t * item,const char * key,const unsigned char * value,size_t value_len)226ca37791eSchristos cbor_add_bytestring(cbor_item_t *item, const char *key,
227ca37791eSchristos const unsigned char *value, size_t value_len)
228ca37791eSchristos {
229ca37791eSchristos struct cbor_pair pair;
230ca37791eSchristos int ok = -1;
231ca37791eSchristos
232ca37791eSchristos memset(&pair, 0, sizeof(pair));
233ca37791eSchristos
234ca37791eSchristos if ((pair.key = cbor_build_string(key)) == NULL ||
235ca37791eSchristos (pair.value = cbor_build_bytestring(value, value_len)) == NULL) {
236ca37791eSchristos fido_log_debug("%s: cbor_build", __func__);
237ca37791eSchristos goto fail;
238ca37791eSchristos }
239ca37791eSchristos
240ca37791eSchristos if (!cbor_map_add(item, pair)) {
241ca37791eSchristos fido_log_debug("%s: cbor_map_add", __func__);
242ca37791eSchristos goto fail;
243ca37791eSchristos }
244ca37791eSchristos
245ca37791eSchristos ok = 0;
246ca37791eSchristos fail:
247ca37791eSchristos if (pair.key)
248ca37791eSchristos cbor_decref(&pair.key);
249ca37791eSchristos if (pair.value)
250ca37791eSchristos cbor_decref(&pair.value);
251ca37791eSchristos
252ca37791eSchristos return (ok);
253ca37791eSchristos }
254ca37791eSchristos
255ca37791eSchristos int
cbor_add_string(cbor_item_t * item,const char * key,const char * value)256ca37791eSchristos cbor_add_string(cbor_item_t *item, const char *key, const char *value)
257ca37791eSchristos {
258ca37791eSchristos struct cbor_pair pair;
259ca37791eSchristos int ok = -1;
260ca37791eSchristos
261ca37791eSchristos memset(&pair, 0, sizeof(pair));
262ca37791eSchristos
263ca37791eSchristos if ((pair.key = cbor_build_string(key)) == NULL ||
264ca37791eSchristos (pair.value = cbor_build_string(value)) == NULL) {
265ca37791eSchristos fido_log_debug("%s: cbor_build", __func__);
266ca37791eSchristos goto fail;
267ca37791eSchristos }
268ca37791eSchristos
269ca37791eSchristos if (!cbor_map_add(item, pair)) {
270ca37791eSchristos fido_log_debug("%s: cbor_map_add", __func__);
271ca37791eSchristos goto fail;
272ca37791eSchristos }
273ca37791eSchristos
274ca37791eSchristos ok = 0;
275ca37791eSchristos fail:
276ca37791eSchristos if (pair.key)
277ca37791eSchristos cbor_decref(&pair.key);
278ca37791eSchristos if (pair.value)
279ca37791eSchristos cbor_decref(&pair.value);
280ca37791eSchristos
281ca37791eSchristos return (ok);
282ca37791eSchristos }
283ca37791eSchristos
284ca37791eSchristos int
cbor_add_bool(cbor_item_t * item,const char * key,fido_opt_t value)285ca37791eSchristos cbor_add_bool(cbor_item_t *item, const char *key, fido_opt_t value)
286ca37791eSchristos {
287ca37791eSchristos struct cbor_pair pair;
288ca37791eSchristos int ok = -1;
289ca37791eSchristos
290ca37791eSchristos memset(&pair, 0, sizeof(pair));
291ca37791eSchristos
292ca37791eSchristos if ((pair.key = cbor_build_string(key)) == NULL ||
293ca37791eSchristos (pair.value = cbor_build_bool(value == FIDO_OPT_TRUE)) == NULL) {
294ca37791eSchristos fido_log_debug("%s: cbor_build", __func__);
295ca37791eSchristos goto fail;
296ca37791eSchristos }
297ca37791eSchristos
298ca37791eSchristos if (!cbor_map_add(item, pair)) {
299ca37791eSchristos fido_log_debug("%s: cbor_map_add", __func__);
300ca37791eSchristos goto fail;
301ca37791eSchristos }
302ca37791eSchristos
303ca37791eSchristos ok = 0;
304ca37791eSchristos fail:
305ca37791eSchristos if (pair.key)
306ca37791eSchristos cbor_decref(&pair.key);
307ca37791eSchristos if (pair.value)
308ca37791eSchristos cbor_decref(&pair.value);
309ca37791eSchristos
310ca37791eSchristos return (ok);
311ca37791eSchristos }
312ca37791eSchristos
313ca37791eSchristos static int
cbor_add_uint8(cbor_item_t * item,const char * key,uint8_t value)314ca37791eSchristos cbor_add_uint8(cbor_item_t *item, const char *key, uint8_t value)
315ca37791eSchristos {
316ca37791eSchristos struct cbor_pair pair;
317ca37791eSchristos int ok = -1;
318ca37791eSchristos
319ca37791eSchristos memset(&pair, 0, sizeof(pair));
320ca37791eSchristos
321ca37791eSchristos if ((pair.key = cbor_build_string(key)) == NULL ||
322ca37791eSchristos (pair.value = cbor_build_uint8(value)) == NULL) {
323ca37791eSchristos fido_log_debug("%s: cbor_build", __func__);
324ca37791eSchristos goto fail;
325ca37791eSchristos }
326ca37791eSchristos
327ca37791eSchristos if (!cbor_map_add(item, pair)) {
328ca37791eSchristos fido_log_debug("%s: cbor_map_add", __func__);
329ca37791eSchristos goto fail;
330ca37791eSchristos }
331ca37791eSchristos
332ca37791eSchristos ok = 0;
333ca37791eSchristos fail:
334ca37791eSchristos if (pair.key)
335ca37791eSchristos cbor_decref(&pair.key);
336ca37791eSchristos if (pair.value)
337ca37791eSchristos cbor_decref(&pair.value);
338ca37791eSchristos
339ca37791eSchristos return (ok);
340ca37791eSchristos }
341ca37791eSchristos
342ca37791eSchristos static int
cbor_add_arg(cbor_item_t * item,uint8_t n,cbor_item_t * arg)343ca37791eSchristos cbor_add_arg(cbor_item_t *item, uint8_t n, cbor_item_t *arg)
344ca37791eSchristos {
345ca37791eSchristos struct cbor_pair pair;
346ca37791eSchristos int ok = -1;
347ca37791eSchristos
348ca37791eSchristos memset(&pair, 0, sizeof(pair));
349ca37791eSchristos
350ca37791eSchristos if (arg == NULL)
351ca37791eSchristos return (0); /* empty argument */
352ca37791eSchristos
353ca37791eSchristos if ((pair.key = cbor_build_uint8(n)) == NULL) {
354ca37791eSchristos fido_log_debug("%s: cbor_build", __func__);
355ca37791eSchristos goto fail;
356ca37791eSchristos }
357ca37791eSchristos
358ca37791eSchristos pair.value = arg;
359ca37791eSchristos
360ca37791eSchristos if (!cbor_map_add(item, pair)) {
361ca37791eSchristos fido_log_debug("%s: cbor_map_add", __func__);
362ca37791eSchristos goto fail;
363ca37791eSchristos }
364ca37791eSchristos
365ca37791eSchristos ok = 0;
366ca37791eSchristos fail:
367ca37791eSchristos if (pair.key)
368ca37791eSchristos cbor_decref(&pair.key);
369ca37791eSchristos
370ca37791eSchristos return (ok);
371ca37791eSchristos }
372ca37791eSchristos
373ca37791eSchristos cbor_item_t *
cbor_flatten_vector(cbor_item_t * argv[],size_t argc)374ca37791eSchristos cbor_flatten_vector(cbor_item_t *argv[], size_t argc)
375ca37791eSchristos {
376ca37791eSchristos cbor_item_t *map;
377ca37791eSchristos uint8_t i;
378ca37791eSchristos
379ca37791eSchristos if (argc > UINT8_MAX - 1)
380ca37791eSchristos return (NULL);
381ca37791eSchristos
382ca37791eSchristos if ((map = cbor_new_definite_map(argc)) == NULL)
383ca37791eSchristos return (NULL);
384ca37791eSchristos
385ca37791eSchristos for (i = 0; i < argc; i++)
3865d97138dSchristos if (cbor_add_arg(map, (uint8_t)(i + 1), argv[i]) < 0)
387ca37791eSchristos break;
388ca37791eSchristos
389ca37791eSchristos if (i != argc) {
390ca37791eSchristos cbor_decref(&map);
391ca37791eSchristos map = NULL;
392ca37791eSchristos }
393ca37791eSchristos
394ca37791eSchristos return (map);
395ca37791eSchristos }
396ca37791eSchristos
397ca37791eSchristos int
cbor_build_frame(uint8_t cmd,cbor_item_t * argv[],size_t argc,fido_blob_t * f)398ca37791eSchristos cbor_build_frame(uint8_t cmd, cbor_item_t *argv[], size_t argc, fido_blob_t *f)
399ca37791eSchristos {
400ca37791eSchristos cbor_item_t *flat = NULL;
401ca37791eSchristos unsigned char *cbor = NULL;
402ca37791eSchristos size_t cbor_len;
403ca37791eSchristos size_t cbor_alloc_len;
404ca37791eSchristos int ok = -1;
405ca37791eSchristos
406ca37791eSchristos if ((flat = cbor_flatten_vector(argv, argc)) == NULL)
407ca37791eSchristos goto fail;
408ca37791eSchristos
409ca37791eSchristos cbor_len = cbor_serialize_alloc(flat, &cbor, &cbor_alloc_len);
410ca37791eSchristos if (cbor_len == 0 || cbor_len == SIZE_MAX) {
411ca37791eSchristos fido_log_debug("%s: cbor_len=%zu", __func__, cbor_len);
412ca37791eSchristos goto fail;
413ca37791eSchristos }
414ca37791eSchristos
415ca37791eSchristos if ((f->ptr = malloc(cbor_len + 1)) == NULL)
416ca37791eSchristos goto fail;
417ca37791eSchristos
418ca37791eSchristos f->len = cbor_len + 1;
419ca37791eSchristos f->ptr[0] = cmd;
420ca37791eSchristos memcpy(f->ptr + 1, cbor, f->len - 1);
421ca37791eSchristos
422ca37791eSchristos ok = 0;
423ca37791eSchristos fail:
424ca37791eSchristos if (flat != NULL)
425ca37791eSchristos cbor_decref(&flat);
426ca37791eSchristos
427ca37791eSchristos free(cbor);
428ca37791eSchristos
429ca37791eSchristos return (ok);
430ca37791eSchristos }
431ca37791eSchristos
432ca37791eSchristos cbor_item_t *
cbor_encode_rp_entity(const fido_rp_t * rp)433ca37791eSchristos cbor_encode_rp_entity(const fido_rp_t *rp)
434ca37791eSchristos {
435ca37791eSchristos cbor_item_t *item = NULL;
436ca37791eSchristos
437ca37791eSchristos if ((item = cbor_new_definite_map(2)) == NULL)
438ca37791eSchristos return (NULL);
439ca37791eSchristos
440ca37791eSchristos if ((rp->id && cbor_add_string(item, "id", rp->id) < 0) ||
441ca37791eSchristos (rp->name && cbor_add_string(item, "name", rp->name) < 0)) {
442ca37791eSchristos cbor_decref(&item);
443ca37791eSchristos return (NULL);
444ca37791eSchristos }
445ca37791eSchristos
446ca37791eSchristos return (item);
447ca37791eSchristos }
448ca37791eSchristos
449ca37791eSchristos cbor_item_t *
cbor_encode_user_entity(const fido_user_t * user)450ca37791eSchristos cbor_encode_user_entity(const fido_user_t *user)
451ca37791eSchristos {
452ca37791eSchristos cbor_item_t *item = NULL;
453ca37791eSchristos const fido_blob_t *id = &user->id;
454ca37791eSchristos const char *display = user->display_name;
455ca37791eSchristos
456ca37791eSchristos if ((item = cbor_new_definite_map(4)) == NULL)
457ca37791eSchristos return (NULL);
458ca37791eSchristos
459ca37791eSchristos if ((id->ptr && cbor_add_bytestring(item, "id", id->ptr, id->len) < 0) ||
460ca37791eSchristos (user->icon && cbor_add_string(item, "icon", user->icon) < 0) ||
461ca37791eSchristos (user->name && cbor_add_string(item, "name", user->name) < 0) ||
462ca37791eSchristos (display && cbor_add_string(item, "displayName", display) < 0)) {
463ca37791eSchristos cbor_decref(&item);
464ca37791eSchristos return (NULL);
465ca37791eSchristos }
466ca37791eSchristos
467ca37791eSchristos return (item);
468ca37791eSchristos }
469ca37791eSchristos
470ca37791eSchristos cbor_item_t *
cbor_encode_pubkey_param(int cose_alg)471ca37791eSchristos cbor_encode_pubkey_param(int cose_alg)
472ca37791eSchristos {
473ca37791eSchristos cbor_item_t *item = NULL;
474ca37791eSchristos cbor_item_t *body = NULL;
475ca37791eSchristos struct cbor_pair alg;
476ca37791eSchristos int ok = -1;
477ca37791eSchristos
478ca37791eSchristos memset(&alg, 0, sizeof(alg));
479ca37791eSchristos
480ca37791eSchristos if ((item = cbor_new_definite_array(1)) == NULL ||
481ca37791eSchristos (body = cbor_new_definite_map(2)) == NULL ||
482ca37791eSchristos cose_alg > -1 || cose_alg < INT16_MIN)
483ca37791eSchristos goto fail;
484ca37791eSchristos
485ca37791eSchristos alg.key = cbor_build_string("alg");
486ca37791eSchristos
487ca37791eSchristos if (-cose_alg - 1 > UINT8_MAX)
488ca37791eSchristos alg.value = cbor_build_negint16((uint16_t)(-cose_alg - 1));
489ca37791eSchristos else
490ca37791eSchristos alg.value = cbor_build_negint8((uint8_t)(-cose_alg - 1));
491ca37791eSchristos
492ca37791eSchristos if (alg.key == NULL || alg.value == NULL) {
493ca37791eSchristos fido_log_debug("%s: cbor_build", __func__);
494ca37791eSchristos goto fail;
495ca37791eSchristos }
496ca37791eSchristos
497ca37791eSchristos if (cbor_map_add(body, alg) == false ||
498ca37791eSchristos cbor_add_string(body, "type", "public-key") < 0 ||
499ca37791eSchristos cbor_array_push(item, body) == false)
500ca37791eSchristos goto fail;
501ca37791eSchristos
502ca37791eSchristos ok = 0;
503ca37791eSchristos fail:
504ca37791eSchristos if (ok < 0) {
505ca37791eSchristos if (item != NULL) {
506ca37791eSchristos cbor_decref(&item);
507ca37791eSchristos item = NULL;
508ca37791eSchristos }
509ca37791eSchristos }
510ca37791eSchristos
511ca37791eSchristos if (body != NULL)
512ca37791eSchristos cbor_decref(&body);
513ca37791eSchristos if (alg.key != NULL)
514ca37791eSchristos cbor_decref(&alg.key);
515ca37791eSchristos if (alg.value != NULL)
516ca37791eSchristos cbor_decref(&alg.value);
517ca37791eSchristos
518ca37791eSchristos return (item);
519ca37791eSchristos }
520ca37791eSchristos
521ca37791eSchristos cbor_item_t *
cbor_encode_pubkey(const fido_blob_t * pubkey)522ca37791eSchristos cbor_encode_pubkey(const fido_blob_t *pubkey)
523ca37791eSchristos {
524ca37791eSchristos cbor_item_t *cbor_key = NULL;
525ca37791eSchristos
526ca37791eSchristos if ((cbor_key = cbor_new_definite_map(2)) == NULL ||
527ca37791eSchristos cbor_add_bytestring(cbor_key, "id", pubkey->ptr, pubkey->len) < 0 ||
528ca37791eSchristos cbor_add_string(cbor_key, "type", "public-key") < 0) {
529ca37791eSchristos if (cbor_key)
530ca37791eSchristos cbor_decref(&cbor_key);
531ca37791eSchristos return (NULL);
532ca37791eSchristos }
533ca37791eSchristos
534ca37791eSchristos return (cbor_key);
535ca37791eSchristos }
536ca37791eSchristos
537ca37791eSchristos cbor_item_t *
cbor_encode_pubkey_list(const fido_blob_array_t * list)538ca37791eSchristos cbor_encode_pubkey_list(const fido_blob_array_t *list)
539ca37791eSchristos {
540ca37791eSchristos cbor_item_t *array = NULL;
541ca37791eSchristos cbor_item_t *key = NULL;
542ca37791eSchristos
543ca37791eSchristos if ((array = cbor_new_definite_array(list->len)) == NULL)
544ca37791eSchristos goto fail;
545ca37791eSchristos
546ca37791eSchristos for (size_t i = 0; i < list->len; i++) {
547ca37791eSchristos if ((key = cbor_encode_pubkey(&list->ptr[i])) == NULL ||
548ca37791eSchristos cbor_array_push(array, key) == false)
549ca37791eSchristos goto fail;
550ca37791eSchristos cbor_decref(&key);
551ca37791eSchristos }
552ca37791eSchristos
553ca37791eSchristos return (array);
554ca37791eSchristos fail:
555ca37791eSchristos if (key != NULL)
556ca37791eSchristos cbor_decref(&key);
557ca37791eSchristos if (array != NULL)
558ca37791eSchristos cbor_decref(&array);
559ca37791eSchristos
560ca37791eSchristos return (NULL);
561ca37791eSchristos }
562ca37791eSchristos
56398a5e356Schristos static int
cbor_encode_largeblob_key_ext(cbor_item_t * map)56498a5e356Schristos cbor_encode_largeblob_key_ext(cbor_item_t *map)
56598a5e356Schristos {
56698a5e356Schristos if (map == NULL ||
56798a5e356Schristos cbor_add_bool(map, "largeBlobKey", FIDO_OPT_TRUE) < 0)
56898a5e356Schristos return (-1);
56998a5e356Schristos
57098a5e356Schristos return (0);
57198a5e356Schristos }
57298a5e356Schristos
573ca37791eSchristos cbor_item_t *
cbor_encode_cred_ext(const fido_cred_ext_t * ext,const fido_blob_t * blob)57498a5e356Schristos cbor_encode_cred_ext(const fido_cred_ext_t *ext, const fido_blob_t *blob)
575ca37791eSchristos {
576ca37791eSchristos cbor_item_t *item = NULL;
577ca37791eSchristos size_t size = 0;
578ca37791eSchristos
57998a5e356Schristos if (ext->mask & FIDO_EXT_CRED_BLOB)
58098a5e356Schristos size++;
581ca37791eSchristos if (ext->mask & FIDO_EXT_HMAC_SECRET)
582ca37791eSchristos size++;
583ca37791eSchristos if (ext->mask & FIDO_EXT_CRED_PROTECT)
584ca37791eSchristos size++;
58598a5e356Schristos if (ext->mask & FIDO_EXT_LARGEBLOB_KEY)
58698a5e356Schristos size++;
58798a5e356Schristos
588ca37791eSchristos if (size == 0 || (item = cbor_new_definite_map(size)) == NULL)
589ca37791eSchristos return (NULL);
590ca37791eSchristos
59198a5e356Schristos if (ext->mask & FIDO_EXT_CRED_BLOB) {
59298a5e356Schristos if (cbor_add_bytestring(item, "credBlob", blob->ptr,
59398a5e356Schristos blob->len) < 0) {
594ca37791eSchristos cbor_decref(&item);
595ca37791eSchristos return (NULL);
596ca37791eSchristos }
597ca37791eSchristos }
598ca37791eSchristos if (ext->mask & FIDO_EXT_CRED_PROTECT) {
5995d97138dSchristos if (ext->prot < 0 || ext->prot > UINT8_MAX ||
6005d97138dSchristos cbor_add_uint8(item, "credProtect",
6015d97138dSchristos (uint8_t)ext->prot) < 0) {
602ca37791eSchristos cbor_decref(&item);
603ca37791eSchristos return (NULL);
604ca37791eSchristos }
605ca37791eSchristos }
60698a5e356Schristos if (ext->mask & FIDO_EXT_HMAC_SECRET) {
60798a5e356Schristos if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) {
60898a5e356Schristos cbor_decref(&item);
60998a5e356Schristos return (NULL);
61098a5e356Schristos }
61198a5e356Schristos }
61298a5e356Schristos if (ext->mask & FIDO_EXT_LARGEBLOB_KEY) {
61398a5e356Schristos if (cbor_encode_largeblob_key_ext(item) < 0) {
61498a5e356Schristos cbor_decref(&item);
61598a5e356Schristos return (NULL);
61698a5e356Schristos }
61798a5e356Schristos }
618ca37791eSchristos
619ca37791eSchristos return (item);
620ca37791eSchristos }
621ca37791eSchristos
622ca37791eSchristos cbor_item_t *
cbor_encode_cred_opt(fido_opt_t rk,fido_opt_t uv)62398a5e356Schristos cbor_encode_cred_opt(fido_opt_t rk, fido_opt_t uv)
624ca37791eSchristos {
625ca37791eSchristos cbor_item_t *item = NULL;
626ca37791eSchristos
627ca37791eSchristos if ((item = cbor_new_definite_map(2)) == NULL)
628ca37791eSchristos return (NULL);
629ca37791eSchristos if ((rk != FIDO_OPT_OMIT && cbor_add_bool(item, "rk", rk) < 0) ||
630ca37791eSchristos (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) {
631ca37791eSchristos cbor_decref(&item);
632ca37791eSchristos return (NULL);
633ca37791eSchristos }
634ca37791eSchristos
635ca37791eSchristos return (item);
636ca37791eSchristos }
637ca37791eSchristos
638ca37791eSchristos cbor_item_t *
cbor_encode_assert_opt(fido_opt_t up,fido_opt_t uv)63998a5e356Schristos cbor_encode_assert_opt(fido_opt_t up, fido_opt_t uv)
640ca37791eSchristos {
641ca37791eSchristos cbor_item_t *item = NULL;
642ca37791eSchristos
643ca37791eSchristos if ((item = cbor_new_definite_map(2)) == NULL)
644ca37791eSchristos return (NULL);
645ca37791eSchristos if ((up != FIDO_OPT_OMIT && cbor_add_bool(item, "up", up) < 0) ||
646ca37791eSchristos (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) {
647ca37791eSchristos cbor_decref(&item);
648ca37791eSchristos return (NULL);
649ca37791eSchristos }
650ca37791eSchristos
651ca37791eSchristos return (item);
652ca37791eSchristos }
653ca37791eSchristos
654ca37791eSchristos cbor_item_t *
cbor_encode_pin_auth(const fido_dev_t * dev,const fido_blob_t * secret,const fido_blob_t * data)65598a5e356Schristos cbor_encode_pin_auth(const fido_dev_t *dev, const fido_blob_t *secret,
65698a5e356Schristos const fido_blob_t *data)
657ca37791eSchristos {
658ca37791eSchristos const EVP_MD *md = NULL;
659ca37791eSchristos unsigned char dgst[SHA256_DIGEST_LENGTH];
660ca37791eSchristos unsigned int dgst_len;
66198a5e356Schristos size_t outlen;
66298a5e356Schristos uint8_t prot;
66398a5e356Schristos fido_blob_t key;
664ca37791eSchristos
66598a5e356Schristos
66698a5e356Schristos key.ptr = secret->ptr;
66798a5e356Schristos key.len = secret->len;
66898a5e356Schristos
66998a5e356Schristos if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
67098a5e356Schristos fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
67198a5e356Schristos return (NULL);
67298a5e356Schristos }
67398a5e356Schristos
67498a5e356Schristos /* select hmac portion of the shared secret */
67598a5e356Schristos if (prot == CTAP_PIN_PROTOCOL2 && key.len > 32)
67698a5e356Schristos key.len = 32;
67798a5e356Schristos
67898a5e356Schristos if ((md = EVP_sha256()) == NULL || HMAC(md, key.ptr,
67998a5e356Schristos (int)key.len, data->ptr, data->len, dgst,
680ca37791eSchristos &dgst_len) == NULL || dgst_len != SHA256_DIGEST_LENGTH)
681ca37791eSchristos return (NULL);
682ca37791eSchristos
68398a5e356Schristos outlen = (prot == CTAP_PIN_PROTOCOL1) ? 16 : dgst_len;
68498a5e356Schristos
68598a5e356Schristos return (cbor_build_bytestring(dgst, outlen));
686ca37791eSchristos }
687ca37791eSchristos
688ca37791eSchristos cbor_item_t *
cbor_encode_pin_opt(const fido_dev_t * dev)68998a5e356Schristos cbor_encode_pin_opt(const fido_dev_t *dev)
690ca37791eSchristos {
69198a5e356Schristos uint8_t prot;
692ca37791eSchristos
69398a5e356Schristos if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
69498a5e356Schristos fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
695ca37791eSchristos return (NULL);
696ca37791eSchristos }
697ca37791eSchristos
69898a5e356Schristos return (cbor_build_uint8(prot));
699ca37791eSchristos }
700ca37791eSchristos
701ca37791eSchristos cbor_item_t *
cbor_encode_change_pin_auth(const fido_dev_t * dev,const fido_blob_t * secret,const fido_blob_t * new_pin_enc,const fido_blob_t * pin_hash_enc)70298a5e356Schristos cbor_encode_change_pin_auth(const fido_dev_t *dev, const fido_blob_t *secret,
70398a5e356Schristos const fido_blob_t *new_pin_enc, const fido_blob_t *pin_hash_enc)
704ca37791eSchristos {
705ca37791eSchristos unsigned char dgst[SHA256_DIGEST_LENGTH];
706ca37791eSchristos unsigned int dgst_len;
707ca37791eSchristos cbor_item_t *item = NULL;
708ca37791eSchristos const EVP_MD *md = NULL;
709ca37791eSchristos #if OPENSSL_VERSION_NUMBER < 0x10100000L
710ca37791eSchristos HMAC_CTX ctx;
711ca37791eSchristos #else
712ca37791eSchristos HMAC_CTX *ctx = NULL;
713ca37791eSchristos #endif
71498a5e356Schristos fido_blob_t key;
71598a5e356Schristos uint8_t prot;
71698a5e356Schristos size_t outlen;
717ca37791eSchristos
71898a5e356Schristos key.ptr = secret->ptr;
71998a5e356Schristos key.len = secret->len;
720ca37791eSchristos
72198a5e356Schristos if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
72298a5e356Schristos fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
723ca37791eSchristos goto fail;
724ca37791eSchristos }
725ca37791eSchristos
72698a5e356Schristos if (prot == CTAP_PIN_PROTOCOL2 && key.len > 32)
72798a5e356Schristos key.len = 32;
728ca37791eSchristos
729ca37791eSchristos #if OPENSSL_VERSION_NUMBER < 0x10100000L
730ca37791eSchristos HMAC_CTX_init(&ctx);
731ca37791eSchristos
732ca37791eSchristos if ((md = EVP_sha256()) == NULL ||
73398a5e356Schristos HMAC_Init_ex(&ctx, key.ptr, (int)key.len, md, NULL) == 0 ||
73498a5e356Schristos HMAC_Update(&ctx, new_pin_enc->ptr, new_pin_enc->len) == 0 ||
73598a5e356Schristos HMAC_Update(&ctx, pin_hash_enc->ptr, pin_hash_enc->len) == 0 ||
73698a5e356Schristos HMAC_Final(&ctx, dgst, &dgst_len) == 0 ||
73798a5e356Schristos dgst_len != SHA256_DIGEST_LENGTH) {
738ca37791eSchristos fido_log_debug("%s: HMAC", __func__);
739ca37791eSchristos goto fail;
740ca37791eSchristos }
741ca37791eSchristos #else
742ca37791eSchristos if ((ctx = HMAC_CTX_new()) == NULL ||
743ca37791eSchristos (md = EVP_sha256()) == NULL ||
74498a5e356Schristos HMAC_Init_ex(ctx, key.ptr, (int)key.len, md, NULL) == 0 ||
74598a5e356Schristos HMAC_Update(ctx, new_pin_enc->ptr, new_pin_enc->len) == 0 ||
74698a5e356Schristos HMAC_Update(ctx, pin_hash_enc->ptr, pin_hash_enc->len) == 0 ||
74798a5e356Schristos HMAC_Final(ctx, dgst, &dgst_len) == 0 ||
74898a5e356Schristos dgst_len != SHA256_DIGEST_LENGTH) {
749ca37791eSchristos fido_log_debug("%s: HMAC", __func__);
750ca37791eSchristos goto fail;
751ca37791eSchristos }
752ca37791eSchristos #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
753ca37791eSchristos
75498a5e356Schristos outlen = (prot == CTAP_PIN_PROTOCOL1) ? 16 : dgst_len;
75598a5e356Schristos
75698a5e356Schristos if ((item = cbor_build_bytestring(dgst, outlen)) == NULL) {
757ca37791eSchristos fido_log_debug("%s: cbor_build_bytestring", __func__);
758ca37791eSchristos goto fail;
759ca37791eSchristos }
760ca37791eSchristos
761ca37791eSchristos fail:
762ca37791eSchristos #if OPENSSL_VERSION_NUMBER >= 0x10100000L
763ca37791eSchristos if (ctx != NULL)
764ca37791eSchristos HMAC_CTX_free(ctx);
765ca37791eSchristos #endif
766ca37791eSchristos
767ca37791eSchristos return (item);
768ca37791eSchristos }
769ca37791eSchristos
77098a5e356Schristos static int
cbor_encode_hmac_secret_param(const fido_dev_t * dev,cbor_item_t * item,const fido_blob_t * ecdh,const es256_pk_t * pk,const fido_blob_t * salt)77198a5e356Schristos cbor_encode_hmac_secret_param(const fido_dev_t *dev, cbor_item_t *item,
77298a5e356Schristos const fido_blob_t *ecdh, const es256_pk_t *pk, const fido_blob_t *salt)
773ca37791eSchristos {
774ca37791eSchristos cbor_item_t *param = NULL;
77598a5e356Schristos cbor_item_t *argv[4];
776ca37791eSchristos struct cbor_pair pair;
77798a5e356Schristos fido_blob_t *enc = NULL;
77898a5e356Schristos int r;
779ca37791eSchristos
780ca37791eSchristos memset(argv, 0, sizeof(argv));
781ca37791eSchristos memset(&pair, 0, sizeof(pair));
782ca37791eSchristos
78398a5e356Schristos if (item == NULL || ecdh == NULL || pk == NULL || salt->ptr == NULL) {
78498a5e356Schristos fido_log_debug("%s: ecdh=%p, pk=%p, salt->ptr=%p", __func__,
78598a5e356Schristos (const void *)ecdh, (const void *)pk,
78698a5e356Schristos (const void *)salt->ptr);
78798a5e356Schristos r = FIDO_ERR_INTERNAL;
788ca37791eSchristos goto fail;
789ca37791eSchristos }
790ca37791eSchristos
79198a5e356Schristos if (salt->len != 32 && salt->len != 64) {
79298a5e356Schristos fido_log_debug("%s: salt->len=%zu", __func__, salt->len);
79398a5e356Schristos r = FIDO_ERR_INTERNAL;
79498a5e356Schristos goto fail;
79598a5e356Schristos }
79698a5e356Schristos
79798a5e356Schristos if ((enc = fido_blob_new()) == NULL ||
79898a5e356Schristos aes256_cbc_enc(dev, ecdh, salt, enc) < 0) {
79998a5e356Schristos fido_log_debug("%s: aes256_cbc_enc", __func__);
80098a5e356Schristos r = FIDO_ERR_INTERNAL;
801ca37791eSchristos goto fail;
802ca37791eSchristos }
803ca37791eSchristos
804ca37791eSchristos /* XXX not pin, but salt */
805ca37791eSchristos if ((argv[0] = es256_pk_encode(pk, 1)) == NULL ||
80698a5e356Schristos (argv[1] = fido_blob_encode(enc)) == NULL ||
80798a5e356Schristos (argv[2] = cbor_encode_pin_auth(dev, ecdh, enc)) == NULL ||
80898a5e356Schristos (argv[3] = cbor_encode_pin_opt(dev)) == NULL) {
809ca37791eSchristos fido_log_debug("%s: cbor encode", __func__);
81098a5e356Schristos r = FIDO_ERR_INTERNAL;
811ca37791eSchristos goto fail;
812ca37791eSchristos }
813ca37791eSchristos
81498a5e356Schristos if ((param = cbor_flatten_vector(argv, nitems(argv))) == NULL) {
815ca37791eSchristos fido_log_debug("%s: cbor_flatten_vector", __func__);
81698a5e356Schristos r = FIDO_ERR_INTERNAL;
817ca37791eSchristos goto fail;
818ca37791eSchristos }
819ca37791eSchristos
820ca37791eSchristos if ((pair.key = cbor_build_string("hmac-secret")) == NULL) {
821ca37791eSchristos fido_log_debug("%s: cbor_build", __func__);
82298a5e356Schristos r = FIDO_ERR_INTERNAL;
823ca37791eSchristos goto fail;
824ca37791eSchristos }
825ca37791eSchristos
826ca37791eSchristos pair.value = param;
827ca37791eSchristos
828ca37791eSchristos if (!cbor_map_add(item, pair)) {
829ca37791eSchristos fido_log_debug("%s: cbor_map_add", __func__);
83098a5e356Schristos r = FIDO_ERR_INTERNAL;
831ca37791eSchristos goto fail;
832ca37791eSchristos }
833ca37791eSchristos
83498a5e356Schristos r = FIDO_OK;
83598a5e356Schristos
836ca37791eSchristos fail:
83798a5e356Schristos cbor_vector_free(argv, nitems(argv));
838ca37791eSchristos
839ca37791eSchristos if (param != NULL)
840ca37791eSchristos cbor_decref(¶m);
841ca37791eSchristos if (pair.key != NULL)
842ca37791eSchristos cbor_decref(&pair.key);
843ca37791eSchristos
84498a5e356Schristos fido_blob_free(&enc);
84598a5e356Schristos
84698a5e356Schristos return (r);
84798a5e356Schristos }
84898a5e356Schristos
84998a5e356Schristos cbor_item_t *
cbor_encode_assert_ext(fido_dev_t * dev,const fido_assert_ext_t * ext,const fido_blob_t * ecdh,const es256_pk_t * pk)85098a5e356Schristos cbor_encode_assert_ext(fido_dev_t *dev, const fido_assert_ext_t *ext,
85198a5e356Schristos const fido_blob_t *ecdh, const es256_pk_t *pk)
85298a5e356Schristos {
85398a5e356Schristos cbor_item_t *item = NULL;
85498a5e356Schristos size_t size = 0;
85598a5e356Schristos
85698a5e356Schristos if (ext->mask & FIDO_EXT_CRED_BLOB)
85798a5e356Schristos size++;
85898a5e356Schristos if (ext->mask & FIDO_EXT_HMAC_SECRET)
85998a5e356Schristos size++;
86098a5e356Schristos if (ext->mask & FIDO_EXT_LARGEBLOB_KEY)
86198a5e356Schristos size++;
86298a5e356Schristos if (size == 0 || (item = cbor_new_definite_map(size)) == NULL)
86398a5e356Schristos return (NULL);
86498a5e356Schristos
86598a5e356Schristos if (ext->mask & FIDO_EXT_CRED_BLOB) {
86698a5e356Schristos if (cbor_add_bool(item, "credBlob", FIDO_OPT_TRUE) < 0) {
86798a5e356Schristos cbor_decref(&item);
86898a5e356Schristos return (NULL);
86998a5e356Schristos }
87098a5e356Schristos }
87198a5e356Schristos if (ext->mask & FIDO_EXT_HMAC_SECRET) {
87298a5e356Schristos if (cbor_encode_hmac_secret_param(dev, item, ecdh, pk,
87398a5e356Schristos &ext->hmac_salt) < 0) {
87498a5e356Schristos cbor_decref(&item);
87598a5e356Schristos return (NULL);
87698a5e356Schristos }
87798a5e356Schristos }
87898a5e356Schristos if (ext->mask & FIDO_EXT_LARGEBLOB_KEY) {
87998a5e356Schristos if (cbor_encode_largeblob_key_ext(item) < 0) {
88098a5e356Schristos cbor_decref(&item);
88198a5e356Schristos return (NULL);
88298a5e356Schristos }
88398a5e356Schristos }
88498a5e356Schristos
885ca37791eSchristos return (item);
886ca37791eSchristos }
887ca37791eSchristos
888ca37791eSchristos int
cbor_decode_fmt(const cbor_item_t * item,char ** fmt)889ca37791eSchristos cbor_decode_fmt(const cbor_item_t *item, char **fmt)
890ca37791eSchristos {
891ca37791eSchristos char *type = NULL;
892ca37791eSchristos
893ca37791eSchristos if (cbor_string_copy(item, &type) < 0) {
894ca37791eSchristos fido_log_debug("%s: cbor_string_copy", __func__);
895ca37791eSchristos return (-1);
896ca37791eSchristos }
897ca37791eSchristos
898*b93b3e8cSchristos if (strcmp(type, "packed") && strcmp(type, "fido-u2f") &&
899*b93b3e8cSchristos strcmp(type, "none")) {
900ca37791eSchristos fido_log_debug("%s: type=%s", __func__, type);
901ca37791eSchristos free(type);
902ca37791eSchristos return (-1);
903ca37791eSchristos }
904ca37791eSchristos
905ca37791eSchristos *fmt = type;
906ca37791eSchristos
907ca37791eSchristos return (0);
908ca37791eSchristos }
909ca37791eSchristos
910ca37791eSchristos struct cose_key {
911ca37791eSchristos int kty;
912ca37791eSchristos int alg;
913ca37791eSchristos int crv;
914ca37791eSchristos };
915ca37791eSchristos
916ca37791eSchristos static int
find_cose_alg(const cbor_item_t * key,const cbor_item_t * val,void * arg)917ca37791eSchristos find_cose_alg(const cbor_item_t *key, const cbor_item_t *val, void *arg)
918ca37791eSchristos {
919ca37791eSchristos struct cose_key *cose_key = arg;
920ca37791eSchristos
921ca37791eSchristos if (cbor_isa_uint(key) == true &&
922ca37791eSchristos cbor_int_get_width(key) == CBOR_INT_8) {
923ca37791eSchristos switch (cbor_get_uint8(key)) {
924ca37791eSchristos case 1:
925ca37791eSchristos if (cbor_isa_uint(val) == false ||
926ca37791eSchristos cbor_get_int(val) > INT_MAX || cose_key->kty != 0) {
927ca37791eSchristos fido_log_debug("%s: kty", __func__);
928ca37791eSchristos return (-1);
929ca37791eSchristos }
930ca37791eSchristos
931ca37791eSchristos cose_key->kty = (int)cbor_get_int(val);
932ca37791eSchristos
933ca37791eSchristos break;
934ca37791eSchristos case 3:
935ca37791eSchristos if (cbor_isa_negint(val) == false ||
936ca37791eSchristos cbor_get_int(val) > INT_MAX || cose_key->alg != 0) {
937ca37791eSchristos fido_log_debug("%s: alg", __func__);
938ca37791eSchristos return (-1);
939ca37791eSchristos }
940ca37791eSchristos
941ca37791eSchristos cose_key->alg = -(int)cbor_get_int(val) - 1;
942ca37791eSchristos
943ca37791eSchristos break;
944ca37791eSchristos }
945ca37791eSchristos } else if (cbor_isa_negint(key) == true &&
946ca37791eSchristos cbor_int_get_width(key) == CBOR_INT_8) {
947ca37791eSchristos if (cbor_get_uint8(key) == 0) {
948ca37791eSchristos /* get crv if not rsa, otherwise ignore */
949ca37791eSchristos if (cbor_isa_uint(val) == true &&
950ca37791eSchristos cbor_get_int(val) <= INT_MAX &&
951ca37791eSchristos cose_key->crv == 0)
952ca37791eSchristos cose_key->crv = (int)cbor_get_int(val);
953ca37791eSchristos }
954ca37791eSchristos }
955ca37791eSchristos
956ca37791eSchristos return (0);
957ca37791eSchristos }
958ca37791eSchristos
959ca37791eSchristos static int
get_cose_alg(const cbor_item_t * item,int * cose_alg)960ca37791eSchristos get_cose_alg(const cbor_item_t *item, int *cose_alg)
961ca37791eSchristos {
962ca37791eSchristos struct cose_key cose_key;
963ca37791eSchristos
964ca37791eSchristos memset(&cose_key, 0, sizeof(cose_key));
965ca37791eSchristos
966ca37791eSchristos *cose_alg = 0;
967ca37791eSchristos
968ca37791eSchristos if (cbor_isa_map(item) == false ||
969ca37791eSchristos cbor_map_is_definite(item) == false ||
970ca37791eSchristos cbor_map_iter(item, &cose_key, find_cose_alg) < 0) {
971ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
972ca37791eSchristos return (-1);
973ca37791eSchristos }
974ca37791eSchristos
975ca37791eSchristos switch (cose_key.alg) {
976ca37791eSchristos case COSE_ES256:
977ca37791eSchristos if (cose_key.kty != COSE_KTY_EC2 ||
978ca37791eSchristos cose_key.crv != COSE_P256) {
979ca37791eSchristos fido_log_debug("%s: invalid kty/crv", __func__);
980ca37791eSchristos return (-1);
981ca37791eSchristos }
982ca37791eSchristos
983ca37791eSchristos break;
984ca37791eSchristos case COSE_EDDSA:
985ca37791eSchristos if (cose_key.kty != COSE_KTY_OKP ||
986ca37791eSchristos cose_key.crv != COSE_ED25519) {
987ca37791eSchristos fido_log_debug("%s: invalid kty/crv", __func__);
988ca37791eSchristos return (-1);
989ca37791eSchristos }
990ca37791eSchristos
991ca37791eSchristos break;
992ca37791eSchristos case COSE_RS256:
993ca37791eSchristos if (cose_key.kty != COSE_KTY_RSA) {
994ca37791eSchristos fido_log_debug("%s: invalid kty/crv", __func__);
995ca37791eSchristos return (-1);
996ca37791eSchristos }
997ca37791eSchristos
998ca37791eSchristos break;
999ca37791eSchristos default:
1000ca37791eSchristos fido_log_debug("%s: unknown alg %d", __func__, cose_key.alg);
1001ca37791eSchristos
1002ca37791eSchristos return (-1);
1003ca37791eSchristos }
1004ca37791eSchristos
1005ca37791eSchristos *cose_alg = cose_key.alg;
1006ca37791eSchristos
1007ca37791eSchristos return (0);
1008ca37791eSchristos }
1009ca37791eSchristos
1010ca37791eSchristos int
cbor_decode_pubkey(const cbor_item_t * item,int * type,void * key)1011ca37791eSchristos cbor_decode_pubkey(const cbor_item_t *item, int *type, void *key)
1012ca37791eSchristos {
1013ca37791eSchristos if (get_cose_alg(item, type) < 0) {
1014ca37791eSchristos fido_log_debug("%s: get_cose_alg", __func__);
1015ca37791eSchristos return (-1);
1016ca37791eSchristos }
1017ca37791eSchristos
1018ca37791eSchristos switch (*type) {
1019ca37791eSchristos case COSE_ES256:
1020ca37791eSchristos if (es256_pk_decode(item, key) < 0) {
1021ca37791eSchristos fido_log_debug("%s: es256_pk_decode", __func__);
1022ca37791eSchristos return (-1);
1023ca37791eSchristos }
1024ca37791eSchristos break;
1025ca37791eSchristos case COSE_RS256:
1026ca37791eSchristos if (rs256_pk_decode(item, key) < 0) {
1027ca37791eSchristos fido_log_debug("%s: rs256_pk_decode", __func__);
1028ca37791eSchristos return (-1);
1029ca37791eSchristos }
1030ca37791eSchristos break;
1031ca37791eSchristos case COSE_EDDSA:
1032ca37791eSchristos if (eddsa_pk_decode(item, key) < 0) {
1033ca37791eSchristos fido_log_debug("%s: eddsa_pk_decode", __func__);
1034ca37791eSchristos return (-1);
1035ca37791eSchristos }
1036ca37791eSchristos break;
1037ca37791eSchristos default:
1038ca37791eSchristos fido_log_debug("%s: invalid cose_alg %d", __func__, *type);
1039ca37791eSchristos return (-1);
1040ca37791eSchristos }
1041ca37791eSchristos
1042ca37791eSchristos return (0);
1043ca37791eSchristos }
1044ca37791eSchristos
1045ca37791eSchristos static int
decode_attcred(const unsigned char ** buf,size_t * len,int cose_alg,fido_attcred_t * attcred)1046ca37791eSchristos decode_attcred(const unsigned char **buf, size_t *len, int cose_alg,
1047ca37791eSchristos fido_attcred_t *attcred)
1048ca37791eSchristos {
1049ca37791eSchristos cbor_item_t *item = NULL;
1050ca37791eSchristos struct cbor_load_result cbor;
1051ca37791eSchristos uint16_t id_len;
1052ca37791eSchristos int ok = -1;
1053ca37791eSchristos
105498a5e356Schristos fido_log_xxd(*buf, *len, "%s", __func__);
1055ca37791eSchristos
1056ca37791eSchristos if (fido_buf_read(buf, len, &attcred->aaguid,
1057ca37791eSchristos sizeof(attcred->aaguid)) < 0) {
1058ca37791eSchristos fido_log_debug("%s: fido_buf_read aaguid", __func__);
1059ca37791eSchristos return (-1);
1060ca37791eSchristos }
1061ca37791eSchristos
1062ca37791eSchristos if (fido_buf_read(buf, len, &id_len, sizeof(id_len)) < 0) {
1063ca37791eSchristos fido_log_debug("%s: fido_buf_read id_len", __func__);
1064ca37791eSchristos return (-1);
1065ca37791eSchristos }
1066ca37791eSchristos
1067ca37791eSchristos attcred->id.len = (size_t)be16toh(id_len);
1068ca37791eSchristos if ((attcred->id.ptr = malloc(attcred->id.len)) == NULL)
1069ca37791eSchristos return (-1);
1070ca37791eSchristos
1071ca37791eSchristos fido_log_debug("%s: attcred->id.len=%zu", __func__, attcred->id.len);
1072ca37791eSchristos
1073ca37791eSchristos if (fido_buf_read(buf, len, attcred->id.ptr, attcred->id.len) < 0) {
1074ca37791eSchristos fido_log_debug("%s: fido_buf_read id", __func__);
1075ca37791eSchristos return (-1);
1076ca37791eSchristos }
1077ca37791eSchristos
1078ca37791eSchristos if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
1079ca37791eSchristos fido_log_debug("%s: cbor_load", __func__);
1080ca37791eSchristos goto fail;
1081ca37791eSchristos }
1082ca37791eSchristos
1083ca37791eSchristos if (cbor_decode_pubkey(item, &attcred->type, &attcred->pubkey) < 0) {
1084ca37791eSchristos fido_log_debug("%s: cbor_decode_pubkey", __func__);
1085ca37791eSchristos goto fail;
1086ca37791eSchristos }
1087ca37791eSchristos
1088ca37791eSchristos if (attcred->type != cose_alg) {
1089ca37791eSchristos fido_log_debug("%s: cose_alg mismatch (%d != %d)", __func__,
1090ca37791eSchristos attcred->type, cose_alg);
1091ca37791eSchristos goto fail;
1092ca37791eSchristos }
1093ca37791eSchristos
1094ca37791eSchristos *buf += cbor.read;
1095ca37791eSchristos *len -= cbor.read;
1096ca37791eSchristos
1097ca37791eSchristos ok = 0;
1098ca37791eSchristos fail:
1099ca37791eSchristos if (item != NULL)
1100ca37791eSchristos cbor_decref(&item);
1101ca37791eSchristos
1102ca37791eSchristos return (ok);
1103ca37791eSchristos }
1104ca37791eSchristos
1105ca37791eSchristos static int
decode_cred_extension(const cbor_item_t * key,const cbor_item_t * val,void * arg)110698a5e356Schristos decode_cred_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1107ca37791eSchristos {
1108ca37791eSchristos fido_cred_ext_t *authdata_ext = arg;
1109ca37791eSchristos char *type = NULL;
1110ca37791eSchristos int ok = -1;
1111ca37791eSchristos
1112ca37791eSchristos if (cbor_string_copy(key, &type) < 0) {
1113ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1114ca37791eSchristos ok = 0; /* ignore */
1115ca37791eSchristos goto out;
1116ca37791eSchristos }
1117ca37791eSchristos
1118ca37791eSchristos if (strcmp(type, "hmac-secret") == 0) {
1119ca37791eSchristos if (cbor_isa_float_ctrl(val) == false ||
1120ca37791eSchristos cbor_float_get_width(val) != CBOR_FLOAT_0 ||
1121ca37791eSchristos cbor_is_bool(val) == false) {
1122ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1123ca37791eSchristos goto out;
1124ca37791eSchristos }
1125ca37791eSchristos if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE)
1126ca37791eSchristos authdata_ext->mask |= FIDO_EXT_HMAC_SECRET;
1127ca37791eSchristos } else if (strcmp(type, "credProtect") == 0) {
1128ca37791eSchristos if (cbor_isa_uint(val) == false ||
1129ca37791eSchristos cbor_int_get_width(val) != CBOR_INT_8) {
1130ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1131ca37791eSchristos goto out;
1132ca37791eSchristos }
1133ca37791eSchristos authdata_ext->mask |= FIDO_EXT_CRED_PROTECT;
1134ca37791eSchristos authdata_ext->prot = cbor_get_uint8(val);
113598a5e356Schristos } else if (strcmp(type, "credBlob") == 0) {
113698a5e356Schristos if (cbor_isa_float_ctrl(val) == false ||
113798a5e356Schristos cbor_float_get_width(val) != CBOR_FLOAT_0 ||
113898a5e356Schristos cbor_is_bool(val) == false) {
113998a5e356Schristos fido_log_debug("%s: cbor type", __func__);
114098a5e356Schristos goto out;
114198a5e356Schristos }
114298a5e356Schristos if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE)
114398a5e356Schristos authdata_ext->mask |= FIDO_EXT_CRED_BLOB;
1144ca37791eSchristos }
1145ca37791eSchristos
1146ca37791eSchristos ok = 0;
1147ca37791eSchristos out:
1148ca37791eSchristos free(type);
1149ca37791eSchristos
1150ca37791eSchristos return (ok);
1151ca37791eSchristos }
1152ca37791eSchristos
1153ca37791eSchristos static int
decode_cred_extensions(const unsigned char ** buf,size_t * len,fido_cred_ext_t * authdata_ext)115498a5e356Schristos decode_cred_extensions(const unsigned char **buf, size_t *len,
1155ca37791eSchristos fido_cred_ext_t *authdata_ext)
1156ca37791eSchristos {
1157ca37791eSchristos cbor_item_t *item = NULL;
1158ca37791eSchristos struct cbor_load_result cbor;
1159ca37791eSchristos int ok = -1;
1160ca37791eSchristos
1161ca37791eSchristos memset(authdata_ext, 0, sizeof(*authdata_ext));
1162ca37791eSchristos
116398a5e356Schristos fido_log_xxd(*buf, *len, "%s", __func__);
116498a5e356Schristos
1165ca37791eSchristos if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
1166ca37791eSchristos fido_log_debug("%s: cbor_load", __func__);
1167ca37791eSchristos goto fail;
1168ca37791eSchristos }
1169ca37791eSchristos
1170ca37791eSchristos if (cbor_isa_map(item) == false ||
1171ca37791eSchristos cbor_map_is_definite(item) == false ||
117298a5e356Schristos cbor_map_iter(item, authdata_ext, decode_cred_extension) < 0) {
1173ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1174ca37791eSchristos goto fail;
1175ca37791eSchristos }
1176ca37791eSchristos
1177ca37791eSchristos *buf += cbor.read;
1178ca37791eSchristos *len -= cbor.read;
1179ca37791eSchristos
1180ca37791eSchristos ok = 0;
1181ca37791eSchristos fail:
1182ca37791eSchristos if (item != NULL)
1183ca37791eSchristos cbor_decref(&item);
1184ca37791eSchristos
1185ca37791eSchristos return (ok);
1186ca37791eSchristos }
1187ca37791eSchristos
1188ca37791eSchristos static int
decode_assert_extension(const cbor_item_t * key,const cbor_item_t * val,void * arg)118998a5e356Schristos decode_assert_extension(const cbor_item_t *key, const cbor_item_t *val,
119098a5e356Schristos void *arg)
1191ca37791eSchristos {
119298a5e356Schristos fido_assert_extattr_t *authdata_ext = arg;
1193ca37791eSchristos char *type = NULL;
1194ca37791eSchristos int ok = -1;
1195ca37791eSchristos
119698a5e356Schristos if (cbor_string_copy(key, &type) < 0) {
1197ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1198ca37791eSchristos ok = 0; /* ignore */
1199ca37791eSchristos goto out;
1200ca37791eSchristos }
1201ca37791eSchristos
120298a5e356Schristos if (strcmp(type, "hmac-secret") == 0) {
120398a5e356Schristos if (fido_blob_decode(val, &authdata_ext->hmac_secret_enc) < 0) {
120498a5e356Schristos fido_log_debug("%s: fido_blob_decode", __func__);
120598a5e356Schristos goto out;
120698a5e356Schristos }
120798a5e356Schristos authdata_ext->mask |= FIDO_EXT_HMAC_SECRET;
120898a5e356Schristos } else if (strcmp(type, "credBlob") == 0) {
120998a5e356Schristos if (fido_blob_decode(val, &authdata_ext->blob) < 0) {
121098a5e356Schristos fido_log_debug("%s: fido_blob_decode", __func__);
121198a5e356Schristos goto out;
121298a5e356Schristos }
121398a5e356Schristos authdata_ext->mask |= FIDO_EXT_CRED_BLOB;
121498a5e356Schristos }
121598a5e356Schristos
121698a5e356Schristos ok = 0;
1217ca37791eSchristos out:
1218ca37791eSchristos free(type);
1219ca37791eSchristos
1220ca37791eSchristos return (ok);
1221ca37791eSchristos }
1222ca37791eSchristos
1223ca37791eSchristos static int
decode_assert_extensions(const unsigned char ** buf,size_t * len,fido_assert_extattr_t * authdata_ext)122498a5e356Schristos decode_assert_extensions(const unsigned char **buf, size_t *len,
122598a5e356Schristos fido_assert_extattr_t *authdata_ext)
1226ca37791eSchristos {
1227ca37791eSchristos cbor_item_t *item = NULL;
1228ca37791eSchristos struct cbor_load_result cbor;
1229ca37791eSchristos int ok = -1;
1230ca37791eSchristos
123198a5e356Schristos fido_log_xxd(*buf, *len, "%s", __func__);
1232ca37791eSchristos
1233ca37791eSchristos if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
1234ca37791eSchristos fido_log_debug("%s: cbor_load", __func__);
1235ca37791eSchristos goto fail;
1236ca37791eSchristos }
1237ca37791eSchristos
1238ca37791eSchristos if (cbor_isa_map(item) == false ||
1239ca37791eSchristos cbor_map_is_definite(item) == false ||
124098a5e356Schristos cbor_map_iter(item, authdata_ext, decode_assert_extension) < 0) {
1241ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1242ca37791eSchristos goto fail;
1243ca37791eSchristos }
1244ca37791eSchristos
1245ca37791eSchristos *buf += cbor.read;
1246ca37791eSchristos *len -= cbor.read;
1247ca37791eSchristos
1248ca37791eSchristos ok = 0;
1249ca37791eSchristos fail:
1250ca37791eSchristos if (item != NULL)
1251ca37791eSchristos cbor_decref(&item);
1252ca37791eSchristos
1253ca37791eSchristos return (ok);
1254ca37791eSchristos }
1255ca37791eSchristos
1256ca37791eSchristos int
cbor_decode_cred_authdata(const cbor_item_t * item,int cose_alg,fido_blob_t * authdata_cbor,fido_authdata_t * authdata,fido_attcred_t * attcred,fido_cred_ext_t * authdata_ext)1257ca37791eSchristos cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
1258ca37791eSchristos fido_blob_t *authdata_cbor, fido_authdata_t *authdata,
1259ca37791eSchristos fido_attcred_t *attcred, fido_cred_ext_t *authdata_ext)
1260ca37791eSchristos {
1261ca37791eSchristos const unsigned char *buf = NULL;
1262ca37791eSchristos size_t len;
1263ca37791eSchristos size_t alloc_len;
1264ca37791eSchristos
1265ca37791eSchristos if (cbor_isa_bytestring(item) == false ||
1266ca37791eSchristos cbor_bytestring_is_definite(item) == false) {
1267ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1268ca37791eSchristos return (-1);
1269ca37791eSchristos }
1270ca37791eSchristos
1271ca37791eSchristos if (authdata_cbor->ptr != NULL ||
1272ca37791eSchristos (authdata_cbor->len = cbor_serialize_alloc(item,
1273ca37791eSchristos &authdata_cbor->ptr, &alloc_len)) == 0) {
1274ca37791eSchristos fido_log_debug("%s: cbor_serialize_alloc", __func__);
1275ca37791eSchristos return (-1);
1276ca37791eSchristos }
1277ca37791eSchristos
1278ca37791eSchristos buf = cbor_bytestring_handle(item);
1279ca37791eSchristos len = cbor_bytestring_length(item);
128098a5e356Schristos fido_log_xxd(buf, len, "%s", __func__);
1281ca37791eSchristos
1282ca37791eSchristos if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
1283ca37791eSchristos fido_log_debug("%s: fido_buf_read", __func__);
1284ca37791eSchristos return (-1);
1285ca37791eSchristos }
1286ca37791eSchristos
1287ca37791eSchristos authdata->sigcount = be32toh(authdata->sigcount);
1288ca37791eSchristos
1289ca37791eSchristos if (attcred != NULL) {
1290ca37791eSchristos if ((authdata->flags & CTAP_AUTHDATA_ATT_CRED) == 0 ||
1291ca37791eSchristos decode_attcred(&buf, &len, cose_alg, attcred) < 0)
1292ca37791eSchristos return (-1);
1293ca37791eSchristos }
1294ca37791eSchristos
1295ca37791eSchristos if (authdata_ext != NULL) {
1296ca37791eSchristos if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 &&
129798a5e356Schristos decode_cred_extensions(&buf, &len, authdata_ext) < 0)
1298ca37791eSchristos return (-1);
1299ca37791eSchristos }
1300ca37791eSchristos
1301ca37791eSchristos /* XXX we should probably ensure that len == 0 at this point */
1302ca37791eSchristos
1303ca37791eSchristos return (FIDO_OK);
1304ca37791eSchristos }
1305ca37791eSchristos
1306ca37791eSchristos int
cbor_decode_assert_authdata(const cbor_item_t * item,fido_blob_t * authdata_cbor,fido_authdata_t * authdata,fido_assert_extattr_t * authdata_ext)1307ca37791eSchristos cbor_decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor,
130898a5e356Schristos fido_authdata_t *authdata, fido_assert_extattr_t *authdata_ext)
1309ca37791eSchristos {
1310ca37791eSchristos const unsigned char *buf = NULL;
1311ca37791eSchristos size_t len;
1312ca37791eSchristos size_t alloc_len;
1313ca37791eSchristos
1314ca37791eSchristos if (cbor_isa_bytestring(item) == false ||
1315ca37791eSchristos cbor_bytestring_is_definite(item) == false) {
1316ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1317ca37791eSchristos return (-1);
1318ca37791eSchristos }
1319ca37791eSchristos
1320ca37791eSchristos if (authdata_cbor->ptr != NULL ||
1321ca37791eSchristos (authdata_cbor->len = cbor_serialize_alloc(item,
1322ca37791eSchristos &authdata_cbor->ptr, &alloc_len)) == 0) {
1323ca37791eSchristos fido_log_debug("%s: cbor_serialize_alloc", __func__);
1324ca37791eSchristos return (-1);
1325ca37791eSchristos }
1326ca37791eSchristos
1327ca37791eSchristos buf = cbor_bytestring_handle(item);
1328ca37791eSchristos len = cbor_bytestring_length(item);
1329ca37791eSchristos
1330ca37791eSchristos fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len);
1331ca37791eSchristos
1332ca37791eSchristos if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
1333ca37791eSchristos fido_log_debug("%s: fido_buf_read", __func__);
1334ca37791eSchristos return (-1);
1335ca37791eSchristos }
1336ca37791eSchristos
1337ca37791eSchristos authdata->sigcount = be32toh(authdata->sigcount);
1338ca37791eSchristos
1339ca37791eSchristos if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0) {
134098a5e356Schristos if (decode_assert_extensions(&buf, &len, authdata_ext) < 0) {
134198a5e356Schristos fido_log_debug("%s: decode_assert_extensions",
134298a5e356Schristos __func__);
1343ca37791eSchristos return (-1);
1344ca37791eSchristos }
1345ca37791eSchristos }
1346ca37791eSchristos
1347ca37791eSchristos /* XXX we should probably ensure that len == 0 at this point */
1348ca37791eSchristos
1349ca37791eSchristos return (FIDO_OK);
1350ca37791eSchristos }
1351ca37791eSchristos
1352ca37791eSchristos static int
decode_x5c(const cbor_item_t * item,void * arg)1353ca37791eSchristos decode_x5c(const cbor_item_t *item, void *arg)
1354ca37791eSchristos {
1355ca37791eSchristos fido_blob_t *x5c = arg;
1356ca37791eSchristos
1357ca37791eSchristos if (x5c->len)
1358ca37791eSchristos return (0); /* ignore */
1359ca37791eSchristos
136098a5e356Schristos return (fido_blob_decode(item, x5c));
1361ca37791eSchristos }
1362ca37791eSchristos
1363ca37791eSchristos static int
decode_attstmt_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1364ca37791eSchristos decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1365ca37791eSchristos {
1366ca37791eSchristos fido_attstmt_t *attstmt = arg;
1367ca37791eSchristos char *name = NULL;
1368ca37791eSchristos int cose_alg = 0;
1369ca37791eSchristos int ok = -1;
1370ca37791eSchristos
1371ca37791eSchristos if (cbor_string_copy(key, &name) < 0) {
1372ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1373ca37791eSchristos ok = 0; /* ignore */
1374ca37791eSchristos goto out;
1375ca37791eSchristos }
1376ca37791eSchristos
1377ca37791eSchristos if (!strcmp(name, "alg")) {
1378ca37791eSchristos if (cbor_isa_negint(val) == false ||
1379ca37791eSchristos cbor_get_int(val) > UINT16_MAX) {
1380ca37791eSchristos fido_log_debug("%s: alg", __func__);
1381ca37791eSchristos goto out;
1382ca37791eSchristos }
1383ca37791eSchristos if ((cose_alg = -(int)cbor_get_int(val) - 1) != COSE_ES256 &&
1384ca37791eSchristos cose_alg != COSE_RS256 && cose_alg != COSE_EDDSA) {
1385ca37791eSchristos fido_log_debug("%s: unsupported cose_alg=%d", __func__,
1386ca37791eSchristos cose_alg);
1387ca37791eSchristos goto out;
1388ca37791eSchristos }
1389ca37791eSchristos } else if (!strcmp(name, "sig")) {
139098a5e356Schristos if (fido_blob_decode(val, &attstmt->sig) < 0) {
1391ca37791eSchristos fido_log_debug("%s: sig", __func__);
1392ca37791eSchristos goto out;
1393ca37791eSchristos }
1394ca37791eSchristos } else if (!strcmp(name, "x5c")) {
1395ca37791eSchristos if (cbor_isa_array(val) == false ||
1396ca37791eSchristos cbor_array_is_definite(val) == false ||
1397ca37791eSchristos cbor_array_iter(val, &attstmt->x5c, decode_x5c) < 0) {
1398ca37791eSchristos fido_log_debug("%s: x5c", __func__);
1399ca37791eSchristos goto out;
1400ca37791eSchristos }
1401ca37791eSchristos }
1402ca37791eSchristos
1403ca37791eSchristos ok = 0;
1404ca37791eSchristos out:
1405ca37791eSchristos free(name);
1406ca37791eSchristos
1407ca37791eSchristos return (ok);
1408ca37791eSchristos }
1409ca37791eSchristos
1410ca37791eSchristos int
cbor_decode_attstmt(const cbor_item_t * item,fido_attstmt_t * attstmt)1411ca37791eSchristos cbor_decode_attstmt(const cbor_item_t *item, fido_attstmt_t *attstmt)
1412ca37791eSchristos {
1413ca37791eSchristos if (cbor_isa_map(item) == false ||
1414ca37791eSchristos cbor_map_is_definite(item) == false ||
1415ca37791eSchristos cbor_map_iter(item, attstmt, decode_attstmt_entry) < 0) {
1416ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1417ca37791eSchristos return (-1);
1418ca37791eSchristos }
1419ca37791eSchristos
1420ca37791eSchristos return (0);
1421ca37791eSchristos }
1422ca37791eSchristos
1423ca37791eSchristos int
cbor_decode_uint64(const cbor_item_t * item,uint64_t * n)1424ca37791eSchristos cbor_decode_uint64(const cbor_item_t *item, uint64_t *n)
1425ca37791eSchristos {
1426ca37791eSchristos if (cbor_isa_uint(item) == false) {
1427ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1428ca37791eSchristos return (-1);
1429ca37791eSchristos }
1430ca37791eSchristos
1431ca37791eSchristos *n = cbor_get_int(item);
1432ca37791eSchristos
1433ca37791eSchristos return (0);
1434ca37791eSchristos }
1435ca37791eSchristos
1436ca37791eSchristos static int
decode_cred_id_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1437ca37791eSchristos decode_cred_id_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1438ca37791eSchristos {
1439ca37791eSchristos fido_blob_t *id = arg;
1440ca37791eSchristos char *name = NULL;
1441ca37791eSchristos int ok = -1;
1442ca37791eSchristos
1443ca37791eSchristos if (cbor_string_copy(key, &name) < 0) {
1444ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1445ca37791eSchristos ok = 0; /* ignore */
1446ca37791eSchristos goto out;
1447ca37791eSchristos }
1448ca37791eSchristos
1449ca37791eSchristos if (!strcmp(name, "id"))
145098a5e356Schristos if (fido_blob_decode(val, id) < 0) {
1451ca37791eSchristos fido_log_debug("%s: cbor_bytestring_copy", __func__);
1452ca37791eSchristos goto out;
1453ca37791eSchristos }
1454ca37791eSchristos
1455ca37791eSchristos ok = 0;
1456ca37791eSchristos out:
1457ca37791eSchristos free(name);
1458ca37791eSchristos
1459ca37791eSchristos return (ok);
1460ca37791eSchristos }
1461ca37791eSchristos
1462ca37791eSchristos int
cbor_decode_cred_id(const cbor_item_t * item,fido_blob_t * id)1463ca37791eSchristos cbor_decode_cred_id(const cbor_item_t *item, fido_blob_t *id)
1464ca37791eSchristos {
1465ca37791eSchristos if (cbor_isa_map(item) == false ||
1466ca37791eSchristos cbor_map_is_definite(item) == false ||
1467ca37791eSchristos cbor_map_iter(item, id, decode_cred_id_entry) < 0) {
1468ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1469ca37791eSchristos return (-1);
1470ca37791eSchristos }
1471ca37791eSchristos
1472ca37791eSchristos return (0);
1473ca37791eSchristos }
1474ca37791eSchristos
1475ca37791eSchristos static int
decode_user_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1476ca37791eSchristos decode_user_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1477ca37791eSchristos {
1478ca37791eSchristos fido_user_t *user = arg;
1479ca37791eSchristos char *name = NULL;
1480ca37791eSchristos int ok = -1;
1481ca37791eSchristos
1482ca37791eSchristos if (cbor_string_copy(key, &name) < 0) {
1483ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1484ca37791eSchristos ok = 0; /* ignore */
1485ca37791eSchristos goto out;
1486ca37791eSchristos }
1487ca37791eSchristos
1488ca37791eSchristos if (!strcmp(name, "icon")) {
1489ca37791eSchristos if (cbor_string_copy(val, &user->icon) < 0) {
1490ca37791eSchristos fido_log_debug("%s: icon", __func__);
1491ca37791eSchristos goto out;
1492ca37791eSchristos }
1493ca37791eSchristos } else if (!strcmp(name, "name")) {
1494ca37791eSchristos if (cbor_string_copy(val, &user->name) < 0) {
1495ca37791eSchristos fido_log_debug("%s: name", __func__);
1496ca37791eSchristos goto out;
1497ca37791eSchristos }
1498ca37791eSchristos } else if (!strcmp(name, "displayName")) {
1499ca37791eSchristos if (cbor_string_copy(val, &user->display_name) < 0) {
1500ca37791eSchristos fido_log_debug("%s: display_name", __func__);
1501ca37791eSchristos goto out;
1502ca37791eSchristos }
1503ca37791eSchristos } else if (!strcmp(name, "id")) {
150498a5e356Schristos if (fido_blob_decode(val, &user->id) < 0) {
1505ca37791eSchristos fido_log_debug("%s: id", __func__);
1506ca37791eSchristos goto out;
1507ca37791eSchristos }
1508ca37791eSchristos }
1509ca37791eSchristos
1510ca37791eSchristos ok = 0;
1511ca37791eSchristos out:
1512ca37791eSchristos free(name);
1513ca37791eSchristos
1514ca37791eSchristos return (ok);
1515ca37791eSchristos }
1516ca37791eSchristos
1517ca37791eSchristos int
cbor_decode_user(const cbor_item_t * item,fido_user_t * user)1518ca37791eSchristos cbor_decode_user(const cbor_item_t *item, fido_user_t *user)
1519ca37791eSchristos {
1520ca37791eSchristos if (cbor_isa_map(item) == false ||
1521ca37791eSchristos cbor_map_is_definite(item) == false ||
1522ca37791eSchristos cbor_map_iter(item, user, decode_user_entry) < 0) {
1523ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1524ca37791eSchristos return (-1);
1525ca37791eSchristos }
1526ca37791eSchristos
1527ca37791eSchristos return (0);
1528ca37791eSchristos }
1529ca37791eSchristos
1530ca37791eSchristos static int
decode_rp_entity_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1531ca37791eSchristos decode_rp_entity_entry(const cbor_item_t *key, const cbor_item_t *val,
1532ca37791eSchristos void *arg)
1533ca37791eSchristos {
1534ca37791eSchristos fido_rp_t *rp = arg;
1535ca37791eSchristos char *name = NULL;
1536ca37791eSchristos int ok = -1;
1537ca37791eSchristos
1538ca37791eSchristos if (cbor_string_copy(key, &name) < 0) {
1539ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1540ca37791eSchristos ok = 0; /* ignore */
1541ca37791eSchristos goto out;
1542ca37791eSchristos }
1543ca37791eSchristos
1544ca37791eSchristos if (!strcmp(name, "id")) {
1545ca37791eSchristos if (cbor_string_copy(val, &rp->id) < 0) {
1546ca37791eSchristos fido_log_debug("%s: id", __func__);
1547ca37791eSchristos goto out;
1548ca37791eSchristos }
1549ca37791eSchristos } else if (!strcmp(name, "name")) {
1550ca37791eSchristos if (cbor_string_copy(val, &rp->name) < 0) {
1551ca37791eSchristos fido_log_debug("%s: name", __func__);
1552ca37791eSchristos goto out;
1553ca37791eSchristos }
1554ca37791eSchristos }
1555ca37791eSchristos
1556ca37791eSchristos ok = 0;
1557ca37791eSchristos out:
1558ca37791eSchristos free(name);
1559ca37791eSchristos
1560ca37791eSchristos return (ok);
1561ca37791eSchristos }
1562ca37791eSchristos
1563ca37791eSchristos int
cbor_decode_rp_entity(const cbor_item_t * item,fido_rp_t * rp)1564ca37791eSchristos cbor_decode_rp_entity(const cbor_item_t *item, fido_rp_t *rp)
1565ca37791eSchristos {
1566ca37791eSchristos if (cbor_isa_map(item) == false ||
1567ca37791eSchristos cbor_map_is_definite(item) == false ||
1568ca37791eSchristos cbor_map_iter(item, rp, decode_rp_entity_entry) < 0) {
1569ca37791eSchristos fido_log_debug("%s: cbor type", __func__);
1570ca37791eSchristos return (-1);
1571ca37791eSchristos }
1572ca37791eSchristos
1573ca37791eSchristos return (0);
1574ca37791eSchristos }
157598a5e356Schristos
157698a5e356Schristos cbor_item_t *
cbor_build_uint(const uint64_t value)157798a5e356Schristos cbor_build_uint(const uint64_t value)
157898a5e356Schristos {
157998a5e356Schristos if (value <= UINT8_MAX)
158098a5e356Schristos return cbor_build_uint8((uint8_t)value);
158198a5e356Schristos else if (value <= UINT16_MAX)
158298a5e356Schristos return cbor_build_uint16((uint16_t)value);
158398a5e356Schristos else if (value <= UINT32_MAX)
158498a5e356Schristos return cbor_build_uint32((uint32_t)value);
158598a5e356Schristos
158698a5e356Schristos return cbor_build_uint64(value);
158798a5e356Schristos }
158898a5e356Schristos
158998a5e356Schristos int
cbor_array_append(cbor_item_t ** array,cbor_item_t * item)159098a5e356Schristos cbor_array_append(cbor_item_t **array, cbor_item_t *item)
159198a5e356Schristos {
159298a5e356Schristos cbor_item_t **v, *ret;
159398a5e356Schristos size_t n;
159498a5e356Schristos
159598a5e356Schristos if ((v = cbor_array_handle(*array)) == NULL ||
159698a5e356Schristos (n = cbor_array_size(*array)) == SIZE_MAX ||
159798a5e356Schristos (ret = cbor_new_definite_array(n + 1)) == NULL)
159898a5e356Schristos return -1;
159998a5e356Schristos for (size_t i = 0; i < n; i++) {
160098a5e356Schristos if (cbor_array_push(ret, v[i]) == 0) {
160198a5e356Schristos cbor_decref(&ret);
160298a5e356Schristos return -1;
160398a5e356Schristos }
160498a5e356Schristos }
160598a5e356Schristos if (cbor_array_push(ret, item) == 0) {
160698a5e356Schristos cbor_decref(&ret);
160798a5e356Schristos return -1;
160898a5e356Schristos }
160998a5e356Schristos cbor_decref(array);
161098a5e356Schristos *array = ret;
161198a5e356Schristos
161298a5e356Schristos return 0;
161398a5e356Schristos }
161498a5e356Schristos
161598a5e356Schristos int
cbor_array_drop(cbor_item_t ** array,size_t idx)161698a5e356Schristos cbor_array_drop(cbor_item_t **array, size_t idx)
161798a5e356Schristos {
161898a5e356Schristos cbor_item_t **v, *ret;
161998a5e356Schristos size_t n;
162098a5e356Schristos
162198a5e356Schristos if ((v = cbor_array_handle(*array)) == NULL ||
162298a5e356Schristos (n = cbor_array_size(*array)) == 0 || idx >= n ||
162398a5e356Schristos (ret = cbor_new_definite_array(n - 1)) == NULL)
162498a5e356Schristos return -1;
162598a5e356Schristos for (size_t i = 0; i < n; i++) {
162698a5e356Schristos if (i != idx && cbor_array_push(ret, v[i]) == 0) {
162798a5e356Schristos cbor_decref(&ret);
162898a5e356Schristos return -1;
162998a5e356Schristos }
163098a5e356Schristos }
163198a5e356Schristos cbor_decref(array);
163298a5e356Schristos *array = ret;
163398a5e356Schristos
163498a5e356Schristos return 0;
163598a5e356Schristos }
1636