Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2018-2024 Yubico AB. All rights reserved. |
3 | | * Use of this source code is governed by a BSD-style |
4 | | * license that can be found in the LICENSE file. |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <openssl/sha.h> |
9 | | #include <openssl/x509.h> |
10 | | |
11 | | #include "fido.h" |
12 | | #include "fido/es256.h" |
13 | | |
14 | | #ifndef FIDO_MAXMSG_CRED |
15 | 7.99k | #define FIDO_MAXMSG_CRED 4096 |
16 | | #endif |
17 | | |
18 | | static int |
19 | | parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg) |
20 | 9.85k | { |
21 | 9.85k | fido_cred_t *cred = arg; |
22 | | |
23 | 9.85k | if (cbor_isa_uint(key) == false || |
24 | 9.85k | cbor_int_get_width(key) != CBOR_INT_8) { |
25 | 91 | fido_log_debug("%s: cbor type", __func__); |
26 | 91 | return (0); /* ignore */ |
27 | 91 | } |
28 | | |
29 | 9.75k | switch (cbor_get_uint8(key)) { |
30 | 3.57k | case 1: /* fmt */ |
31 | 3.57k | return (cbor_decode_fmt(val, &cred->fmt)); |
32 | 3.36k | case 2: /* authdata */ |
33 | 3.36k | if (fido_blob_decode(val, &cred->authdata_raw) < 0) { |
34 | 1 | fido_log_debug("%s: fido_blob_decode", __func__); |
35 | 1 | return (-1); |
36 | 1 | } |
37 | 3.36k | return (cbor_decode_cred_authdata(val, cred->type, |
38 | 3.36k | &cred->authdata_cbor, &cred->authdata, &cred->attcred, |
39 | 3.36k | &cred->authdata_ext)); |
40 | 2.69k | case 3: /* attestation statement */ |
41 | 2.69k | return (cbor_decode_attstmt(val, &cred->attstmt)); |
42 | 6 | case 4: /* enterprise attestation */ |
43 | 6 | return (cbor_decode_bool(val, &cred->ea.att)); |
44 | 4 | case 5: /* large blob key */ |
45 | 4 | return (fido_blob_decode(val, &cred->largeblob_key)); |
46 | 121 | default: /* ignore */ |
47 | 121 | fido_log_debug("%s: cbor type", __func__); |
48 | 121 | return (0); |
49 | 9.75k | } |
50 | 9.75k | } |
51 | | |
52 | | static int |
53 | | fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin, |
54 | | int *ms) |
55 | 6.24k | { |
56 | 6.24k | fido_blob_t f; |
57 | 6.24k | fido_blob_t *ecdh = NULL; |
58 | 6.24k | fido_opt_t uv = cred->uv; |
59 | 6.24k | es256_pk_t *pk = NULL; |
60 | 6.24k | cbor_item_t *argv[10]; |
61 | 6.24k | const uint8_t cmd = CTAP_CBOR_MAKECRED; |
62 | 6.24k | int r; |
63 | | |
64 | 6.24k | memset(&f, 0, sizeof(f)); |
65 | 6.24k | memset(argv, 0, sizeof(argv)); |
66 | | |
67 | 6.24k | if (cred->cdh.ptr == NULL || cred->type == 0) { |
68 | 7 | fido_log_debug("%s: cdh=%p, type=%d", __func__, |
69 | 7 | (void *)cred->cdh.ptr, cred->type); |
70 | 7 | r = FIDO_ERR_INVALID_ARGUMENT; |
71 | 7 | goto fail; |
72 | 7 | } |
73 | | |
74 | 6.24k | if ((argv[0] = fido_blob_encode(&cred->cdh)) == NULL || |
75 | 6.24k | (argv[1] = cbor_encode_rp_entity(&cred->rp)) == NULL || |
76 | 6.24k | (argv[2] = cbor_encode_user_entity(&cred->user)) == NULL || |
77 | 6.24k | (argv[3] = cbor_encode_pubkey_param(cred->type)) == NULL) { |
78 | 189 | fido_log_debug("%s: cbor encode", __func__); |
79 | 189 | r = FIDO_ERR_INTERNAL; |
80 | 189 | goto fail; |
81 | 189 | } |
82 | | |
83 | | /* excluded credentials */ |
84 | 6.05k | if (cred->excl.len) |
85 | 3.91k | if ((argv[4] = cbor_encode_pubkey_list(&cred->excl)) == NULL) { |
86 | 237 | fido_log_debug("%s: cbor_encode_pubkey_list", __func__); |
87 | 237 | r = FIDO_ERR_INTERNAL; |
88 | 237 | goto fail; |
89 | 237 | } |
90 | | |
91 | | /* extensions */ |
92 | 5.81k | if (cred->ext.mask) |
93 | 4.15k | if ((argv[5] = cbor_encode_cred_ext(&cred->ext, |
94 | 4.15k | &cred->blob)) == NULL) { |
95 | 47 | fido_log_debug("%s: cbor_encode_cred_ext", __func__); |
96 | 47 | r = FIDO_ERR_INTERNAL; |
97 | 47 | goto fail; |
98 | 47 | } |
99 | | |
100 | | /* user verification */ |
101 | 5.76k | if (pin != NULL || (uv == FIDO_OPT_TRUE && |
102 | 4.41k | fido_dev_supports_permissions(dev))) { |
103 | 4.41k | if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) { |
104 | 832 | fido_log_debug("%s: fido_do_ecdh", __func__); |
105 | 832 | goto fail; |
106 | 832 | } |
107 | 3.58k | if ((r = cbor_add_uv_params(dev, cmd, &cred->cdh, pk, ecdh, |
108 | 3.58k | pin, cred->rp.id, &argv[7], &argv[8], ms)) != FIDO_OK) { |
109 | 450 | fido_log_debug("%s: cbor_add_uv_params", __func__); |
110 | 450 | goto fail; |
111 | 450 | } |
112 | 3.13k | uv = FIDO_OPT_OMIT; |
113 | 3.13k | } |
114 | | |
115 | | /* options */ |
116 | 4.48k | if (cred->rk != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT) |
117 | 1.31k | if ((argv[6] = cbor_encode_cred_opt(cred->rk, uv)) == NULL) { |
118 | 15 | fido_log_debug("%s: cbor_encode_cred_opt", __func__); |
119 | 15 | r = FIDO_ERR_INTERNAL; |
120 | 15 | goto fail; |
121 | 15 | } |
122 | | |
123 | | /* enterprise attestation */ |
124 | 4.47k | if (cred->ea.mode != 0) |
125 | 1.78k | if ((argv[9] = cbor_build_uint8((uint8_t)cred->ea.mode)) == |
126 | 1.78k | NULL) { |
127 | 4 | fido_log_debug("%s: cbor_build_uint8", __func__); |
128 | 4 | r = FIDO_ERR_INTERNAL; |
129 | 4 | goto fail; |
130 | 4 | } |
131 | | |
132 | | /* framing and transmission */ |
133 | 4.46k | if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || |
134 | 4.46k | fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) { |
135 | 466 | fido_log_debug("%s: fido_tx", __func__); |
136 | 466 | r = FIDO_ERR_TX; |
137 | 466 | goto fail; |
138 | 466 | } |
139 | | |
140 | 4.00k | r = FIDO_OK; |
141 | 6.24k | fail: |
142 | 6.24k | es256_pk_free(&pk); |
143 | 6.24k | fido_blob_free(&ecdh); |
144 | 6.24k | cbor_vector_free(argv, nitems(argv)); |
145 | 6.24k | free(f.ptr); |
146 | | |
147 | 6.24k | return (r); |
148 | 4.00k | } |
149 | | |
150 | | static int |
151 | | fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int *ms) |
152 | 4.00k | { |
153 | 4.00k | unsigned char *reply; |
154 | 4.00k | int reply_len; |
155 | 4.00k | int r; |
156 | | |
157 | 4.00k | fido_cred_reset_rx(cred); |
158 | | |
159 | 4.00k | if ((reply = malloc(FIDO_MAXMSG_CRED)) == NULL) { |
160 | 3 | r = FIDO_ERR_INTERNAL; |
161 | 3 | goto fail; |
162 | 3 | } |
163 | | |
164 | 3.99k | if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, reply, FIDO_MAXMSG_CRED, |
165 | 3.99k | ms)) < 0) { |
166 | 211 | fido_log_debug("%s: fido_rx", __func__); |
167 | 211 | r = FIDO_ERR_RX; |
168 | 211 | goto fail; |
169 | 211 | } |
170 | | |
171 | 3.78k | if ((r = cbor_parse_reply(reply, (size_t)reply_len, cred, |
172 | 3.78k | parse_makecred_reply)) != FIDO_OK) { |
173 | 1.28k | fido_log_debug("%s: parse_makecred_reply", __func__); |
174 | 1.28k | goto fail; |
175 | 1.28k | } |
176 | | |
177 | 2.50k | if (cred->fmt == NULL || fido_blob_is_empty(&cred->authdata_cbor) || |
178 | 2.50k | fido_blob_is_empty(&cred->attcred.id)) { |
179 | 86 | r = FIDO_ERR_INVALID_CBOR; |
180 | 86 | goto fail; |
181 | 86 | } |
182 | | |
183 | 2.41k | r = FIDO_OK; |
184 | 4.00k | fail: |
185 | 4.00k | free(reply); |
186 | | |
187 | 4.00k | if (r != FIDO_OK) |
188 | 1.58k | fido_cred_reset_rx(cred); |
189 | | |
190 | 4.00k | return (r); |
191 | 2.41k | } |
192 | | |
193 | | static int |
194 | | fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin, |
195 | | int *ms) |
196 | 6.24k | { |
197 | 6.24k | int r; |
198 | | |
199 | 6.24k | if ((r = fido_dev_make_cred_tx(dev, cred, pin, ms)) != FIDO_OK || |
200 | 6.24k | (r = fido_dev_make_cred_rx(dev, cred, ms)) != FIDO_OK) |
201 | 3.83k | return (r); |
202 | | |
203 | 2.41k | return (FIDO_OK); |
204 | 6.24k | } |
205 | | |
206 | | int |
207 | | fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin) |
208 | 12.0k | { |
209 | 12.0k | int ms = dev->timeout_ms; |
210 | | |
211 | | #ifdef USE_WINHELLO |
212 | | if (dev->flags & FIDO_DEV_WINHELLO) |
213 | | return (fido_winhello_make_cred(dev, cred, pin, ms)); |
214 | | #endif |
215 | 12.0k | if (fido_dev_is_fido2(dev) == false) { |
216 | 5.79k | if (pin != NULL || cred->rk == FIDO_OPT_TRUE || |
217 | 5.79k | cred->ext.mask != 0) |
218 | 2.70k | return (FIDO_ERR_UNSUPPORTED_OPTION); |
219 | 3.08k | return (u2f_register(dev, cred, &ms)); |
220 | 5.79k | } |
221 | | |
222 | 6.24k | return (fido_dev_make_cred_wait(dev, cred, pin, &ms)); |
223 | 12.0k | } |
224 | | |
225 | | static int |
226 | | check_extensions(const fido_cred_ext_t *authdata_ext, |
227 | | const fido_cred_ext_t *ext) |
228 | 5.36k | { |
229 | 5.36k | fido_cred_ext_t tmp; |
230 | | |
231 | | /* XXX: largeBlobKey is not part of the extensions map */ |
232 | 5.36k | memcpy(&tmp, ext, sizeof(tmp)); |
233 | 5.36k | tmp.mask &= ~FIDO_EXT_LARGEBLOB_KEY; |
234 | | |
235 | 5.36k | return (timingsafe_bcmp(authdata_ext, &tmp, sizeof(*authdata_ext))); |
236 | 5.36k | } |
237 | | |
238 | | int |
239 | | fido_check_rp_id(const char *id, const unsigned char *obtained_hash) |
240 | 9.16k | { |
241 | 9.16k | unsigned char expected_hash[SHA256_DIGEST_LENGTH]; |
242 | | |
243 | 9.16k | explicit_bzero(expected_hash, sizeof(expected_hash)); |
244 | | |
245 | 9.16k | if (SHA256((const unsigned char *)id, strlen(id), |
246 | 9.16k | expected_hash) != expected_hash) { |
247 | 41 | fido_log_debug("%s: sha256", __func__); |
248 | 41 | return (-1); |
249 | 41 | } |
250 | | |
251 | 9.12k | return (timingsafe_bcmp(expected_hash, obtained_hash, |
252 | 9.12k | SHA256_DIGEST_LENGTH)); |
253 | 9.16k | } |
254 | | |
255 | | static int |
256 | | get_signed_hash_u2f(fido_blob_t *dgst, const unsigned char *rp_id, |
257 | | size_t rp_id_len, const fido_blob_t *clientdata, const fido_blob_t *id, |
258 | | const es256_pk_t *pk) |
259 | 581 | { |
260 | 581 | const uint8_t zero = 0; |
261 | 581 | const uint8_t four = 4; /* uncompressed point */ |
262 | 581 | const EVP_MD *md = NULL; |
263 | 581 | EVP_MD_CTX *ctx = NULL; |
264 | 581 | int ok = -1; |
265 | | |
266 | 581 | if (dgst->len < SHA256_DIGEST_LENGTH || |
267 | 581 | (md = EVP_sha256()) == NULL || |
268 | 581 | (ctx = EVP_MD_CTX_new()) == NULL || |
269 | 581 | EVP_DigestInit_ex(ctx, md, NULL) != 1 || |
270 | 581 | EVP_DigestUpdate(ctx, &zero, sizeof(zero)) != 1 || |
271 | 581 | EVP_DigestUpdate(ctx, rp_id, rp_id_len) != 1 || |
272 | 581 | EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 || |
273 | 581 | EVP_DigestUpdate(ctx, id->ptr, id->len) != 1 || |
274 | 581 | EVP_DigestUpdate(ctx, &four, sizeof(four)) != 1 || |
275 | 581 | EVP_DigestUpdate(ctx, pk->x, sizeof(pk->x)) != 1 || |
276 | 581 | EVP_DigestUpdate(ctx, pk->y, sizeof(pk->y)) != 1 || |
277 | 581 | EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) { |
278 | 50 | fido_log_debug("%s: sha256", __func__); |
279 | 50 | goto fail; |
280 | 50 | } |
281 | 531 | dgst->len = SHA256_DIGEST_LENGTH; |
282 | | |
283 | 531 | ok = 0; |
284 | 581 | fail: |
285 | 581 | EVP_MD_CTX_free(ctx); |
286 | | |
287 | 581 | return (ok); |
288 | 531 | } |
289 | | |
290 | | static int |
291 | | verify_attstmt(const fido_blob_t *dgst, const fido_attstmt_t *attstmt) |
292 | 4.57k | { |
293 | 4.57k | BIO *rawcert = NULL; |
294 | 4.57k | X509 *cert = NULL; |
295 | 4.57k | EVP_PKEY *pkey = NULL; |
296 | 4.57k | int ok = -1; |
297 | | |
298 | 4.57k | if (!attstmt->x5c.len) { |
299 | 0 | fido_log_debug("%s: x5c.len=%zu", __func__, attstmt->x5c.len); |
300 | 0 | return (-1); |
301 | 0 | } |
302 | | |
303 | | /* openssl needs ints */ |
304 | 4.57k | if (attstmt->x5c.ptr[0].len > INT_MAX) { |
305 | 0 | fido_log_debug("%s: x5c[0].len=%zu", __func__, |
306 | 0 | attstmt->x5c.ptr[0].len); |
307 | 0 | return (-1); |
308 | 0 | } |
309 | | |
310 | | /* fetch key from x509 */ |
311 | 4.57k | if ((rawcert = BIO_new_mem_buf(attstmt->x5c.ptr[0].ptr, |
312 | 4.57k | (int)attstmt->x5c.ptr[0].len)) == NULL || |
313 | 4.57k | (cert = d2i_X509_bio(rawcert, NULL)) == NULL || |
314 | 4.57k | (pkey = X509_get_pubkey(cert)) == NULL) { |
315 | 2.95k | fido_log_debug("%s: x509 key", __func__); |
316 | 2.95k | goto fail; |
317 | 2.95k | } |
318 | | |
319 | 1.62k | switch (attstmt->alg) { |
320 | 25 | case COSE_UNSPEC: |
321 | 1.11k | case COSE_ES256: |
322 | 1.11k | ok = es256_verify_sig(dgst, pkey, &attstmt->sig); |
323 | 1.11k | break; |
324 | 9 | case COSE_ES384: |
325 | 9 | ok = es384_verify_sig(dgst, pkey, &attstmt->sig); |
326 | 9 | break; |
327 | 4 | case COSE_RS256: |
328 | 4 | ok = rs256_verify_sig(dgst, pkey, &attstmt->sig); |
329 | 4 | break; |
330 | 481 | case COSE_RS1: |
331 | 481 | ok = rs1_verify_sig(dgst, pkey, &attstmt->sig); |
332 | 481 | break; |
333 | 13 | case COSE_EDDSA: |
334 | 13 | ok = eddsa_verify_sig(dgst, pkey, &attstmt->sig); |
335 | 13 | break; |
336 | 0 | default: |
337 | 0 | fido_log_debug("%s: unknown alg %d", __func__, attstmt->alg); |
338 | 0 | break; |
339 | 1.62k | } |
340 | | |
341 | 4.57k | fail: |
342 | 4.57k | BIO_free(rawcert); |
343 | 4.57k | X509_free(cert); |
344 | 4.57k | EVP_PKEY_free(pkey); |
345 | | |
346 | 4.57k | return (ok); |
347 | 1.62k | } |
348 | | |
349 | | int |
350 | | fido_cred_verify(const fido_cred_t *cred) |
351 | 19.5k | { |
352 | 19.5k | unsigned char buf[1024]; /* XXX */ |
353 | 19.5k | fido_blob_t dgst; |
354 | 19.5k | int cose_alg; |
355 | 19.5k | int r; |
356 | | |
357 | 19.5k | dgst.ptr = buf; |
358 | 19.5k | dgst.len = sizeof(buf); |
359 | | |
360 | | /* do we have everything we need? */ |
361 | 19.5k | if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL || |
362 | 19.5k | cred->attstmt.x5c.ptr == NULL || cred->attstmt.sig.ptr == NULL || |
363 | 19.5k | cred->fmt == NULL || cred->attcred.id.ptr == NULL || |
364 | 19.5k | cred->rp.id == NULL) { |
365 | 14.4k | fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, " |
366 | 14.4k | "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr, |
367 | 14.4k | (void *)cred->authdata_cbor.ptr, |
368 | 14.4k | (void *)cred->attstmt.x5c.ptr, |
369 | 14.4k | (void *)cred->attstmt.sig.ptr, (void *)cred->fmt, |
370 | 14.4k | (void *)cred->attcred.id.ptr, cred->rp.id); |
371 | 14.4k | r = FIDO_ERR_INVALID_ARGUMENT; |
372 | 14.4k | goto out; |
373 | 14.4k | } |
374 | | |
375 | 5.12k | if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) { |
376 | 330 | fido_log_debug("%s: fido_check_rp_id", __func__); |
377 | 330 | r = FIDO_ERR_INVALID_PARAM; |
378 | 330 | goto out; |
379 | 330 | } |
380 | | |
381 | 4.79k | if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE, |
382 | 4.79k | cred->uv) < 0) { |
383 | 7 | fido_log_debug("%s: fido_check_flags", __func__); |
384 | 7 | r = FIDO_ERR_INVALID_PARAM; |
385 | 7 | goto out; |
386 | 7 | } |
387 | | |
388 | 4.78k | if (check_extensions(&cred->authdata_ext, &cred->ext) != 0) { |
389 | 8 | fido_log_debug("%s: check_extensions", __func__); |
390 | 8 | r = FIDO_ERR_INVALID_PARAM; |
391 | 8 | goto out; |
392 | 8 | } |
393 | | |
394 | 4.77k | if ((cose_alg = cred->attstmt.alg) == COSE_UNSPEC) |
395 | 137 | cose_alg = COSE_ES256; /* backwards compat */ |
396 | | |
397 | 4.77k | if (!strcmp(cred->fmt, "packed")) { |
398 | 3.47k | if (fido_get_signed_hash(cose_alg, &dgst, &cred->cdh, |
399 | 3.47k | &cred->authdata_cbor) < 0) { |
400 | 72 | fido_log_debug("%s: fido_get_signed_hash", __func__); |
401 | 72 | r = FIDO_ERR_INTERNAL; |
402 | 72 | goto out; |
403 | 72 | } |
404 | 3.47k | } else if (!strcmp(cred->fmt, "fido-u2f")) { |
405 | 510 | if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash, |
406 | 510 | sizeof(cred->authdata.rp_id_hash), &cred->cdh, |
407 | 510 | &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) { |
408 | 46 | fido_log_debug("%s: get_signed_hash_u2f", __func__); |
409 | 46 | r = FIDO_ERR_INTERNAL; |
410 | 46 | goto out; |
411 | 46 | } |
412 | 789 | } else if (!strcmp(cred->fmt, "tpm")) { |
413 | 786 | if (fido_get_signed_hash_tpm(&dgst, &cred->cdh, |
414 | 786 | &cred->authdata_raw, &cred->attstmt, &cred->attcred) < 0) { |
415 | 80 | fido_log_debug("%s: fido_get_signed_hash_tpm", __func__); |
416 | 80 | r = FIDO_ERR_INTERNAL; |
417 | 80 | goto out; |
418 | 80 | } |
419 | 786 | } else { |
420 | 3 | fido_log_debug("%s: unknown fmt %s", __func__, cred->fmt); |
421 | 3 | r = FIDO_ERR_INVALID_ARGUMENT; |
422 | 3 | goto out; |
423 | 3 | } |
424 | | |
425 | 4.57k | if (verify_attstmt(&dgst, &cred->attstmt) < 0) { |
426 | 4.57k | fido_log_debug("%s: verify_attstmt", __func__); |
427 | 4.57k | r = FIDO_ERR_INVALID_SIG; |
428 | 4.57k | goto out; |
429 | 4.57k | } |
430 | | |
431 | 4 | r = FIDO_OK; |
432 | 19.5k | out: |
433 | 19.5k | explicit_bzero(buf, sizeof(buf)); |
434 | | |
435 | 19.5k | return (r); |
436 | 4 | } |
437 | | |
438 | | int |
439 | | fido_cred_verify_self(const fido_cred_t *cred) |
440 | 13.1k | { |
441 | 13.1k | unsigned char buf[1024]; /* XXX */ |
442 | 13.1k | fido_blob_t dgst; |
443 | 13.1k | int ok = -1; |
444 | 13.1k | int r; |
445 | | |
446 | 13.1k | dgst.ptr = buf; |
447 | 13.1k | dgst.len = sizeof(buf); |
448 | | |
449 | | /* do we have everything we need? */ |
450 | 13.1k | if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL || |
451 | 13.1k | cred->attstmt.x5c.ptr != NULL || cred->attstmt.sig.ptr == NULL || |
452 | 13.1k | cred->fmt == NULL || cred->attcred.id.ptr == NULL || |
453 | 13.1k | cred->rp.id == NULL) { |
454 | 12.2k | fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, " |
455 | 12.2k | "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr, |
456 | 12.2k | (void *)cred->authdata_cbor.ptr, |
457 | 12.2k | (void *)cred->attstmt.x5c.ptr, |
458 | 12.2k | (void *)cred->attstmt.sig.ptr, (void *)cred->fmt, |
459 | 12.2k | (void *)cred->attcred.id.ptr, cred->rp.id); |
460 | 12.2k | r = FIDO_ERR_INVALID_ARGUMENT; |
461 | 12.2k | goto out; |
462 | 12.2k | } |
463 | | |
464 | 866 | if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) { |
465 | 280 | fido_log_debug("%s: fido_check_rp_id", __func__); |
466 | 280 | r = FIDO_ERR_INVALID_PARAM; |
467 | 280 | goto out; |
468 | 280 | } |
469 | | |
470 | 586 | if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE, |
471 | 586 | cred->uv) < 0) { |
472 | 6 | fido_log_debug("%s: fido_check_flags", __func__); |
473 | 6 | r = FIDO_ERR_INVALID_PARAM; |
474 | 6 | goto out; |
475 | 6 | } |
476 | | |
477 | 580 | if (check_extensions(&cred->authdata_ext, &cred->ext) != 0) { |
478 | 5 | fido_log_debug("%s: check_extensions", __func__); |
479 | 5 | r = FIDO_ERR_INVALID_PARAM; |
480 | 5 | goto out; |
481 | 5 | } |
482 | | |
483 | 575 | if (!strcmp(cred->fmt, "packed")) { |
484 | 500 | if (fido_get_signed_hash(cred->attcred.type, &dgst, &cred->cdh, |
485 | 500 | &cred->authdata_cbor) < 0) { |
486 | 28 | fido_log_debug("%s: fido_get_signed_hash", __func__); |
487 | 28 | r = FIDO_ERR_INTERNAL; |
488 | 28 | goto out; |
489 | 28 | } |
490 | 500 | } else if (!strcmp(cred->fmt, "fido-u2f")) { |
491 | 71 | if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash, |
492 | 71 | sizeof(cred->authdata.rp_id_hash), &cred->cdh, |
493 | 71 | &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) { |
494 | 4 | fido_log_debug("%s: get_signed_hash_u2f", __func__); |
495 | 4 | r = FIDO_ERR_INTERNAL; |
496 | 4 | goto out; |
497 | 4 | } |
498 | 71 | } else { |
499 | 4 | fido_log_debug("%s: unknown fmt %s", __func__, cred->fmt); |
500 | 4 | r = FIDO_ERR_INVALID_ARGUMENT; |
501 | 4 | goto out; |
502 | 4 | } |
503 | | |
504 | 539 | switch (cred->attcred.type) { |
505 | 113 | case COSE_ES256: |
506 | 113 | ok = es256_pk_verify_sig(&dgst, &cred->attcred.pubkey.es256, |
507 | 113 | &cred->attstmt.sig); |
508 | 113 | break; |
509 | 187 | case COSE_ES384: |
510 | 187 | ok = es384_pk_verify_sig(&dgst, &cred->attcred.pubkey.es384, |
511 | 187 | &cred->attstmt.sig); |
512 | 187 | break; |
513 | 57 | case COSE_RS256: |
514 | 57 | ok = rs256_pk_verify_sig(&dgst, &cred->attcred.pubkey.rs256, |
515 | 57 | &cred->attstmt.sig); |
516 | 57 | break; |
517 | 182 | case COSE_EDDSA: |
518 | 182 | ok = eddsa_pk_verify_sig(&dgst, &cred->attcred.pubkey.eddsa, |
519 | 182 | &cred->attstmt.sig); |
520 | 182 | break; |
521 | 0 | default: |
522 | 0 | fido_log_debug("%s: unsupported cose_alg %d", __func__, |
523 | 0 | cred->attcred.type); |
524 | 0 | r = FIDO_ERR_UNSUPPORTED_OPTION; |
525 | 0 | goto out; |
526 | 539 | } |
527 | | |
528 | 539 | if (ok < 0) |
529 | 539 | r = FIDO_ERR_INVALID_SIG; |
530 | 0 | else |
531 | 0 | r = FIDO_OK; |
532 | | |
533 | 13.1k | out: |
534 | 13.1k | explicit_bzero(buf, sizeof(buf)); |
535 | | |
536 | 13.1k | return (r); |
537 | 539 | } |
538 | | |
539 | | fido_cred_t * |
540 | | fido_cred_new(void) |
541 | 55.5k | { |
542 | 55.5k | return (calloc(1, sizeof(fido_cred_t))); |
543 | 55.5k | } |
544 | | |
545 | | static void |
546 | | fido_cred_clean_authdata(fido_cred_t *cred) |
547 | 228k | { |
548 | 228k | fido_blob_reset(&cred->authdata_cbor); |
549 | 228k | fido_blob_reset(&cred->authdata_raw); |
550 | 228k | fido_blob_reset(&cred->attcred.id); |
551 | | |
552 | 228k | memset(&cred->authdata_ext, 0, sizeof(cred->authdata_ext)); |
553 | 228k | memset(&cred->authdata, 0, sizeof(cred->authdata)); |
554 | 228k | memset(&cred->attcred, 0, sizeof(cred->attcred)); |
555 | 228k | } |
556 | | |
557 | | static void |
558 | | fido_cred_clean_attstmt(fido_attstmt_t *attstmt) |
559 | 187k | { |
560 | 187k | fido_blob_reset(&attstmt->certinfo); |
561 | 187k | fido_blob_reset(&attstmt->pubarea); |
562 | 187k | fido_blob_reset(&attstmt->cbor); |
563 | 187k | fido_free_blob_array(&attstmt->x5c); |
564 | 187k | fido_blob_reset(&attstmt->sig); |
565 | | |
566 | 187k | memset(attstmt, 0, sizeof(*attstmt)); |
567 | 187k | } |
568 | | |
569 | | static void |
570 | | fido_cred_clean_attobj(fido_cred_t *cred) |
571 | 139k | { |
572 | 139k | free(cred->fmt); |
573 | 139k | cred->fmt = NULL; |
574 | 139k | fido_cred_clean_authdata(cred); |
575 | 139k | fido_cred_clean_attstmt(&cred->attstmt); |
576 | 139k | } |
577 | | |
578 | | void |
579 | | fido_cred_reset_tx(fido_cred_t *cred) |
580 | 127k | { |
581 | 127k | fido_blob_reset(&cred->cd); |
582 | 127k | fido_blob_reset(&cred->cdh); |
583 | 127k | fido_blob_reset(&cred->user.id); |
584 | 127k | fido_blob_reset(&cred->blob); |
585 | | |
586 | 127k | free(cred->rp.id); |
587 | 127k | free(cred->rp.name); |
588 | 127k | free(cred->user.icon); |
589 | 127k | free(cred->user.name); |
590 | 127k | free(cred->user.display_name); |
591 | 127k | fido_cred_empty_exclude_list(cred); |
592 | | |
593 | 127k | memset(&cred->rp, 0, sizeof(cred->rp)); |
594 | 127k | memset(&cred->user, 0, sizeof(cred->user)); |
595 | 127k | memset(&cred->ext, 0, sizeof(cred->ext)); |
596 | | |
597 | 127k | cred->type = 0; |
598 | 127k | cred->rk = FIDO_OPT_OMIT; |
599 | 127k | cred->uv = FIDO_OPT_OMIT; |
600 | 127k | cred->ea.mode = 0; |
601 | 127k | } |
602 | | |
603 | | void |
604 | | fido_cred_reset_rx(fido_cred_t *cred) |
605 | 133k | { |
606 | 133k | fido_cred_clean_attobj(cred); |
607 | 133k | fido_blob_reset(&cred->largeblob_key); |
608 | 133k | cred->ea.att = false; |
609 | 133k | } |
610 | | |
611 | | void |
612 | | fido_cred_free(fido_cred_t **cred_p) |
613 | 55.3k | { |
614 | 55.3k | fido_cred_t *cred; |
615 | | |
616 | 55.3k | if (cred_p == NULL || (cred = *cred_p) == NULL) |
617 | 13 | return; |
618 | 55.3k | fido_cred_reset_tx(cred); |
619 | 55.3k | fido_cred_reset_rx(cred); |
620 | 55.3k | free(cred); |
621 | 55.3k | *cred_p = NULL; |
622 | 55.3k | } |
623 | | |
624 | | int |
625 | | fido_cred_set_authdata(fido_cred_t *cred, const unsigned char *ptr, size_t len) |
626 | 26.8k | { |
627 | 26.8k | cbor_item_t *item = NULL; |
628 | 26.8k | struct cbor_load_result cbor; |
629 | 26.8k | int r = FIDO_ERR_INVALID_ARGUMENT; |
630 | | |
631 | 26.8k | fido_cred_clean_authdata(cred); |
632 | | |
633 | 26.8k | if (ptr == NULL || len == 0) |
634 | 20.1k | goto fail; |
635 | | |
636 | 6.70k | if ((item = cbor_load(ptr, len, &cbor)) == NULL) { |
637 | 276 | fido_log_debug("%s: cbor_load", __func__); |
638 | 276 | goto fail; |
639 | 276 | } |
640 | | |
641 | 6.42k | if (fido_blob_decode(item, &cred->authdata_raw) < 0) { |
642 | 28 | fido_log_debug("%s: fido_blob_decode", __func__); |
643 | 28 | goto fail; |
644 | 28 | } |
645 | | |
646 | 6.39k | if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor, |
647 | 6.39k | &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) { |
648 | 268 | fido_log_debug("%s: cbor_decode_cred_authdata", __func__); |
649 | 268 | goto fail; |
650 | 268 | } |
651 | | |
652 | 6.13k | r = FIDO_OK; |
653 | 26.8k | fail: |
654 | 26.8k | if (item != NULL) |
655 | 6.42k | cbor_decref(&item); |
656 | | |
657 | 26.8k | if (r != FIDO_OK) |
658 | 20.7k | fido_cred_clean_authdata(cred); |
659 | | |
660 | 26.8k | return (r); |
661 | 6.13k | } |
662 | | |
663 | | int |
664 | | fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr, |
665 | | size_t len) |
666 | 20.7k | { |
667 | 20.7k | cbor_item_t *item = NULL; |
668 | 20.7k | int r = FIDO_ERR_INVALID_ARGUMENT; |
669 | | |
670 | 20.7k | fido_cred_clean_authdata(cred); |
671 | | |
672 | 20.7k | if (ptr == NULL || len == 0) |
673 | 20.1k | goto fail; |
674 | | |
675 | 559 | if (fido_blob_set(&cred->authdata_raw, ptr, len) < 0) { |
676 | 1 | fido_log_debug("%s: fido_blob_set", __func__); |
677 | 1 | r = FIDO_ERR_INTERNAL; |
678 | 1 | goto fail; |
679 | 1 | } |
680 | | |
681 | 558 | if ((item = cbor_build_bytestring(ptr, len)) == NULL) { |
682 | 8 | fido_log_debug("%s: cbor_build_bytestring", __func__); |
683 | 8 | r = FIDO_ERR_INTERNAL; |
684 | 8 | goto fail; |
685 | 8 | } |
686 | | |
687 | 550 | if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor, |
688 | 550 | &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) { |
689 | 243 | fido_log_debug("%s: cbor_decode_cred_authdata", __func__); |
690 | 243 | goto fail; |
691 | 243 | } |
692 | | |
693 | 307 | r = FIDO_OK; |
694 | 20.7k | fail: |
695 | 20.7k | if (item != NULL) |
696 | 550 | cbor_decref(&item); |
697 | | |
698 | 20.7k | if (r != FIDO_OK) |
699 | 20.4k | fido_cred_clean_authdata(cred); |
700 | | |
701 | 20.7k | return (r); |
702 | 307 | } |
703 | | |
704 | | int |
705 | | fido_cred_set_id(fido_cred_t *cred, const unsigned char *ptr, size_t len) |
706 | 22.5k | { |
707 | 22.5k | if (fido_blob_set(&cred->attcred.id, ptr, len) < 0) |
708 | 172 | return (FIDO_ERR_INVALID_ARGUMENT); |
709 | | |
710 | 22.4k | return (FIDO_OK); |
711 | 22.5k | } |
712 | | |
713 | | int |
714 | | fido_cred_set_x509(fido_cred_t *cred, const unsigned char *ptr, size_t len) |
715 | 33.7k | { |
716 | 33.7k | fido_blob_t x5c_blob; |
717 | 33.7k | fido_blob_t *list_ptr = NULL; |
718 | | |
719 | 33.7k | memset(&x5c_blob, 0, sizeof(x5c_blob)); |
720 | 33.7k | fido_free_blob_array(&cred->attstmt.x5c); |
721 | | |
722 | 33.7k | if (fido_blob_set(&x5c_blob, ptr, len) < 0) |
723 | 31.7k | return (FIDO_ERR_INVALID_ARGUMENT); |
724 | | |
725 | 2.06k | if (cred->attstmt.x5c.len == SIZE_MAX) { |
726 | 0 | fido_blob_reset(&x5c_blob); |
727 | 0 | return (FIDO_ERR_INVALID_ARGUMENT); |
728 | 0 | } |
729 | | |
730 | 2.06k | if ((list_ptr = recallocarray(cred->attstmt.x5c.ptr, |
731 | 2.06k | cred->attstmt.x5c.len, cred->attstmt.x5c.len + 1, |
732 | 2.06k | sizeof(x5c_blob))) == NULL) { |
733 | 29 | fido_blob_reset(&x5c_blob); |
734 | 29 | return (FIDO_ERR_INTERNAL); |
735 | 29 | } |
736 | | |
737 | 2.03k | list_ptr[cred->attstmt.x5c.len++] = x5c_blob; |
738 | 2.03k | cred->attstmt.x5c.ptr = list_ptr; |
739 | | |
740 | 2.03k | return (FIDO_OK); |
741 | 2.06k | } |
742 | | |
743 | | int |
744 | | fido_cred_set_sig(fido_cred_t *cred, const unsigned char *ptr, size_t len) |
745 | 33.7k | { |
746 | 33.7k | if (fido_blob_set(&cred->attstmt.sig, ptr, len) < 0) |
747 | 30.8k | return (FIDO_ERR_INVALID_ARGUMENT); |
748 | | |
749 | 2.97k | return (FIDO_OK); |
750 | 33.7k | } |
751 | | |
752 | | int |
753 | | fido_cred_set_attstmt(fido_cred_t *cred, const unsigned char *ptr, size_t len) |
754 | 26.8k | { |
755 | 26.8k | cbor_item_t *item = NULL; |
756 | 26.8k | struct cbor_load_result cbor; |
757 | 26.8k | int r = FIDO_ERR_INVALID_ARGUMENT; |
758 | | |
759 | 26.8k | fido_cred_clean_attstmt(&cred->attstmt); |
760 | | |
761 | 26.8k | if (ptr == NULL || len == 0) |
762 | 20.3k | goto fail; |
763 | | |
764 | 6.56k | if ((item = cbor_load(ptr, len, &cbor)) == NULL) { |
765 | 37 | fido_log_debug("%s: cbor_load", __func__); |
766 | 37 | goto fail; |
767 | 37 | } |
768 | | |
769 | 6.53k | if (cbor_decode_attstmt(item, &cred->attstmt) < 0) { |
770 | 322 | fido_log_debug("%s: cbor_decode_attstmt", __func__); |
771 | 322 | goto fail; |
772 | 322 | } |
773 | | |
774 | 6.20k | r = FIDO_OK; |
775 | 26.8k | fail: |
776 | 26.8k | if (item != NULL) |
777 | 6.53k | cbor_decref(&item); |
778 | | |
779 | 26.8k | if (r != FIDO_OK) |
780 | 20.6k | fido_cred_clean_attstmt(&cred->attstmt); |
781 | | |
782 | 26.8k | return (r); |
783 | 6.20k | } |
784 | | |
785 | | int |
786 | | fido_cred_set_attobj(fido_cred_t *cred, const unsigned char *ptr, size_t len) |
787 | 6.45k | { |
788 | 6.45k | cbor_item_t *item = NULL; |
789 | 6.45k | struct cbor_load_result cbor; |
790 | 6.45k | int r = FIDO_ERR_INVALID_ARGUMENT; |
791 | | |
792 | 6.45k | fido_cred_clean_attobj(cred); |
793 | | |
794 | 6.45k | if (ptr == NULL || len == 0) |
795 | 9 | goto fail; |
796 | | |
797 | 6.44k | if ((item = cbor_load(ptr, len, &cbor)) == NULL) { |
798 | 178 | fido_log_debug("%s: cbor_load", __func__); |
799 | 178 | goto fail; |
800 | 178 | } |
801 | 6.26k | if (cbor_decode_attobj(item, cred) != 0) { |
802 | 5.35k | fido_log_debug("%s: cbor_decode_attobj", __func__); |
803 | 5.35k | goto fail; |
804 | 5.35k | } |
805 | | |
806 | 910 | r = FIDO_OK; |
807 | 6.45k | fail: |
808 | 6.45k | if (item != NULL) |
809 | 6.26k | cbor_decref(&item); |
810 | | |
811 | 6.45k | return (r); |
812 | 910 | } |
813 | | |
814 | | int |
815 | | fido_cred_exclude(fido_cred_t *cred, const unsigned char *id_ptr, size_t id_len) |
816 | 337k | { |
817 | 337k | fido_blob_t id_blob; |
818 | 337k | fido_blob_t *list_ptr; |
819 | | |
820 | 337k | memset(&id_blob, 0, sizeof(id_blob)); |
821 | | |
822 | 337k | if (fido_blob_set(&id_blob, id_ptr, id_len) < 0) |
823 | 2.00k | return (FIDO_ERR_INVALID_ARGUMENT); |
824 | | |
825 | 335k | if (cred->excl.len == SIZE_MAX) { |
826 | 0 | free(id_blob.ptr); |
827 | 0 | return (FIDO_ERR_INVALID_ARGUMENT); |
828 | 0 | } |
829 | | |
830 | 335k | if ((list_ptr = recallocarray(cred->excl.ptr, cred->excl.len, |
831 | 335k | cred->excl.len + 1, sizeof(fido_blob_t))) == NULL) { |
832 | 891 | free(id_blob.ptr); |
833 | 891 | return (FIDO_ERR_INTERNAL); |
834 | 891 | } |
835 | | |
836 | 334k | list_ptr[cred->excl.len++] = id_blob; |
837 | 334k | cred->excl.ptr = list_ptr; |
838 | | |
839 | 334k | return (FIDO_OK); |
840 | 335k | } |
841 | | |
842 | | int |
843 | | fido_cred_empty_exclude_list(fido_cred_t *cred) |
844 | 127k | { |
845 | 127k | fido_free_blob_array(&cred->excl); |
846 | 127k | memset(&cred->excl, 0, sizeof(cred->excl)); |
847 | | |
848 | 127k | return (FIDO_OK); |
849 | 127k | } |
850 | | |
851 | | int |
852 | | fido_cred_set_clientdata(fido_cred_t *cred, const unsigned char *data, |
853 | | size_t data_len) |
854 | 0 | { |
855 | 0 | if (!fido_blob_is_empty(&cred->cdh) || |
856 | 0 | fido_blob_set(&cred->cd, data, data_len) < 0) { |
857 | 0 | return (FIDO_ERR_INVALID_ARGUMENT); |
858 | 0 | } |
859 | 0 | if (fido_sha256(&cred->cdh, data, data_len) < 0) { |
860 | 0 | fido_blob_reset(&cred->cd); |
861 | 0 | return (FIDO_ERR_INTERNAL); |
862 | 0 | } |
863 | | |
864 | 0 | return (FIDO_OK); |
865 | 0 | } |
866 | | |
867 | | int |
868 | | fido_cred_set_clientdata_hash(fido_cred_t *cred, const unsigned char *hash, |
869 | | size_t hash_len) |
870 | 43.6k | { |
871 | 43.6k | if (!fido_blob_is_empty(&cred->cd) || |
872 | 43.6k | fido_blob_set(&cred->cdh, hash, hash_len) < 0) |
873 | 2.82k | return (FIDO_ERR_INVALID_ARGUMENT); |
874 | | |
875 | 40.8k | return (FIDO_OK); |
876 | 43.6k | } |
877 | | |
878 | | int |
879 | | fido_cred_set_rp(fido_cred_t *cred, const char *id, const char *name) |
880 | 43.6k | { |
881 | 43.6k | fido_rp_t *rp = &cred->rp; |
882 | | |
883 | 43.6k | if (rp->id != NULL) { |
884 | 11.9k | free(rp->id); |
885 | 11.9k | rp->id = NULL; |
886 | 11.9k | } |
887 | 43.6k | if (rp->name != NULL) { |
888 | 11.9k | free(rp->name); |
889 | 11.9k | rp->name = NULL; |
890 | 11.9k | } |
891 | | |
892 | 43.6k | if (id != NULL && (rp->id = strdup(id)) == NULL) |
893 | 172 | goto fail; |
894 | 43.4k | if (name != NULL && (rp->name = strdup(name)) == NULL) |
895 | 111 | goto fail; |
896 | | |
897 | 43.3k | return (FIDO_OK); |
898 | 283 | fail: |
899 | 283 | free(rp->id); |
900 | 283 | free(rp->name); |
901 | 283 | rp->id = NULL; |
902 | 283 | rp->name = NULL; |
903 | | |
904 | 283 | return (FIDO_ERR_INTERNAL); |
905 | 43.4k | } |
906 | | |
907 | | int |
908 | | fido_cred_set_user(fido_cred_t *cred, const unsigned char *user_id, |
909 | | size_t user_id_len, const char *name, const char *display_name, |
910 | | const char *icon) |
911 | 33.4k | { |
912 | 33.4k | fido_user_t *up = &cred->user; |
913 | | |
914 | 33.4k | if (up->id.ptr != NULL) { |
915 | 11.8k | free(up->id.ptr); |
916 | 11.8k | up->id.ptr = NULL; |
917 | 11.8k | up->id.len = 0; |
918 | 11.8k | } |
919 | 33.4k | if (up->name != NULL) { |
920 | 11.8k | free(up->name); |
921 | 11.8k | up->name = NULL; |
922 | 11.8k | } |
923 | 33.4k | if (up->display_name != NULL) { |
924 | 11.8k | free(up->display_name); |
925 | 11.8k | up->display_name = NULL; |
926 | 11.8k | } |
927 | 33.4k | if (up->icon != NULL) { |
928 | 11.8k | free(up->icon); |
929 | 11.8k | up->icon = NULL; |
930 | 11.8k | } |
931 | | |
932 | 33.4k | if (user_id != NULL && fido_blob_set(&up->id, user_id, user_id_len) < 0) |
933 | 202 | goto fail; |
934 | 33.2k | if (name != NULL && (up->name = strdup(name)) == NULL) |
935 | 116 | goto fail; |
936 | 33.0k | if (display_name != NULL && |
937 | 33.0k | (up->display_name = strdup(display_name)) == NULL) |
938 | 107 | goto fail; |
939 | 32.9k | if (icon != NULL && (up->icon = strdup(icon)) == NULL) |
940 | 67 | goto fail; |
941 | | |
942 | 32.9k | return (FIDO_OK); |
943 | 492 | fail: |
944 | 492 | free(up->id.ptr); |
945 | 492 | free(up->name); |
946 | 492 | free(up->display_name); |
947 | 492 | free(up->icon); |
948 | | |
949 | 492 | up->id.ptr = NULL; |
950 | 492 | up->id.len = 0; |
951 | 492 | up->name = NULL; |
952 | 492 | up->display_name = NULL; |
953 | 492 | up->icon = NULL; |
954 | | |
955 | 492 | return (FIDO_ERR_INTERNAL); |
956 | 32.9k | } |
957 | | |
958 | | int |
959 | | fido_cred_set_extensions(fido_cred_t *cred, int ext) |
960 | 19.0k | { |
961 | 19.0k | if (ext == 0) |
962 | 1.12k | cred->ext.mask = 0; |
963 | 17.9k | else { |
964 | 17.9k | if ((ext & FIDO_EXT_CRED_MASK) != ext) |
965 | 11.8k | return (FIDO_ERR_INVALID_ARGUMENT); |
966 | 6.05k | cred->ext.mask |= ext; |
967 | 6.05k | } |
968 | | |
969 | 7.18k | return (FIDO_OK); |
970 | 19.0k | } |
971 | | |
972 | | int |
973 | | fido_cred_set_options(fido_cred_t *cred, bool rk, bool uv) |
974 | 0 | { |
975 | 0 | cred->rk = rk ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; |
976 | 0 | cred->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; |
977 | |
|
978 | 0 | return (FIDO_OK); |
979 | 0 | } |
980 | | |
981 | | int |
982 | | fido_cred_set_rk(fido_cred_t *cred, fido_opt_t rk) |
983 | 6.19k | { |
984 | 6.19k | cred->rk = rk; |
985 | | |
986 | 6.19k | return (FIDO_OK); |
987 | 6.19k | } |
988 | | |
989 | | int |
990 | | fido_cred_set_uv(fido_cred_t *cred, fido_opt_t uv) |
991 | 6.37k | { |
992 | 6.37k | cred->uv = uv; |
993 | | |
994 | 6.37k | return (FIDO_OK); |
995 | 6.37k | } |
996 | | |
997 | | int |
998 | | fido_cred_set_entattest(fido_cred_t *cred, int ea) |
999 | 11.2k | { |
1000 | 11.2k | if (ea != 0 && ea != FIDO_ENTATTEST_VENDOR && |
1001 | 11.2k | ea != FIDO_ENTATTEST_PLATFORM) |
1002 | 2.01k | return (FIDO_ERR_INVALID_ARGUMENT); |
1003 | | |
1004 | 9.20k | cred->ea.mode = ea; |
1005 | | |
1006 | 9.20k | return (FIDO_OK); |
1007 | 11.2k | } |
1008 | | |
1009 | | int |
1010 | | fido_cred_set_prot(fido_cred_t *cred, int prot) |
1011 | 29.0k | { |
1012 | 29.0k | if (prot == 0) { |
1013 | 16.9k | cred->ext.mask &= ~FIDO_EXT_CRED_PROTECT; |
1014 | 16.9k | cred->ext.prot = 0; |
1015 | 16.9k | } else { |
1016 | 12.1k | if (prot != FIDO_CRED_PROT_UV_OPTIONAL && |
1017 | 12.1k | prot != FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID && |
1018 | 12.1k | prot != FIDO_CRED_PROT_UV_REQUIRED) |
1019 | 176 | return (FIDO_ERR_INVALID_ARGUMENT); |
1020 | | |
1021 | 11.9k | cred->ext.mask |= FIDO_EXT_CRED_PROTECT; |
1022 | 11.9k | cred->ext.prot = prot; |
1023 | 11.9k | } |
1024 | | |
1025 | 28.8k | return (FIDO_OK); |
1026 | 29.0k | } |
1027 | | |
1028 | | int |
1029 | | fido_cred_set_pin_minlen(fido_cred_t *cred, size_t len) |
1030 | 16.0k | { |
1031 | 16.0k | if (len == 0) |
1032 | 12.6k | cred->ext.mask &= ~FIDO_EXT_MINPINLEN; |
1033 | 3.34k | else |
1034 | 3.34k | cred->ext.mask |= FIDO_EXT_MINPINLEN; |
1035 | | |
1036 | 16.0k | cred->ext.minpinlen = len; |
1037 | | |
1038 | 16.0k | return (FIDO_OK); |
1039 | 16.0k | } |
1040 | | |
1041 | | int |
1042 | | fido_cred_set_blob(fido_cred_t *cred, const unsigned char *ptr, size_t len) |
1043 | 2.63k | { |
1044 | 2.63k | if (ptr == NULL || len == 0) |
1045 | 5 | return (FIDO_ERR_INVALID_ARGUMENT); |
1046 | 2.62k | if (fido_blob_set(&cred->blob, ptr, len) < 0) |
1047 | 6 | return (FIDO_ERR_INTERNAL); |
1048 | | |
1049 | 2.62k | cred->ext.mask |= FIDO_EXT_CRED_BLOB; |
1050 | | |
1051 | 2.62k | return (FIDO_OK); |
1052 | 2.62k | } |
1053 | | |
1054 | | int |
1055 | | fido_cred_set_fmt(fido_cred_t *cred, const char *fmt) |
1056 | 3.68k | { |
1057 | 3.68k | free(cred->fmt); |
1058 | 3.68k | cred->fmt = NULL; |
1059 | | |
1060 | 3.68k | if (fmt == NULL) |
1061 | 0 | return (FIDO_ERR_INVALID_ARGUMENT); |
1062 | | |
1063 | 3.68k | if (strcmp(fmt, "packed") && strcmp(fmt, "fido-u2f") && |
1064 | 3.68k | strcmp(fmt, "none") && strcmp(fmt, "tpm")) |
1065 | 0 | return (FIDO_ERR_INVALID_ARGUMENT); |
1066 | | |
1067 | 3.68k | if ((cred->fmt = strdup(fmt)) == NULL) |
1068 | 36 | return (FIDO_ERR_INTERNAL); |
1069 | | |
1070 | 3.65k | return (FIDO_OK); |
1071 | 3.68k | } |
1072 | | |
1073 | | int |
1074 | | fido_cred_set_type(fido_cred_t *cred, int cose_alg) |
1075 | 43.6k | { |
1076 | 43.6k | if (cred->type != 0) |
1077 | 12.0k | return (FIDO_ERR_INVALID_ARGUMENT); |
1078 | 31.6k | if (cose_alg != COSE_ES256 && cose_alg != COSE_ES384 && |
1079 | 31.6k | cose_alg != COSE_RS256 && cose_alg != COSE_EDDSA) |
1080 | 0 | return (FIDO_ERR_INVALID_ARGUMENT); |
1081 | | |
1082 | 31.6k | cred->type = cose_alg; |
1083 | | |
1084 | 31.6k | return (FIDO_OK); |
1085 | 31.6k | } |
1086 | | |
1087 | | int |
1088 | | fido_cred_type(const fido_cred_t *cred) |
1089 | 24.3k | { |
1090 | 24.3k | return (cred->type); |
1091 | 24.3k | } |
1092 | | |
1093 | | uint8_t |
1094 | | fido_cred_flags(const fido_cred_t *cred) |
1095 | 13.1k | { |
1096 | 13.1k | return (cred->authdata.flags); |
1097 | 13.1k | } |
1098 | | |
1099 | | uint32_t |
1100 | | fido_cred_sigcount(const fido_cred_t *cred) |
1101 | 13.1k | { |
1102 | 13.1k | return (cred->authdata.sigcount); |
1103 | 13.1k | } |
1104 | | |
1105 | | const unsigned char * |
1106 | | fido_cred_clientdata_hash_ptr(const fido_cred_t *cred) |
1107 | 13.1k | { |
1108 | 13.1k | return (cred->cdh.ptr); |
1109 | 13.1k | } |
1110 | | |
1111 | | size_t |
1112 | | fido_cred_clientdata_hash_len(const fido_cred_t *cred) |
1113 | 13.1k | { |
1114 | 13.1k | return (cred->cdh.len); |
1115 | 13.1k | } |
1116 | | |
1117 | | const unsigned char * |
1118 | | fido_cred_x5c_ptr(const fido_cred_t *cred) |
1119 | 13.1k | { |
1120 | 13.1k | return (fido_cred_x5c_list_ptr(cred, 0)); |
1121 | 13.1k | } |
1122 | | |
1123 | | size_t |
1124 | | fido_cred_x5c_len(const fido_cred_t *cred) |
1125 | 13.1k | { |
1126 | 13.1k | return (fido_cred_x5c_list_len(cred, 0)); |
1127 | 13.1k | } |
1128 | | |
1129 | | size_t |
1130 | | fido_cred_x5c_list_count(const fido_cred_t *cred) |
1131 | 28.3k | { |
1132 | 28.3k | return (cred->attstmt.x5c.len); |
1133 | 28.3k | } |
1134 | | |
1135 | | const unsigned char * |
1136 | | fido_cred_x5c_list_ptr(const fido_cred_t *cred, size_t i) |
1137 | 28.4k | { |
1138 | 28.4k | if (i >= cred->attstmt.x5c.len) |
1139 | 24.3k | return (NULL); |
1140 | | |
1141 | 4.08k | return (cred->attstmt.x5c.ptr[i].ptr); |
1142 | 28.4k | } |
1143 | | |
1144 | | size_t |
1145 | | fido_cred_x5c_list_len(const fido_cred_t *cred, size_t i) |
1146 | 28.4k | { |
1147 | 28.4k | if (i >= cred->attstmt.x5c.len) |
1148 | 24.3k | return (0); |
1149 | | |
1150 | 4.08k | return (cred->attstmt.x5c.ptr[i].len); |
1151 | 28.4k | } |
1152 | | |
1153 | | const unsigned char * |
1154 | | fido_cred_sig_ptr(const fido_cred_t *cred) |
1155 | 13.1k | { |
1156 | 13.1k | return (cred->attstmt.sig.ptr); |
1157 | 13.1k | } |
1158 | | |
1159 | | size_t |
1160 | | fido_cred_sig_len(const fido_cred_t *cred) |
1161 | 13.1k | { |
1162 | 13.1k | return (cred->attstmt.sig.len); |
1163 | 13.1k | } |
1164 | | |
1165 | | const unsigned char * |
1166 | | fido_cred_authdata_ptr(const fido_cred_t *cred) |
1167 | 19.6k | { |
1168 | 19.6k | return (cred->authdata_cbor.ptr); |
1169 | 19.6k | } |
1170 | | |
1171 | | size_t |
1172 | | fido_cred_authdata_len(const fido_cred_t *cred) |
1173 | 19.6k | { |
1174 | 19.6k | return (cred->authdata_cbor.len); |
1175 | 19.6k | } |
1176 | | |
1177 | | const unsigned char * |
1178 | | fido_cred_authdata_raw_ptr(const fido_cred_t *cred) |
1179 | 13.1k | { |
1180 | 13.1k | return (cred->authdata_raw.ptr); |
1181 | 13.1k | } |
1182 | | |
1183 | | size_t |
1184 | | fido_cred_authdata_raw_len(const fido_cred_t *cred) |
1185 | 13.1k | { |
1186 | 13.1k | return (cred->authdata_raw.len); |
1187 | 13.1k | } |
1188 | | |
1189 | | const unsigned char * |
1190 | | fido_cred_attstmt_ptr(const fido_cred_t *cred) |
1191 | 19.6k | { |
1192 | 19.6k | return (cred->attstmt.cbor.ptr); |
1193 | 19.6k | } |
1194 | | |
1195 | | size_t |
1196 | | fido_cred_attstmt_len(const fido_cred_t *cred) |
1197 | 19.6k | { |
1198 | 19.6k | return (cred->attstmt.cbor.len); |
1199 | 19.6k | } |
1200 | | |
1201 | | const unsigned char * |
1202 | | fido_cred_pubkey_ptr(const fido_cred_t *cred) |
1203 | 24.3k | { |
1204 | 24.3k | const void *ptr; |
1205 | | |
1206 | 24.3k | switch (cred->attcred.type) { |
1207 | 6.45k | case COSE_ES256: |
1208 | 6.45k | ptr = &cred->attcred.pubkey.es256; |
1209 | 6.45k | break; |
1210 | 260 | case COSE_ES384: |
1211 | 260 | ptr = &cred->attcred.pubkey.es384; |
1212 | 260 | break; |
1213 | 243 | case COSE_RS256: |
1214 | 243 | ptr = &cred->attcred.pubkey.rs256; |
1215 | 243 | break; |
1216 | 1.14k | case COSE_EDDSA: |
1217 | 1.14k | ptr = &cred->attcred.pubkey.eddsa; |
1218 | 1.14k | break; |
1219 | 16.2k | default: |
1220 | 16.2k | ptr = NULL; |
1221 | 16.2k | break; |
1222 | 24.3k | } |
1223 | | |
1224 | 24.3k | return (ptr); |
1225 | 24.3k | } |
1226 | | |
1227 | | size_t |
1228 | | fido_cred_pubkey_len(const fido_cred_t *cred) |
1229 | 24.3k | { |
1230 | 24.3k | size_t len; |
1231 | | |
1232 | 24.3k | switch (cred->attcred.type) { |
1233 | 6.45k | case COSE_ES256: |
1234 | 6.45k | len = sizeof(cred->attcred.pubkey.es256); |
1235 | 6.45k | break; |
1236 | 260 | case COSE_ES384: |
1237 | 260 | len = sizeof(cred->attcred.pubkey.es384); |
1238 | 260 | break; |
1239 | 243 | case COSE_RS256: |
1240 | 243 | len = sizeof(cred->attcred.pubkey.rs256); |
1241 | 243 | break; |
1242 | 1.14k | case COSE_EDDSA: |
1243 | 1.14k | len = sizeof(cred->attcred.pubkey.eddsa); |
1244 | 1.14k | break; |
1245 | 16.2k | default: |
1246 | 16.2k | len = 0; |
1247 | 16.2k | break; |
1248 | 24.3k | } |
1249 | | |
1250 | 24.3k | return (len); |
1251 | 24.3k | } |
1252 | | |
1253 | | const unsigned char * |
1254 | | fido_cred_id_ptr(const fido_cred_t *cred) |
1255 | 37.5k | { |
1256 | 37.5k | return (cred->attcred.id.ptr); |
1257 | 37.5k | } |
1258 | | |
1259 | | size_t |
1260 | | fido_cred_id_len(const fido_cred_t *cred) |
1261 | 37.5k | { |
1262 | 37.5k | return (cred->attcred.id.len); |
1263 | 37.5k | } |
1264 | | |
1265 | | const unsigned char * |
1266 | | fido_cred_aaguid_ptr(const fido_cred_t *cred) |
1267 | 13.1k | { |
1268 | 13.1k | return (cred->attcred.aaguid); |
1269 | 13.1k | } |
1270 | | |
1271 | | size_t |
1272 | | fido_cred_aaguid_len(const fido_cred_t *cred) |
1273 | 13.1k | { |
1274 | 13.1k | return (sizeof(cred->attcred.aaguid)); |
1275 | 13.1k | } |
1276 | | |
1277 | | int |
1278 | | fido_cred_prot(const fido_cred_t *cred) |
1279 | 24.4k | { |
1280 | 24.4k | return (cred->ext.prot); |
1281 | 24.4k | } |
1282 | | |
1283 | | size_t |
1284 | | fido_cred_pin_minlen(const fido_cred_t *cred) |
1285 | 26.3k | { |
1286 | 26.3k | return (cred->ext.minpinlen); |
1287 | 26.3k | } |
1288 | | |
1289 | | const char * |
1290 | | fido_cred_fmt(const fido_cred_t *cred) |
1291 | 19.6k | { |
1292 | 19.6k | return (cred->fmt); |
1293 | 19.6k | } |
1294 | | |
1295 | | const char * |
1296 | | fido_cred_rp_id(const fido_cred_t *cred) |
1297 | 13.1k | { |
1298 | 13.1k | return (cred->rp.id); |
1299 | 13.1k | } |
1300 | | |
1301 | | const char * |
1302 | | fido_cred_rp_name(const fido_cred_t *cred) |
1303 | 13.1k | { |
1304 | 13.1k | return (cred->rp.name); |
1305 | 13.1k | } |
1306 | | |
1307 | | const char * |
1308 | | fido_cred_user_name(const fido_cred_t *cred) |
1309 | 24.3k | { |
1310 | 24.3k | return (cred->user.name); |
1311 | 24.3k | } |
1312 | | |
1313 | | const char * |
1314 | | fido_cred_display_name(const fido_cred_t *cred) |
1315 | 24.3k | { |
1316 | 24.3k | return (cred->user.display_name); |
1317 | 24.3k | } |
1318 | | |
1319 | | const unsigned char * |
1320 | | fido_cred_user_id_ptr(const fido_cred_t *cred) |
1321 | 24.3k | { |
1322 | 24.3k | return (cred->user.id.ptr); |
1323 | 24.3k | } |
1324 | | |
1325 | | size_t |
1326 | | fido_cred_user_id_len(const fido_cred_t *cred) |
1327 | 24.3k | { |
1328 | 24.3k | return (cred->user.id.len); |
1329 | 24.3k | } |
1330 | | |
1331 | | const unsigned char * |
1332 | | fido_cred_largeblob_key_ptr(const fido_cred_t *cred) |
1333 | 13.1k | { |
1334 | 13.1k | return (cred->largeblob_key.ptr); |
1335 | 13.1k | } |
1336 | | |
1337 | | size_t |
1338 | | fido_cred_largeblob_key_len(const fido_cred_t *cred) |
1339 | 13.1k | { |
1340 | 13.1k | return (cred->largeblob_key.len); |
1341 | 13.1k | } |
1342 | | |
1343 | | bool |
1344 | | fido_cred_entattest(const fido_cred_t *cred) |
1345 | 13.1k | { |
1346 | 13.1k | return (cred->ea.att); |
1347 | 13.1k | } |