summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt104
-rw-r--r--src/aes256.c98
-rw-r--r--src/assert.c1090
-rw-r--r--src/authkey.c98
-rw-r--r--src/bio.c844
-rw-r--r--src/blob.c102
-rw-r--r--src/blob.h28
-rw-r--r--src/buf.c34
-rw-r--r--src/cbor.c1520
-rw-r--r--src/cred.c1034
-rw-r--r--src/credman.c736
-rw-r--r--src/dev.c284
-rwxr-xr-xsrc/diff_exports.sh23
-rw-r--r--src/ecdh.c121
-rw-r--r--src/eddsa.c169
-rw-r--r--src/err.c122
-rw-r--r--src/es256.c434
-rw-r--r--src/export.gnu183
-rw-r--r--src/export.llvm178
-rw-r--r--src/export.msvc179
-rw-r--r--src/extern.h132
-rw-r--r--src/fido.h194
-rw-r--r--src/fido/bio.h95
-rw-r--r--src/fido/credman.h74
-rw-r--r--src/fido/eddsa.h40
-rw-r--r--src/fido/err.h69
-rw-r--r--src/fido/es256.h34
-rw-r--r--src/fido/param.h84
-rw-r--r--src/fido/rs256.h22
-rw-r--r--src/hid.c70
-rw-r--r--src/hid_linux.c344
-rw-r--r--src/hid_openbsd.c277
-rw-r--r--src/hid_osx.c410
-rw-r--r--src/hid_win.c324
-rw-r--r--src/info.c410
-rw-r--r--src/io.c256
-rw-r--r--src/iso7816.c70
-rw-r--r--src/iso7816.h38
-rw-r--r--src/libfido2.pc.in12
-rw-r--r--src/log.c63
-rw-r--r--src/packed.h22
-rw-r--r--src/pin.c428
-rw-r--r--src/reset.c40
-rw-r--r--src/rs256.c204
-rw-r--r--src/types.h171
-rw-r--r--src/u2f.c758
46 files changed, 12022 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..926e7f2
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,104 @@
1# Copyright (c) 2018 Yubico AB. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file.
4
5add_definitions(-D_FIDO_INTERNAL)
6
7list(APPEND FIDO_SOURCES
8 aes256.c
9 assert.c
10 authkey.c
11 bio.c
12 blob.c
13 buf.c
14 cbor.c
15 cred.c
16 credman.c
17 dev.c
18 ecdh.c
19 eddsa.c
20 err.c
21 es256.c
22 hid.c
23 info.c
24 io.c
25 iso7816.c
26 log.c
27 pin.c
28 reset.c
29 rs256.c
30 u2f.c
31)
32
33if(FUZZ)
34 list(APPEND FIDO_SOURCES ../fuzz/uniform_random.c)
35 list(APPEND FIDO_SOURCES ../fuzz/wrap.c)
36endif()
37
38if(WIN32)
39 list(APPEND COMPAT_SOURCES hid_win.c)
40elseif(APPLE)
41 list(APPEND COMPAT_SOURCES hid_osx.c)
42elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
43 list(APPEND COMPAT_SOURCES hid_linux.c)
44elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
45 list(APPEND COMPAT_SOURCES hid_openbsd.c)
46endif()
47
48list(APPEND COMPAT_SOURCES
49 ../openbsd-compat/bsd-getpagesize.c
50 ../openbsd-compat/explicit_bzero.c
51 ../openbsd-compat/explicit_bzero_win32.c
52 ../openbsd-compat/recallocarray.c
53 ../openbsd-compat/timingsafe_bcmp.c
54)
55
56# static library
57add_library(fido2 STATIC ${FIDO_SOURCES} ${COMPAT_SOURCES})
58target_link_libraries(fido2 ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES}
59 ${UDEV_LIBRARIES} ${BASE_LIBRARIES})
60if(WIN32)
61 if (MINGW)
62 target_link_libraries(fido2 wsock32 ws2_32 bcrypt setupapi hid)
63 else()
64 target_link_libraries(fido2 wsock32 ws2_32 bcrypt SetupAPI hid)
65 set_target_properties(fido2 PROPERTIES OUTPUT_NAME fido2_static)
66 endif()
67elseif(APPLE)
68 target_link_libraries(fido2 "-framework CoreFoundation"
69 "-framework IOKit")
70endif()
71install(TARGETS fido2 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
72 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
73
74# dynamic library
75add_library(fido2_shared SHARED ${FIDO_SOURCES} ${COMPAT_SOURCES})
76target_link_libraries(fido2_shared ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES}
77 ${UDEV_LIBRARIES} ${BASE_LIBRARIES})
78if(WIN32)
79 if (MINGW)
80 target_link_libraries(fido2_shared wsock32 ws2_32 bcrypt
81 setupapi hid)
82 else()
83 target_link_libraries(fido2_shared wsock32 ws2_32 bcrypt
84 SetupAPI hid)
85 endif()
86elseif(APPLE)
87 target_link_libraries(fido2_shared "-framework CoreFoundation"
88 "-framework IOKit")
89endif()
90set_target_properties(fido2_shared PROPERTIES OUTPUT_NAME fido2
91 VERSION ${LIB_VERSION} SOVERSION ${LIB_SOVERSION})
92install(TARGETS fido2_shared
93 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
94 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
95 RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR})
96
97install(FILES fido.h DESTINATION include)
98install(DIRECTORY fido DESTINATION include)
99
100if(NOT WIN32)
101 configure_file(libfido2.pc.in libfido2.pc @ONLY)
102 install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libfido2.pc"
103 DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
104endif()
diff --git a/src/aes256.c b/src/aes256.c
new file mode 100644
index 0000000..767cdb2
--- /dev/null
+++ b/src/aes256.c
@@ -0,0 +1,98 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/evp.h>
8#include <string.h>
9
10#include "fido.h"
11
12int
13aes256_cbc_enc(const fido_blob_t *key, const fido_blob_t *in, fido_blob_t *out)
14{
15 EVP_CIPHER_CTX *ctx = NULL;
16 unsigned char iv[32];
17 int len;
18 int ok = -1;
19
20 memset(iv, 0, sizeof(iv));
21 out->ptr = NULL;
22 out->len = 0;
23
24 /* sanity check */
25 if (in->len > INT_MAX || (in->len % 16) != 0 ||
26 (out->ptr = calloc(1, in->len)) == NULL) {
27 fido_log_debug("%s: in->len=%zu", __func__, in->len);
28 goto fail;
29 }
30
31 if ((ctx = EVP_CIPHER_CTX_new()) == NULL || key->len != 32 ||
32 !EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key->ptr, iv) ||
33 !EVP_CIPHER_CTX_set_padding(ctx, 0) ||
34 !EVP_EncryptUpdate(ctx, out->ptr, &len, in->ptr, (int)in->len) ||
35 len < 0 || (size_t)len != in->len) {
36 fido_log_debug("%s: EVP_Encrypt", __func__);
37 goto fail;
38 }
39
40 out->len = (size_t)len;
41
42 ok = 0;
43fail:
44 if (ctx != NULL)
45 EVP_CIPHER_CTX_free(ctx);
46
47 if (ok < 0) {
48 free(out->ptr);
49 out->ptr = NULL;
50 out->len = 0;
51 }
52
53 return (ok);
54}
55
56int
57aes256_cbc_dec(const fido_blob_t *key, const fido_blob_t *in, fido_blob_t *out)
58{
59 EVP_CIPHER_CTX *ctx = NULL;
60 unsigned char iv[32];
61 int len;
62 int ok = -1;
63
64 memset(iv, 0, sizeof(iv));
65 out->ptr = NULL;
66 out->len = 0;
67
68 /* sanity check */
69 if (in->len > INT_MAX || (in->len % 16) != 0 ||
70 (out->ptr = calloc(1, in->len)) == NULL) {
71 fido_log_debug("%s: in->len=%zu", __func__, in->len);
72 goto fail;
73 }
74
75 if ((ctx = EVP_CIPHER_CTX_new()) == NULL || key->len != 32 ||
76 !EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key->ptr, iv) ||
77 !EVP_CIPHER_CTX_set_padding(ctx, 0) ||
78 !EVP_DecryptUpdate(ctx, out->ptr, &len, in->ptr, (int)in->len) ||
79 len < 0 || (size_t)len > in->len + 32) {
80 fido_log_debug("%s: EVP_Decrypt", __func__);
81 goto fail;
82 }
83
84 out->len = (size_t)len;
85
86 ok = 0;
87fail:
88 if (ctx != NULL)
89 EVP_CIPHER_CTX_free(ctx);
90
91 if (ok < 0) {
92 free(out->ptr);
93 out->ptr = NULL;
94 out->len = 0;
95 }
96
97 return (ok);
98}
diff --git a/src/assert.c b/src/assert.c
new file mode 100644
index 0000000..a21b308
--- /dev/null
+++ b/src/assert.c
@@ -0,0 +1,1090 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/ec.h>
8#include <openssl/ecdsa.h>
9#include <openssl/evp.h>
10#include <openssl/sha.h>
11
12#include <string.h>
13#include "fido.h"
14#include "fido/es256.h"
15#include "fido/rs256.h"
16#include "fido/eddsa.h"
17
18static int
19adjust_assert_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
20{
21 fido_assert_t *assert = arg;
22 uint64_t n;
23
24 /* numberOfCredentials; see section 6.2 */
25 if (cbor_isa_uint(key) == false ||
26 cbor_int_get_width(key) != CBOR_INT_8 ||
27 cbor_get_uint8(key) != 5) {
28 fido_log_debug("%s: cbor_type", __func__);
29 return (0); /* ignore */
30 }
31
32 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
33 fido_log_debug("%s: cbor_decode_uint64", __func__);
34 return (-1);
35 }
36
37 if (assert->stmt_len != 0 || assert->stmt_cnt != 1 ||
38 (size_t)n < assert->stmt_cnt) {
39 fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu, n=%zu",
40 __func__, assert->stmt_len, assert->stmt_cnt, (size_t)n);
41 return (-1);
42 }
43
44 if (fido_assert_set_count(assert, (size_t)n) != FIDO_OK) {
45 fido_log_debug("%s: fido_assert_set_count", __func__);
46 return (-1);
47 }
48
49 assert->stmt_len = 0; /* XXX */
50
51 return (0);
52}
53
54static int
55parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
56{
57 fido_assert_stmt *stmt = arg;
58
59 if (cbor_isa_uint(key) == false ||
60 cbor_int_get_width(key) != CBOR_INT_8) {
61 fido_log_debug("%s: cbor type", __func__);
62 return (0); /* ignore */
63 }
64
65 switch (cbor_get_uint8(key)) {
66 case 1: /* credential id */
67 return (cbor_decode_cred_id(val, &stmt->id));
68 case 2: /* authdata */
69 return (cbor_decode_assert_authdata(val, &stmt->authdata_cbor,
70 &stmt->authdata, &stmt->authdata_ext,
71 &stmt->hmac_secret_enc));
72 case 3: /* signature */
73 return (fido_blob_decode(val, &stmt->sig));
74 case 4: /* user attributes */
75 return (cbor_decode_user(val, &stmt->user));
76 default: /* ignore */
77 fido_log_debug("%s: cbor type", __func__);
78 return (0);
79 }
80}
81
82static int
83fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
84 const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin)
85{
86 fido_blob_t f;
87 cbor_item_t *argv[7];
88 int r;
89
90 memset(argv, 0, sizeof(argv));
91 memset(&f, 0, sizeof(f));
92
93 /* do we have everything we need? */
94 if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
95 fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
96 (void *)assert->rp_id, (void *)assert->cdh.ptr);
97 r = FIDO_ERR_INVALID_ARGUMENT;
98 goto fail;
99 }
100
101 if ((argv[0] = cbor_build_string(assert->rp_id)) == NULL ||
102 (argv[1] = fido_blob_encode(&assert->cdh)) == NULL) {
103 fido_log_debug("%s: cbor encode", __func__);
104 r = FIDO_ERR_INTERNAL;
105 goto fail;
106 }
107
108 /* allowed credentials */
109 if (assert->allow_list.len) {
110 const fido_blob_array_t *cl = &assert->allow_list;
111 if ((argv[2] = cbor_encode_pubkey_list(cl)) == NULL) {
112 fido_log_debug("%s: cbor_encode_pubkey_list", __func__);
113 r = FIDO_ERR_INTERNAL;
114 goto fail;
115 }
116 }
117
118 /* hmac-secret extension */
119 if (assert->ext & FIDO_EXT_HMAC_SECRET)
120 if ((argv[3] = cbor_encode_hmac_secret_param(ecdh, pk,
121 &assert->hmac_salt)) == NULL) {
122 fido_log_debug("%s: cbor_encode_hmac_secret_param",
123 __func__);
124 r = FIDO_ERR_INTERNAL;
125 goto fail;
126 }
127
128 /* options */
129 if (assert->up != FIDO_OPT_OMIT || assert->uv != FIDO_OPT_OMIT)
130 if ((argv[4] = cbor_encode_assert_options(assert->up,
131 assert->uv)) == NULL) {
132 fido_log_debug("%s: cbor_encode_assert_options",
133 __func__);
134 r = FIDO_ERR_INTERNAL;
135 goto fail;
136 }
137
138 /* pin authentication */
139 if (pin) {
140 if (pk == NULL || ecdh == NULL) {
141 fido_log_debug("%s: pin=%p, pk=%p, ecdh=%p", __func__,
142 (const void *)pin, (const void *)pk,
143 (const void *)ecdh);
144 r = FIDO_ERR_INVALID_ARGUMENT;
145 goto fail;
146 }
147 if ((r = cbor_add_pin_params(dev, &assert->cdh, pk, ecdh, pin,
148 &argv[5], &argv[6])) != FIDO_OK) {
149 fido_log_debug("%s: cbor_add_pin_params", __func__);
150 goto fail;
151 }
152 }
153
154 /* frame and transmit */
155 if (cbor_build_frame(CTAP_CBOR_ASSERT, argv, 7, &f) < 0 ||
156 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
157 fido_log_debug("%s: fido_tx", __func__);
158 r = FIDO_ERR_TX;
159 goto fail;
160 }
161
162 r = FIDO_OK;
163fail:
164 cbor_vector_free(argv, nitems(argv));
165 free(f.ptr);
166
167 return (r);
168}
169
170static int
171fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms)
172{
173 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
174 unsigned char reply[2048];
175 int reply_len;
176 int r;
177
178 fido_assert_reset_rx(assert);
179
180 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
181 fido_log_debug("%s: fido_rx", __func__);
182 return (FIDO_ERR_RX);
183 }
184
185 /* start with room for a single assertion */
186 if ((assert->stmt = calloc(1, sizeof(fido_assert_stmt))) == NULL)
187 return (FIDO_ERR_INTERNAL);
188
189 assert->stmt_len = 0;
190 assert->stmt_cnt = 1;
191
192 /* adjust as needed */
193 if ((r = cbor_parse_reply(reply, (size_t)reply_len, assert,
194 adjust_assert_count)) != FIDO_OK) {
195 fido_log_debug("%s: adjust_assert_count", __func__);
196 return (r);
197 }
198
199 /* parse the first assertion */
200 if ((r = cbor_parse_reply(reply, (size_t)reply_len,
201 &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
202 fido_log_debug("%s: parse_assert_reply", __func__);
203 return (r);
204 }
205
206 assert->stmt_len++;
207
208 return (FIDO_OK);
209}
210
211static int
212fido_get_next_assert_tx(fido_dev_t *dev)
213{
214 const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT };
215 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
216
217 if (fido_tx(dev, cmd, cbor, sizeof(cbor)) < 0) {
218 fido_log_debug("%s: fido_tx", __func__);
219 return (FIDO_ERR_TX);
220 }
221
222 return (FIDO_OK);
223}
224
225static int
226fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms)
227{
228 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
229 unsigned char reply[2048];
230 int reply_len;
231 int r;
232
233 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
234 fido_log_debug("%s: fido_rx", __func__);
235 return (FIDO_ERR_RX);
236 }
237
238 /* sanity check */
239 if (assert->stmt_len >= assert->stmt_cnt) {
240 fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu", __func__,
241 assert->stmt_len, assert->stmt_cnt);
242 return (FIDO_ERR_INTERNAL);
243 }
244
245 if ((r = cbor_parse_reply(reply, (size_t)reply_len,
246 &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
247 fido_log_debug("%s: parse_assert_reply", __func__);
248 return (r);
249 }
250
251 return (FIDO_OK);
252}
253
254static int
255fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert,
256 const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int ms)
257{
258 int r;
259
260 if ((r = fido_dev_get_assert_tx(dev, assert, pk, ecdh, pin)) != FIDO_OK ||
261 (r = fido_dev_get_assert_rx(dev, assert, ms)) != FIDO_OK)
262 return (r);
263
264 while (assert->stmt_len < assert->stmt_cnt) {
265 if ((r = fido_get_next_assert_tx(dev)) != FIDO_OK ||
266 (r = fido_get_next_assert_rx(dev, assert, ms)) != FIDO_OK)
267 return (r);
268 assert->stmt_len++;
269 }
270
271 return (FIDO_OK);
272}
273
274static int
275decrypt_hmac_secrets(fido_assert_t *assert, const fido_blob_t *key)
276{
277 for (size_t i = 0; i < assert->stmt_cnt; i++) {
278 fido_assert_stmt *stmt = &assert->stmt[i];
279 if (stmt->hmac_secret_enc.ptr != NULL) {
280 if (aes256_cbc_dec(key, &stmt->hmac_secret_enc,
281 &stmt->hmac_secret) < 0) {
282 fido_log_debug("%s: aes256_cbc_dec %zu",
283 __func__, i);
284 return (-1);
285 }
286 }
287 }
288
289 return (0);
290}
291
292int
293fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
294{
295 fido_blob_t *ecdh = NULL;
296 es256_pk_t *pk = NULL;
297 int r;
298
299 if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
300 fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
301 (void *)assert->rp_id, (void *)assert->cdh.ptr);
302 return (FIDO_ERR_INVALID_ARGUMENT);
303 }
304
305 if (fido_dev_is_fido2(dev) == false) {
306 if (pin != NULL || assert->ext != 0)
307 return (FIDO_ERR_UNSUPPORTED_OPTION);
308 return (u2f_authenticate(dev, assert, -1));
309 }
310
311 if (pin != NULL || assert->ext != 0) {
312 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
313 fido_log_debug("%s: fido_do_ecdh", __func__);
314 goto fail;
315 }
316 }
317
318 r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, -1);
319 if (r == FIDO_OK && assert->ext & FIDO_EXT_HMAC_SECRET)
320 if (decrypt_hmac_secrets(assert, ecdh) < 0) {
321 fido_log_debug("%s: decrypt_hmac_secrets", __func__);
322 r = FIDO_ERR_INTERNAL;
323 goto fail;
324 }
325
326fail:
327 es256_pk_free(&pk);
328 fido_blob_free(&ecdh);
329
330 return (r);
331}
332
333int
334fido_check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv)
335{
336 fido_log_debug("%s: flags=%02x", __func__, flags);
337 fido_log_debug("%s: up=%d, uv=%d", __func__, up, uv);
338
339 if (up == FIDO_OPT_TRUE &&
340 (flags & CTAP_AUTHDATA_USER_PRESENT) == 0) {
341 fido_log_debug("%s: CTAP_AUTHDATA_USER_PRESENT", __func__);
342 return (-1); /* user not present */
343 }
344
345 if (uv == FIDO_OPT_TRUE &&
346 (flags & CTAP_AUTHDATA_USER_VERIFIED) == 0) {
347 fido_log_debug("%s: CTAP_AUTHDATA_USER_VERIFIED", __func__);
348 return (-1); /* user not verified */
349 }
350
351 return (0);
352}
353
354static int
355check_extensions(int authdata_ext, int ext)
356{
357 if (authdata_ext != ext) {
358 fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__,
359 authdata_ext, ext);
360 return (-1);
361 }
362
363 return (0);
364}
365
366static int
367get_signed_hash(int cose_alg, fido_blob_t *dgst, const fido_blob_t *clientdata,
368 const fido_blob_t *authdata_cbor)
369{
370 cbor_item_t *item = NULL;
371 unsigned char *authdata_ptr = NULL;
372 size_t authdata_len;
373 struct cbor_load_result cbor;
374 SHA256_CTX ctx;
375 int ok = -1;
376
377 if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len,
378 &cbor)) == NULL || cbor_isa_bytestring(item) == false ||
379 cbor_bytestring_is_definite(item) == false) {
380 fido_log_debug("%s: authdata", __func__);
381 goto fail;
382 }
383
384 authdata_ptr = cbor_bytestring_handle(item);
385 authdata_len = cbor_bytestring_length(item);
386
387 if (cose_alg != COSE_EDDSA) {
388 if (dgst->len < SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 ||
389 SHA256_Update(&ctx, authdata_ptr, authdata_len) == 0 ||
390 SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 ||
391 SHA256_Final(dgst->ptr, &ctx) == 0) {
392 fido_log_debug("%s: sha256", __func__);
393 goto fail;
394 }
395 dgst->len = SHA256_DIGEST_LENGTH;
396 } else {
397 if (SIZE_MAX - authdata_len < clientdata->len ||
398 dgst->len < authdata_len + clientdata->len) {
399 fido_log_debug("%s: memcpy", __func__);
400 goto fail;
401 }
402 memcpy(dgst->ptr, authdata_ptr, authdata_len);
403 memcpy(dgst->ptr + authdata_len, clientdata->ptr,
404 clientdata->len);
405 dgst->len = authdata_len + clientdata->len;
406 }
407
408 ok = 0;
409fail:
410 if (item != NULL)
411 cbor_decref(&item);
412
413 return (ok);
414}
415
416int
417fido_verify_sig_es256(const fido_blob_t *dgst, const es256_pk_t *pk,
418 const fido_blob_t *sig)
419{
420 EVP_PKEY *pkey = NULL;
421 EC_KEY *ec = NULL;
422 int ok = -1;
423
424 /* ECDSA_verify needs ints */
425 if (dgst->len > INT_MAX || sig->len > INT_MAX) {
426 fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
427 dgst->len, sig->len);
428 return (-1);
429 }
430
431 if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL ||
432 (ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
433 fido_log_debug("%s: pk -> ec", __func__);
434 goto fail;
435 }
436
437 if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr,
438 (int)sig->len, ec) != 1) {
439 fido_log_debug("%s: ECDSA_verify", __func__);
440 goto fail;
441 }
442
443 ok = 0;
444fail:
445 if (pkey != NULL)
446 EVP_PKEY_free(pkey);
447
448 return (ok);
449}
450
451int
452fido_verify_sig_rs256(const fido_blob_t *dgst, const rs256_pk_t *pk,
453 const fido_blob_t *sig)
454{
455 EVP_PKEY *pkey = NULL;
456 RSA *rsa = NULL;
457 int ok = -1;
458
459 /* RSA_verify needs unsigned ints */
460 if (dgst->len > UINT_MAX || sig->len > UINT_MAX) {
461 fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
462 dgst->len, sig->len);
463 return (-1);
464 }
465
466 if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL ||
467 (rsa = EVP_PKEY_get0_RSA(pkey)) == NULL) {
468 fido_log_debug("%s: pk -> ec", __func__);
469 goto fail;
470 }
471
472 if (RSA_verify(NID_sha256, dgst->ptr, (unsigned int)dgst->len, sig->ptr,
473 (unsigned int)sig->len, rsa) != 1) {
474 fido_log_debug("%s: RSA_verify", __func__);
475 goto fail;
476 }
477
478 ok = 0;
479fail:
480 if (pkey != NULL)
481 EVP_PKEY_free(pkey);
482
483 return (ok);
484}
485
486int
487fido_verify_sig_eddsa(const fido_blob_t *dgst, const eddsa_pk_t *pk,
488 const fido_blob_t *sig)
489{
490 EVP_PKEY *pkey = NULL;
491 EVP_MD_CTX *mdctx = NULL;
492 int ok = -1;
493
494 /* EVP_DigestVerify needs ints */
495 if (dgst->len > INT_MAX || sig->len > INT_MAX) {
496 fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
497 dgst->len, sig->len);
498 return (-1);
499 }
500
501 if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
502 fido_log_debug("%s: pk -> pkey", __func__);
503 goto fail;
504 }
505
506 if ((mdctx = EVP_MD_CTX_new()) == NULL) {
507 fido_log_debug("%s: EVP_MD_CTX_new", __func__);
508 goto fail;
509 }
510
511 if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) != 1) {
512 fido_log_debug("%s: EVP_DigestVerifyInit", __func__);
513 goto fail;
514 }
515
516 if (EVP_DigestVerify(mdctx, sig->ptr, sig->len, dgst->ptr,
517 dgst->len) != 1) {
518 fido_log_debug("%s: EVP_DigestVerify", __func__);
519 goto fail;
520 }
521
522 ok = 0;
523fail:
524 if (mdctx != NULL)
525 EVP_MD_CTX_free(mdctx);
526
527 if (pkey != NULL)
528 EVP_PKEY_free(pkey);
529
530 return (ok);
531}
532
533int
534fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg,
535 const void *pk)
536{
537 unsigned char buf[1024];
538 fido_blob_t dgst;
539 const fido_assert_stmt *stmt = NULL;
540 int ok = -1;
541 int r;
542
543 dgst.ptr = buf;
544 dgst.len = sizeof(buf);
545
546 if (idx >= assert->stmt_len || pk == NULL) {
547 r = FIDO_ERR_INVALID_ARGUMENT;
548 goto out;
549 }
550
551 stmt = &assert->stmt[idx];
552
553 /* do we have everything we need? */
554 if (assert->cdh.ptr == NULL || assert->rp_id == NULL ||
555 stmt->authdata_cbor.ptr == NULL || stmt->sig.ptr == NULL) {
556 fido_log_debug("%s: cdh=%p, rp_id=%s, authdata=%p, sig=%p",
557 __func__, (void *)assert->cdh.ptr, assert->rp_id,
558 (void *)stmt->authdata_cbor.ptr, (void *)stmt->sig.ptr);
559 r = FIDO_ERR_INVALID_ARGUMENT;
560 goto out;
561 }
562
563 if (fido_check_flags(stmt->authdata.flags, assert->up,
564 assert->uv) < 0) {
565 fido_log_debug("%s: fido_check_flags", __func__);
566 r = FIDO_ERR_INVALID_PARAM;
567 goto out;
568 }
569
570 if (check_extensions(stmt->authdata_ext, assert->ext) < 0) {
571 fido_log_debug("%s: check_extensions", __func__);
572 r = FIDO_ERR_INVALID_PARAM;
573 goto out;
574 }
575
576 if (fido_check_rp_id(assert->rp_id, stmt->authdata.rp_id_hash) != 0) {
577 fido_log_debug("%s: fido_check_rp_id", __func__);
578 r = FIDO_ERR_INVALID_PARAM;
579 goto out;
580 }
581
582 if (get_signed_hash(cose_alg, &dgst, &assert->cdh,
583 &stmt->authdata_cbor) < 0) {
584 fido_log_debug("%s: get_signed_hash", __func__);
585 r = FIDO_ERR_INTERNAL;
586 goto out;
587 }
588
589 switch (cose_alg) {
590 case COSE_ES256:
591 ok = fido_verify_sig_es256(&dgst, pk, &stmt->sig);
592 break;
593 case COSE_RS256:
594 ok = fido_verify_sig_rs256(&dgst, pk, &stmt->sig);
595 break;
596 case COSE_EDDSA:
597 ok = fido_verify_sig_eddsa(&dgst, pk, &stmt->sig);
598 break;
599 default:
600 fido_log_debug("%s: unsupported cose_alg %d", __func__,
601 cose_alg);
602 r = FIDO_ERR_UNSUPPORTED_OPTION;
603 goto out;
604 }
605
606 if (ok < 0)
607 r = FIDO_ERR_INVALID_SIG;
608 else
609 r = FIDO_OK;
610out:
611 explicit_bzero(buf, sizeof(buf));
612
613 return (r);
614}
615
616int
617fido_assert_set_clientdata_hash(fido_assert_t *assert,
618 const unsigned char *hash, size_t hash_len)
619{
620 if (fido_blob_set(&assert->cdh, hash, hash_len) < 0)
621 return (FIDO_ERR_INVALID_ARGUMENT);
622
623 return (FIDO_OK);
624}
625
626int
627fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt,
628 size_t salt_len)
629{
630 if ((salt_len != 32 && salt_len != 64) ||
631 fido_blob_set(&assert->hmac_salt, salt, salt_len) < 0)
632 return (FIDO_ERR_INVALID_ARGUMENT);
633
634 return (FIDO_OK);
635}
636
637int
638fido_assert_set_rp(fido_assert_t *assert, const char *id)
639{
640 if (assert->rp_id != NULL) {
641 free(assert->rp_id);
642 assert->rp_id = NULL;
643 }
644
645 if (id == NULL)
646 return (FIDO_ERR_INVALID_ARGUMENT);
647
648 if ((assert->rp_id = strdup(id)) == NULL)
649 return (FIDO_ERR_INTERNAL);
650
651 return (FIDO_OK);
652}
653
654int
655fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr,
656 size_t len)
657{
658 fido_blob_t id;
659 fido_blob_t *list_ptr;
660 int r;
661
662 memset(&id, 0, sizeof(id));
663
664 if (assert->allow_list.len == SIZE_MAX) {
665 r = FIDO_ERR_INVALID_ARGUMENT;
666 goto fail;
667 }
668
669 if (fido_blob_set(&id, ptr, len) < 0 || (list_ptr =
670 recallocarray(assert->allow_list.ptr, assert->allow_list.len,
671 assert->allow_list.len + 1, sizeof(fido_blob_t))) == NULL) {
672 r = FIDO_ERR_INVALID_ARGUMENT;
673 goto fail;
674 }
675
676 list_ptr[assert->allow_list.len++] = id;
677 assert->allow_list.ptr = list_ptr;
678
679 return (FIDO_OK);
680fail:
681 free(id.ptr);
682
683 return (r);
684
685}
686
687int
688fido_assert_set_extensions(fido_assert_t *assert, int ext)
689{
690 if (ext != 0 && ext != FIDO_EXT_HMAC_SECRET)
691 return (FIDO_ERR_INVALID_ARGUMENT);
692
693 assert->ext = ext;
694
695 return (FIDO_OK);
696}
697
698int
699fido_assert_set_options(fido_assert_t *assert, bool up, bool uv)
700{
701 assert->up = up ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
702 assert->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
703
704 return (FIDO_OK);
705}
706
707int
708fido_assert_set_up(fido_assert_t *assert, fido_opt_t up)
709{
710 assert->up = up;
711
712 return (FIDO_OK);
713}
714
715int
716fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv)
717{
718 assert->uv = uv;
719
720 return (FIDO_OK);
721}
722
723const unsigned char *
724fido_assert_clientdata_hash_ptr(const fido_assert_t *assert)
725{
726 return (assert->cdh.ptr);
727}
728
729size_t
730fido_assert_clientdata_hash_len(const fido_assert_t *assert)
731{
732 return (assert->cdh.len);
733}
734
735fido_assert_t *
736fido_assert_new(void)
737{
738 return (calloc(1, sizeof(fido_assert_t)));
739}
740
741void
742fido_assert_reset_tx(fido_assert_t *assert)
743{
744 free(assert->rp_id);
745 free(assert->cdh.ptr);
746 free(assert->hmac_salt.ptr);
747 fido_free_blob_array(&assert->allow_list);
748
749 memset(&assert->cdh, 0, sizeof(assert->cdh));
750 memset(&assert->hmac_salt, 0, sizeof(assert->hmac_salt));
751 memset(&assert->allow_list, 0, sizeof(assert->allow_list));
752
753 assert->rp_id = NULL;
754 assert->up = FIDO_OPT_OMIT;
755 assert->uv = FIDO_OPT_OMIT;
756 assert->ext = 0;
757}
758
759void
760fido_assert_reset_rx(fido_assert_t *assert)
761{
762 for (size_t i = 0; i < assert->stmt_cnt; i++) {
763 free(assert->stmt[i].user.id.ptr);
764 free(assert->stmt[i].user.icon);
765 free(assert->stmt[i].user.name);
766 free(assert->stmt[i].user.display_name);
767 free(assert->stmt[i].id.ptr);
768 if (assert->stmt[i].hmac_secret.ptr != NULL) {
769 explicit_bzero(assert->stmt[i].hmac_secret.ptr,
770 assert->stmt[i].hmac_secret.len);
771 }
772 free(assert->stmt[i].hmac_secret.ptr);
773 free(assert->stmt[i].hmac_secret_enc.ptr);
774 free(assert->stmt[i].authdata_cbor.ptr);
775 free(assert->stmt[i].sig.ptr);
776 memset(&assert->stmt[i], 0, sizeof(assert->stmt[i]));
777 }
778
779 free(assert->stmt);
780
781 assert->stmt = NULL;
782 assert->stmt_len = 0;
783 assert->stmt_cnt = 0;
784}
785
786void
787fido_assert_free(fido_assert_t **assert_p)
788{
789 fido_assert_t *assert;
790
791 if (assert_p == NULL || (assert = *assert_p) == NULL)
792 return;
793
794 fido_assert_reset_tx(assert);
795 fido_assert_reset_rx(assert);
796
797 free(assert);
798
799 *assert_p = NULL;
800}
801
802size_t
803fido_assert_count(const fido_assert_t *assert)
804{
805 return (assert->stmt_len);
806}
807
808const char *
809fido_assert_rp_id(const fido_assert_t *assert)
810{
811 return (assert->rp_id);
812}
813
814uint8_t
815fido_assert_flags(const fido_assert_t *assert, size_t idx)
816{
817 if (idx >= assert->stmt_len)
818 return (0);
819
820 return (assert->stmt[idx].authdata.flags);
821}
822
823uint32_t
824fido_assert_sigcount(const fido_assert_t *assert, size_t idx)
825{
826 if (idx >= assert->stmt_len)
827 return (0);
828
829 return (assert->stmt[idx].authdata.sigcount);
830}
831
832const unsigned char *
833fido_assert_authdata_ptr(const fido_assert_t *assert, size_t idx)
834{
835 if (idx >= assert->stmt_len)
836 return (NULL);
837
838 return (assert->stmt[idx].authdata_cbor.ptr);
839}
840
841size_t
842fido_assert_authdata_len(const fido_assert_t *assert, size_t idx)
843{
844 if (idx >= assert->stmt_len)
845 return (0);
846
847 return (assert->stmt[idx].authdata_cbor.len);
848}
849
850const unsigned char *
851fido_assert_sig_ptr(const fido_assert_t *assert, size_t idx)
852{
853 if (idx >= assert->stmt_len)
854 return (NULL);
855
856 return (assert->stmt[idx].sig.ptr);
857}
858
859size_t
860fido_assert_sig_len(const fido_assert_t *assert, size_t idx)
861{
862 if (idx >= assert->stmt_len)
863 return (0);
864
865 return (assert->stmt[idx].sig.len);
866}
867
868const unsigned char *
869fido_assert_id_ptr(const fido_assert_t *assert, size_t idx)
870{
871 if (idx >= assert->stmt_len)
872 return (NULL);
873
874 return (assert->stmt[idx].id.ptr);
875}
876
877size_t
878fido_assert_id_len(const fido_assert_t *assert, size_t idx)
879{
880 if (idx >= assert->stmt_len)
881 return (0);
882
883 return (assert->stmt[idx].id.len);
884}
885
886const unsigned char *
887fido_assert_user_id_ptr(const fido_assert_t *assert, size_t idx)
888{
889 if (idx >= assert->stmt_len)
890 return (NULL);
891
892 return (assert->stmt[idx].user.id.ptr);
893}
894
895size_t
896fido_assert_user_id_len(const fido_assert_t *assert, size_t idx)
897{
898 if (idx >= assert->stmt_len)
899 return (0);
900
901 return (assert->stmt[idx].user.id.len);
902}
903
904const char *
905fido_assert_user_icon(const fido_assert_t *assert, size_t idx)
906{
907 if (idx >= assert->stmt_len)
908 return (NULL);
909
910 return (assert->stmt[idx].user.icon);
911}
912
913const char *
914fido_assert_user_name(const fido_assert_t *assert, size_t idx)
915{
916 if (idx >= assert->stmt_len)
917 return (NULL);
918
919 return (assert->stmt[idx].user.name);
920}
921
922const char *
923fido_assert_user_display_name(const fido_assert_t *assert, size_t idx)
924{
925 if (idx >= assert->stmt_len)
926 return (NULL);
927
928 return (assert->stmt[idx].user.display_name);
929}
930
931const unsigned char *
932fido_assert_hmac_secret_ptr(const fido_assert_t *assert, size_t idx)
933{
934 if (idx >= assert->stmt_len)
935 return (NULL);
936
937 return (assert->stmt[idx].hmac_secret.ptr);
938}
939
940size_t
941fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx)
942{
943 if (idx >= assert->stmt_len)
944 return (0);
945
946 return (assert->stmt[idx].hmac_secret.len);
947}
948
949static void
950fido_assert_clean_authdata(fido_assert_stmt *as)
951{
952 free(as->authdata_cbor.ptr);
953 free(as->hmac_secret_enc.ptr);
954
955 memset(&as->authdata_ext, 0, sizeof(as->authdata_ext));
956 memset(&as->authdata_cbor, 0, sizeof(as->authdata_cbor));
957 memset(&as->authdata, 0, sizeof(as->authdata));
958 memset(&as->hmac_secret_enc, 0, sizeof(as->hmac_secret_enc));
959}
960
961int
962fido_assert_set_authdata(fido_assert_t *assert, size_t idx,
963 const unsigned char *ptr, size_t len)
964{
965 cbor_item_t *item = NULL;
966 fido_assert_stmt *stmt = NULL;
967 struct cbor_load_result cbor;
968 int r;
969
970 if (idx >= assert->stmt_len || ptr == NULL || len == 0)
971 return (FIDO_ERR_INVALID_ARGUMENT);
972
973 stmt = &assert->stmt[idx];
974 fido_assert_clean_authdata(stmt);
975
976 if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
977 fido_log_debug("%s: cbor_load", __func__);
978 r = FIDO_ERR_INVALID_ARGUMENT;
979 goto fail;
980 }
981
982 if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
983 &stmt->authdata, &stmt->authdata_ext, &stmt->hmac_secret_enc) < 0) {
984 fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
985 r = FIDO_ERR_INVALID_ARGUMENT;
986 goto fail;
987 }
988
989 r = FIDO_OK;
990fail:
991 if (item != NULL)
992 cbor_decref(&item);
993
994 if (r != FIDO_OK)
995 fido_assert_clean_authdata(stmt);
996
997 return (r);
998}
999
1000int
1001fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx,
1002 const unsigned char *ptr, size_t len)
1003{
1004 cbor_item_t *item = NULL;
1005 fido_assert_stmt *stmt = NULL;
1006 int r;
1007
1008 if (idx >= assert->stmt_len || ptr == NULL || len == 0)
1009 return (FIDO_ERR_INVALID_ARGUMENT);
1010
1011 stmt = &assert->stmt[idx];
1012 fido_assert_clean_authdata(stmt);
1013
1014 if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
1015 fido_log_debug("%s: cbor_build_bytestring", __func__);
1016 r = FIDO_ERR_INTERNAL;
1017 goto fail;
1018 }
1019
1020 if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
1021 &stmt->authdata, &stmt->authdata_ext, &stmt->hmac_secret_enc) < 0) {
1022 fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
1023 r = FIDO_ERR_INVALID_ARGUMENT;
1024 goto fail;
1025 }
1026
1027 r = FIDO_OK;
1028fail:
1029 if (item != NULL)
1030 cbor_decref(&item);
1031
1032 if (r != FIDO_OK)
1033 fido_assert_clean_authdata(stmt);
1034
1035 return (r);
1036}
1037
1038static void
1039fido_assert_clean_sig(fido_assert_stmt *as)
1040{
1041 free(as->sig.ptr);
1042 as->sig.ptr = NULL;
1043 as->sig.len = 0;
1044}
1045
1046int
1047fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr,
1048 size_t len)
1049{
1050 unsigned char *sig;
1051
1052 if (idx >= a->stmt_len || ptr == NULL || len == 0)
1053 return (FIDO_ERR_INVALID_ARGUMENT);
1054
1055 fido_assert_clean_sig(&a->stmt[idx]);
1056
1057 if ((sig = malloc(len)) == NULL)
1058 return (FIDO_ERR_INTERNAL);
1059
1060 memcpy(sig, ptr, len);
1061 a->stmt[idx].sig.ptr = sig;
1062 a->stmt[idx].sig.len = len;
1063
1064 return (FIDO_OK);
1065}
1066
1067/* XXX shrinking leaks memory; fortunately that shouldn't happen */
1068int
1069fido_assert_set_count(fido_assert_t *assert, size_t n)
1070{
1071 void *new_stmt;
1072
1073#ifdef FIDO_FUZZ
1074 if (n > UINT8_MAX) {
1075 fido_log_debug("%s: n > UINT8_MAX", __func__);
1076 return (FIDO_ERR_INTERNAL);
1077 }
1078#endif
1079
1080 new_stmt = recallocarray(assert->stmt, assert->stmt_cnt, n,
1081 sizeof(fido_assert_stmt));
1082 if (new_stmt == NULL)
1083 return (FIDO_ERR_INTERNAL);
1084
1085 assert->stmt = new_stmt;
1086 assert->stmt_cnt = n;
1087 assert->stmt_len = n;
1088
1089 return (FIDO_OK);
1090}
diff --git a/src/authkey.c b/src/authkey.c
new file mode 100644
index 0000000..9de37f1
--- /dev/null
+++ b/src/authkey.c
@@ -0,0 +1,98 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8#include "fido.h"
9
10static int
11parse_authkey(const cbor_item_t *key, const cbor_item_t *val, void *arg)
12{
13 es256_pk_t *authkey = arg;
14
15 if (cbor_isa_uint(key) == false ||
16 cbor_int_get_width(key) != CBOR_INT_8 ||
17 cbor_get_uint8(key) != 1) {
18 fido_log_debug("%s: cbor type", __func__);
19 return (0); /* ignore */
20 }
21
22 return (es256_pk_decode(val, authkey));
23}
24
25static int
26fido_dev_authkey_tx(fido_dev_t *dev)
27{
28 fido_blob_t f;
29 cbor_item_t *argv[2];
30 int r;
31
32 fido_log_debug("%s: dev=%p", __func__, (void *)dev);
33
34 memset(&f, 0, sizeof(f));
35 memset(argv, 0, sizeof(argv));
36
37 /* add command parameters */
38 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
39 (argv[1] = cbor_build_uint8(2)) == NULL) {
40 fido_log_debug("%s: cbor_build", __func__);
41 r = FIDO_ERR_INTERNAL;
42 goto fail;
43 }
44
45 /* frame and transmit */
46 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 2, &f) < 0 ||
47 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
48 fido_log_debug("%s: fido_tx", __func__);
49 r = FIDO_ERR_TX;
50 goto fail;
51 }
52
53 r = FIDO_OK;
54fail:
55 cbor_vector_free(argv, nitems(argv));
56 free(f.ptr);
57
58 return (r);
59}
60
61static int
62fido_dev_authkey_rx(fido_dev_t *dev, es256_pk_t *authkey, int ms)
63{
64 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
65 unsigned char reply[2048];
66 int reply_len;
67
68 fido_log_debug("%s: dev=%p, authkey=%p, ms=%d", __func__, (void *)dev,
69 (void *)authkey, ms);
70
71 memset(authkey, 0, sizeof(*authkey));
72
73 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
74 fido_log_debug("%s: fido_rx", __func__);
75 return (FIDO_ERR_RX);
76 }
77
78 return (cbor_parse_reply(reply, (size_t)reply_len, authkey,
79 parse_authkey));
80}
81
82static int
83fido_dev_authkey_wait(fido_dev_t *dev, es256_pk_t *authkey, int ms)
84{
85 int r;
86
87 if ((r = fido_dev_authkey_tx(dev)) != FIDO_OK ||
88 (r = fido_dev_authkey_rx(dev, authkey, ms)) != FIDO_OK)
89 return (r);
90
91 return (FIDO_OK);
92}
93
94int
95fido_dev_authkey(fido_dev_t *dev, es256_pk_t *authkey)
96{
97 return (fido_dev_authkey_wait(dev, authkey, -1));
98}
diff --git a/src/bio.c b/src/bio.c
new file mode 100644
index 0000000..74814b9
--- /dev/null
+++ b/src/bio.c
@@ -0,0 +1,844 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8
9#include "fido.h"
10#include "fido/bio.h"
11#include "fido/es256.h"
12
13#define CMD_ENROLL_BEGIN 0x01
14#define CMD_ENROLL_NEXT 0x02
15#define CMD_ENROLL_CANCEL 0x03
16#define CMD_ENUM 0x04
17#define CMD_SET_NAME 0x05
18#define CMD_ENROLL_REMOVE 0x06
19#define CMD_GET_INFO 0x07
20
21static int
22bio_prepare_hmac(uint8_t cmd, cbor_item_t **argv, size_t argc,
23 cbor_item_t **param, fido_blob_t *hmac_data)
24{
25 const uint8_t prefix[2] = { 0x01 /* modality */, cmd };
26 int ok = -1;
27 size_t cbor_alloc_len;
28 size_t cbor_len;
29 unsigned char *cbor = NULL;
30
31 if (argv == NULL || param == NULL)
32 return (fido_blob_set(hmac_data, prefix, sizeof(prefix)));
33
34 if ((*param = cbor_flatten_vector(argv, argc)) == NULL) {
35 fido_log_debug("%s: cbor_flatten_vector", __func__);
36 goto fail;
37 }
38
39 if ((cbor_len = cbor_serialize_alloc(*param, &cbor,
40 &cbor_alloc_len)) == 0 || cbor_len > SIZE_MAX - sizeof(prefix)) {
41 fido_log_debug("%s: cbor_serialize_alloc", __func__);
42 goto fail;
43 }
44
45 if ((hmac_data->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) {
46 fido_log_debug("%s: malloc", __func__);
47 goto fail;
48 }
49
50 memcpy(hmac_data->ptr, prefix, sizeof(prefix));
51 memcpy(hmac_data->ptr + sizeof(prefix), cbor, cbor_len);
52 hmac_data->len = cbor_len + sizeof(prefix);
53
54 ok = 0;
55fail:
56 free(cbor);
57
58 return (ok);
59}
60
61static int
62bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc,
63 const char *pin, const fido_blob_t *token)
64{
65 cbor_item_t *argv[5];
66 es256_pk_t *pk = NULL;
67 fido_blob_t *ecdh = NULL;
68 fido_blob_t f;
69 fido_blob_t hmac;
70 int r = FIDO_ERR_INTERNAL;
71
72 memset(&f, 0, sizeof(f));
73 memset(&hmac, 0, sizeof(hmac));
74 memset(&argv, 0, sizeof(argv));
75
76 /* modality, subCommand */
77 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
78 (argv[1] = cbor_build_uint8(cmd)) == NULL) {
79 fido_log_debug("%s: cbor encode", __func__);
80 goto fail;
81 }
82
83 /* subParams */
84 if (pin || token) {
85 if (bio_prepare_hmac(cmd, sub_argv, sub_argc, &argv[2],
86 &hmac) < 0) {
87 fido_log_debug("%s: bio_prepare_hmac", __func__);
88 goto fail;
89 }
90 }
91
92 /* pinProtocol, pinAuth */
93 if (pin) {
94 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
95 fido_log_debug("%s: fido_do_ecdh", __func__);
96 goto fail;
97 }
98 if ((r = cbor_add_pin_params(dev, &hmac, pk, ecdh, pin,
99 &argv[4], &argv[3])) != FIDO_OK) {
100 fido_log_debug("%s: cbor_add_pin_params", __func__);
101 goto fail;
102 }
103 } else if (token) {
104 if ((argv[3] = cbor_encode_pin_opt()) == NULL ||
105 (argv[4] = cbor_encode_pin_auth(token, &hmac)) == NULL) {
106 fido_log_debug("%s: encode pin", __func__);
107 goto fail;
108 }
109 }
110
111 /* framing and transmission */
112 if (cbor_build_frame(CTAP_CBOR_BIO_ENROLL_PRE, argv, 5, &f) < 0 ||
113 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
114 fido_log_debug("%s: fido_tx", __func__);
115 r = FIDO_ERR_TX;
116 goto fail;
117 }
118
119 r = FIDO_OK;
120fail:
121 cbor_vector_free(argv, nitems(argv));
122 es256_pk_free(&pk);
123 fido_blob_free(&ecdh);
124 free(f.ptr);
125 free(hmac.ptr);
126
127 return (r);
128}
129
130static void
131bio_reset_template(fido_bio_template_t *t)
132{
133 free(t->name);
134 free(t->id.ptr);
135 t->name = NULL;
136 memset(&t->id, 0, sizeof(t->id));
137}
138
139static void
140bio_reset_template_array(fido_bio_template_array_t *ta)
141{
142 for (size_t i = 0; i < ta->n_alloc; i++)
143 bio_reset_template(&ta->ptr[i]);
144
145 free(ta->ptr);
146 ta->ptr = NULL;
147 memset(ta, 0, sizeof(*ta));
148}
149
150static int
151decode_template(const cbor_item_t *key, const cbor_item_t *val, void *arg)
152{
153 fido_bio_template_t *t = arg;
154
155 if (cbor_isa_uint(key) == false ||
156 cbor_int_get_width(key) != CBOR_INT_8) {
157 fido_log_debug("%s: cbor type", __func__);
158 return (0); /* ignore */
159 }
160
161 switch (cbor_get_uint8(key)) {
162 case 1: /* id */
163 return (fido_blob_decode(val, &t->id));
164 case 2: /* name */
165 return (cbor_string_copy(val, &t->name));
166 }
167
168 return (0); /* ignore */
169}
170
171static int
172decode_template_array(const cbor_item_t *item, void *arg)
173{
174 fido_bio_template_array_t *ta = arg;
175
176 if (cbor_isa_map(item) == false ||
177 cbor_map_is_definite(item) == false) {
178 fido_log_debug("%s: cbor type", __func__);
179 return (-1);
180 }
181
182 if (ta->n_rx >= ta->n_alloc) {
183 fido_log_debug("%s: n_rx >= n_alloc", __func__);
184 return (-1);
185 }
186
187 if (cbor_map_iter(item, &ta->ptr[ta->n_rx], decode_template) < 0) {
188 fido_log_debug("%s: decode_template", __func__);
189 return (-1);
190 }
191
192 ta->n_rx++;
193
194 return (0);
195}
196
197static int
198bio_parse_template_array(const cbor_item_t *key, const cbor_item_t *val,
199 void *arg)
200{
201 fido_bio_template_array_t *ta = arg;
202
203 if (cbor_isa_uint(key) == false ||
204 cbor_int_get_width(key) != CBOR_INT_8 ||
205 cbor_get_uint8(key) != 7) {
206 fido_log_debug("%s: cbor type", __func__);
207 return (0); /* ignore */
208 }
209
210 if (cbor_isa_array(val) == false ||
211 cbor_array_is_definite(val) == false) {
212 fido_log_debug("%s: cbor type", __func__);
213 return (-1);
214 }
215
216 if (ta->ptr != NULL || ta->n_alloc != 0 || ta->n_rx != 0) {
217 fido_log_debug("%s: ptr != NULL || n_alloc != 0 || n_rx != 0",
218 __func__);
219 return (-1);
220 }
221
222 if ((ta->ptr = calloc(cbor_array_size(val), sizeof(*ta->ptr))) == NULL)
223 return (-1);
224
225 ta->n_alloc = cbor_array_size(val);
226
227 if (cbor_array_iter(val, ta, decode_template_array) < 0) {
228 fido_log_debug("%s: decode_template_array", __func__);
229 return (-1);
230 }
231
232 return (0);
233}
234
235static int
236bio_rx_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, int ms)
237{
238 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
239 unsigned char reply[2048];
240 int reply_len;
241 int r;
242
243 bio_reset_template_array(ta);
244
245 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
246 fido_log_debug("%s: fido_rx", __func__);
247 return (FIDO_ERR_RX);
248 }
249
250 if ((r = cbor_parse_reply(reply, (size_t)reply_len, ta,
251 bio_parse_template_array)) != FIDO_OK) {
252 fido_log_debug("%s: bio_parse_template_array" , __func__);
253 return (r);
254 }
255
256 return (FIDO_OK);
257}
258
259static int
260bio_get_template_array_wait(fido_dev_t *dev, fido_bio_template_array_t *ta,
261 const char *pin, int ms)
262{
263 int r;
264
265 if ((r = bio_tx(dev, CMD_ENUM, NULL, 0, pin, NULL)) != FIDO_OK ||
266 (r = bio_rx_template_array(dev, ta, ms)) != FIDO_OK)
267 return (r);
268
269 return (FIDO_OK);
270}
271
272int
273fido_bio_dev_get_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta,
274 const char *pin)
275{
276 if (pin == NULL)
277 return (FIDO_ERR_INVALID_ARGUMENT);
278
279 return (bio_get_template_array_wait(dev, ta, pin, -1));
280}
281
282static int
283bio_set_template_name_wait(fido_dev_t *dev, const fido_bio_template_t *t,
284 const char *pin, int ms)
285{
286 cbor_item_t *argv[2];
287 int r = FIDO_ERR_INTERNAL;
288
289 memset(&argv, 0, sizeof(argv));
290
291 if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
292 (argv[1] = cbor_build_string(t->name)) == NULL) {
293 fido_log_debug("%s: cbor encode", __func__);
294 goto fail;
295 }
296
297 if ((r = bio_tx(dev, CMD_SET_NAME, argv, 2, pin, NULL)) != FIDO_OK ||
298 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
299 fido_log_debug("%s: tx/rx", __func__);
300 goto fail;
301 }
302
303 r = FIDO_OK;
304fail:
305 cbor_vector_free(argv, nitems(argv));
306
307 return (r);
308}
309
310int
311fido_bio_dev_set_template_name(fido_dev_t *dev, const fido_bio_template_t *t,
312 const char *pin)
313{
314 if (pin == NULL || t->name == NULL)
315 return (FIDO_ERR_INVALID_ARGUMENT);
316
317 return (bio_set_template_name_wait(dev, t, pin, -1));
318}
319
320static void
321bio_reset_enroll(fido_bio_enroll_t *e)
322{
323 e->remaining_samples = 0;
324 e->last_status = 0;
325
326 if (e->token)
327 fido_blob_free(&e->token);
328}
329
330static int
331bio_parse_enroll_status(const cbor_item_t *key, const cbor_item_t *val,
332 void *arg)
333{
334 fido_bio_enroll_t *e = arg;
335 uint64_t x;
336
337 if (cbor_isa_uint(key) == false ||
338 cbor_int_get_width(key) != CBOR_INT_8) {
339 fido_log_debug("%s: cbor type", __func__);
340 return (0); /* ignore */
341 }
342
343 switch (cbor_get_uint8(key)) {
344 case 5:
345 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
346 fido_log_debug("%s: cbor_decode_uint64", __func__);
347 return (-1);
348 }
349 e->last_status = (uint8_t)x;
350 break;
351 case 6:
352 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
353 fido_log_debug("%s: cbor_decode_uint64", __func__);
354 return (-1);
355 }
356 e->remaining_samples = (uint8_t)x;
357 break;
358 default:
359 return (0); /* ignore */
360 }
361
362 return (0);
363}
364
365static int
366bio_parse_template_id(const cbor_item_t *key, const cbor_item_t *val,
367 void *arg)
368{
369 fido_blob_t *id = arg;
370
371 if (cbor_isa_uint(key) == false ||
372 cbor_int_get_width(key) != CBOR_INT_8 ||
373 cbor_get_uint8(key) != 4) {
374 fido_log_debug("%s: cbor type", __func__);
375 return (0); /* ignore */
376 }
377
378 return (fido_blob_decode(val, id));
379}
380
381static int
382bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
383 fido_bio_enroll_t *e, int ms)
384{
385 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
386 unsigned char reply[2048];
387 int reply_len;
388 int r;
389
390 bio_reset_template(t);
391
392 e->remaining_samples = 0;
393 e->last_status = 0;
394
395 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
396 fido_log_debug("%s: fido_rx", __func__);
397 return (FIDO_ERR_RX);
398 }
399
400 if ((r = cbor_parse_reply(reply, (size_t)reply_len, e,
401 bio_parse_enroll_status)) != FIDO_OK) {
402 fido_log_debug("%s: bio_parse_enroll_status", __func__);
403 return (r);
404 }
405 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &t->id,
406 bio_parse_template_id)) != FIDO_OK) {
407 fido_log_debug("%s: bio_parse_template_id", __func__);
408 return (r);
409 }
410
411 return (FIDO_OK);
412}
413
414static int
415bio_enroll_begin_wait(fido_dev_t *dev, fido_bio_template_t *t,
416 fido_bio_enroll_t *e, uint32_t timo_ms, int ms)
417{
418 cbor_item_t *argv[3];
419 const uint8_t cmd = CMD_ENROLL_BEGIN;
420 int r = FIDO_ERR_INTERNAL;
421
422 memset(&argv, 0, sizeof(argv));
423
424 if ((argv[2] = cbor_build_uint32(timo_ms)) == NULL) {
425 fido_log_debug("%s: cbor encode", __func__);
426 goto fail;
427 }
428
429 if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK ||
430 (r = bio_rx_enroll_begin(dev, t, e, ms)) != FIDO_OK) {
431 fido_log_debug("%s: tx/rx", __func__);
432 goto fail;
433 }
434
435 r = FIDO_OK;
436fail:
437 cbor_vector_free(argv, nitems(argv));
438
439 return (r);
440}
441
442int
443fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
444 fido_bio_enroll_t *e, uint32_t timo_ms, const char *pin)
445{
446 es256_pk_t *pk = NULL;
447 fido_blob_t *ecdh = NULL;
448 fido_blob_t *token = NULL;
449 int r;
450
451 if (pin == NULL || e->token != NULL)
452 return (FIDO_ERR_INVALID_ARGUMENT);
453
454 if ((token = fido_blob_new()) == NULL) {
455 r = FIDO_ERR_INTERNAL;
456 goto fail;
457 }
458
459 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
460 fido_log_debug("%s: fido_do_ecdh", __func__);
461 goto fail;
462 }
463
464 if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) {
465 fido_log_debug("%s: fido_dev_get_pin_token", __func__);
466 goto fail;
467 }
468
469 e->token = token;
470 token = NULL;
471fail:
472 es256_pk_free(&pk);
473 fido_blob_free(&ecdh);
474 fido_blob_free(&token);
475
476 if (r != FIDO_OK)
477 return (r);
478
479 return (bio_enroll_begin_wait(dev, t, e, timo_ms, -1));
480}
481
482static int
483bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int ms)
484{
485 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
486 unsigned char reply[2048];
487 int reply_len;
488 int r;
489
490 e->remaining_samples = 0;
491 e->last_status = 0;
492
493 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
494 fido_log_debug("%s: fido_rx", __func__);
495 return (FIDO_ERR_RX);
496 }
497
498 if ((r = cbor_parse_reply(reply, (size_t)reply_len, e,
499 bio_parse_enroll_status)) != FIDO_OK) {
500 fido_log_debug("%s: bio_parse_enroll_status", __func__);
501 return (r);
502 }
503
504 return (FIDO_OK);
505}
506
507static int
508bio_enroll_continue_wait(fido_dev_t *dev, const fido_bio_template_t *t,
509 fido_bio_enroll_t *e, uint32_t timo_ms, int ms)
510{
511 cbor_item_t *argv[3];
512 const uint8_t cmd = CMD_ENROLL_NEXT;
513 int r = FIDO_ERR_INTERNAL;
514
515 memset(&argv, 0, sizeof(argv));
516
517 if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
518 (argv[2] = cbor_build_uint32(timo_ms)) == NULL) {
519 fido_log_debug("%s: cbor encode", __func__);
520 goto fail;
521 }
522
523 if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK ||
524 (r = bio_rx_enroll_continue(dev, e, ms)) != FIDO_OK) {
525 fido_log_debug("%s: tx/rx", __func__);
526 goto fail;
527 }
528
529 r = FIDO_OK;
530fail:
531 cbor_vector_free(argv, nitems(argv));
532
533 return (r);
534}
535
536int
537fido_bio_dev_enroll_continue(fido_dev_t *dev, const fido_bio_template_t *t,
538 fido_bio_enroll_t *e, uint32_t timo_ms)
539{
540 if (e->token == NULL)
541 return (FIDO_ERR_INVALID_ARGUMENT);
542
543 return (bio_enroll_continue_wait(dev, t, e, timo_ms, -1));
544}
545
546static int
547bio_enroll_cancel_wait(fido_dev_t *dev, int ms)
548{
549 const uint8_t cmd = CMD_ENROLL_CANCEL;
550 int r;
551
552 if ((r = bio_tx(dev, cmd, NULL, 0, NULL, NULL)) != FIDO_OK ||
553 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
554 fido_log_debug("%s: tx/rx", __func__);
555 return (r);
556 }
557
558 return (FIDO_OK);
559}
560
561int
562fido_bio_dev_enroll_cancel(fido_dev_t *dev)
563{
564 return (bio_enroll_cancel_wait(dev, -1));
565}
566
567static int
568bio_enroll_remove_wait(fido_dev_t *dev, const fido_bio_template_t *t,
569 const char *pin, int ms)
570{
571 cbor_item_t *argv[1];
572 const uint8_t cmd = CMD_ENROLL_REMOVE;
573 int r = FIDO_ERR_INTERNAL;
574
575 memset(&argv, 0, sizeof(argv));
576
577 if ((argv[0] = fido_blob_encode(&t->id)) == NULL) {
578 fido_log_debug("%s: cbor encode", __func__);
579 goto fail;
580 }
581
582 if ((r = bio_tx(dev, cmd, argv, 1, pin, NULL)) != FIDO_OK ||
583 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
584 fido_log_debug("%s: tx/rx", __func__);
585 goto fail;
586 }
587
588 r = FIDO_OK;
589fail:
590 cbor_vector_free(argv, nitems(argv));
591
592 return (r);
593}
594
595int
596fido_bio_dev_enroll_remove(fido_dev_t *dev, const fido_bio_template_t *t,
597 const char *pin)
598{
599 return (bio_enroll_remove_wait(dev, t, pin, -1));
600}
601
602static void
603bio_reset_info(fido_bio_info_t *i)
604{
605 i->type = 0;
606 i->max_samples = 0;
607}
608
609static int
610bio_parse_info(const cbor_item_t *key, const cbor_item_t *val, void *arg)
611{
612 fido_bio_info_t *i = arg;
613 uint64_t x;
614
615 if (cbor_isa_uint(key) == false ||
616 cbor_int_get_width(key) != CBOR_INT_8) {
617 fido_log_debug("%s: cbor type", __func__);
618 return (0); /* ignore */
619 }
620
621 switch (cbor_get_uint8(key)) {
622 case 2:
623 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
624 fido_log_debug("%s: cbor_decode_uint64", __func__);
625 return (-1);
626 }
627 i->type = (uint8_t)x;
628 break;
629 case 3:
630 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
631 fido_log_debug("%s: cbor_decode_uint64", __func__);
632 return (-1);
633 }
634 i->max_samples = (uint8_t)x;
635 break;
636 default:
637 return (0); /* ignore */
638 }
639
640 return (0);
641}
642
643static int
644bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int ms)
645{
646 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
647 unsigned char reply[2048];
648 int reply_len;
649 int r;
650
651 bio_reset_info(i);
652
653 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
654 fido_log_debug("%s: fido_rx", __func__);
655 return (FIDO_ERR_RX);
656 }
657
658 if ((r = cbor_parse_reply(reply, (size_t)reply_len, i,
659 bio_parse_info)) != FIDO_OK) {
660 fido_log_debug("%s: bio_parse_info" , __func__);
661 return (r);
662 }
663
664 return (FIDO_OK);
665}
666
667static int
668bio_get_info_wait(fido_dev_t *dev, fido_bio_info_t *i, int ms)
669{
670 int r;
671
672 if ((r = bio_tx(dev, CMD_GET_INFO, NULL, 0, NULL, NULL)) != FIDO_OK ||
673 (r = bio_rx_info(dev, i, ms)) != FIDO_OK) {
674 fido_log_debug("%s: tx/rx", __func__);
675 return (r);
676 }
677
678 return (FIDO_OK);
679}
680
681int
682fido_bio_dev_get_info(fido_dev_t *dev, fido_bio_info_t *i)
683{
684 return (bio_get_info_wait(dev, i, -1));
685}
686
687const char *
688fido_bio_template_name(const fido_bio_template_t *t)
689{
690 return (t->name);
691}
692
693const unsigned char *
694fido_bio_template_id_ptr(const fido_bio_template_t *t)
695{
696 return (t->id.ptr);
697}
698
699size_t
700fido_bio_template_id_len(const fido_bio_template_t *t)
701{
702 return (t->id.len);
703}
704
705size_t
706fido_bio_template_array_count(const fido_bio_template_array_t *ta)
707{
708 return (ta->n_rx);
709}
710
711fido_bio_template_array_t *
712fido_bio_template_array_new(void)
713{
714 return (calloc(1, sizeof(fido_bio_template_array_t)));
715}
716
717fido_bio_template_t *
718fido_bio_template_new(void)
719{
720 return (calloc(1, sizeof(fido_bio_template_t)));
721}
722
723void
724fido_bio_template_array_free(fido_bio_template_array_t **tap)
725{
726 fido_bio_template_array_t *ta;
727
728 if (tap == NULL || (ta = *tap) == NULL)
729 return;
730
731 bio_reset_template_array(ta);
732 free(ta);
733 *tap = NULL;
734}
735
736void
737fido_bio_template_free(fido_bio_template_t **tp)
738{
739 fido_bio_template_t *t;
740
741 if (tp == NULL || (t = *tp) == NULL)
742 return;
743
744 bio_reset_template(t);
745 free(t);
746 *tp = NULL;
747}
748
749int
750fido_bio_template_set_name(fido_bio_template_t *t, const char *name)
751{
752 free(t->name);
753 t->name = NULL;
754
755 if (name && (t->name = strdup(name)) == NULL)
756 return (FIDO_ERR_INTERNAL);
757
758 return (FIDO_OK);
759}
760
761int
762fido_bio_template_set_id(fido_bio_template_t *t, const unsigned char *ptr,
763 size_t len)
764{
765 free(t->id.ptr);
766 t->id.ptr = NULL;
767 t->id.len = 0;
768
769 if (ptr && fido_blob_set(&t->id, ptr, len) < 0)
770 return (FIDO_ERR_INTERNAL);
771
772 return (FIDO_OK);
773}
774
775const fido_bio_template_t *
776fido_bio_template(const fido_bio_template_array_t *ta, size_t idx)
777{
778 if (idx >= ta->n_alloc)
779 return (NULL);
780
781 return (&ta->ptr[idx]);
782}
783
784fido_bio_enroll_t *
785fido_bio_enroll_new(void)
786{
787 return (calloc(1, sizeof(fido_bio_enroll_t)));
788}
789
790fido_bio_info_t *
791fido_bio_info_new(void)
792{
793 return (calloc(1, sizeof(fido_bio_info_t)));
794}
795
796uint8_t
797fido_bio_info_type(const fido_bio_info_t *i)
798{
799 return (i->type);
800}
801
802uint8_t
803fido_bio_info_max_samples(const fido_bio_info_t *i)
804{
805 return (i->max_samples);
806}
807
808void
809fido_bio_enroll_free(fido_bio_enroll_t **ep)
810{
811 fido_bio_enroll_t *e;
812
813 if (ep == NULL || (e = *ep) == NULL)
814 return;
815
816 bio_reset_enroll(e);
817
818 free(e);
819 *ep = NULL;
820}
821
822void
823fido_bio_info_free(fido_bio_info_t **ip)
824{
825 fido_bio_info_t *i;
826
827 if (ip == NULL || (i = *ip) == NULL)
828 return;
829
830 free(i);
831 *ip = NULL;
832}
833
834uint8_t
835fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *e)
836{
837 return (e->remaining_samples);
838}
839
840uint8_t
841fido_bio_enroll_last_status(const fido_bio_enroll_t *e)
842{
843 return (e->last_status);
844}
diff --git a/src/blob.c b/src/blob.c
new file mode 100644
index 0000000..d4eae70
--- /dev/null
+++ b/src/blob.c
@@ -0,0 +1,102 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8#include "fido.h"
9
10fido_blob_t *
11fido_blob_new(void)
12{
13 return (calloc(1, sizeof(fido_blob_t)));
14}
15
16int
17fido_blob_set(fido_blob_t *b, const unsigned char *ptr, size_t len)
18{
19 if (b->ptr != NULL) {
20 explicit_bzero(b->ptr, b->len);
21 free(b->ptr);
22 b->ptr = NULL;
23 }
24
25 b->len = 0;
26
27 if (ptr == NULL || len == 0) {
28 fido_log_debug("%s: ptr=%p, len=%zu", __func__,
29 (const void *)ptr, len);
30 return (-1);
31 }
32
33 if ((b->ptr = malloc(len)) == NULL) {
34 fido_log_debug("%s: malloc", __func__);
35 return (-1);
36 }
37
38 memcpy(b->ptr, ptr, len);
39 b->len = len;
40
41 return (0);
42}
43
44void
45fido_blob_free(fido_blob_t **bp)
46{
47 fido_blob_t *b;
48
49 if (bp == NULL || (b = *bp) == NULL)
50 return;
51
52 if (b->ptr) {
53 explicit_bzero(b->ptr, b->len);
54 free(b->ptr);
55 }
56
57 explicit_bzero(b, sizeof(*b));
58 free(b);
59
60 *bp = NULL;
61}
62
63void
64fido_free_blob_array(fido_blob_array_t *array)
65{
66 if (array->ptr == NULL)
67 return;
68
69 for (size_t i = 0; i < array->len; i++) {
70 fido_blob_t *b = &array->ptr[i];
71 if (b->ptr != NULL) {
72 explicit_bzero(b->ptr, b->len);
73 free(b->ptr);
74 b->ptr = NULL;
75 }
76 }
77
78 free(array->ptr);
79 array->ptr = NULL;
80 array->len = 0;
81}
82
83cbor_item_t *
84fido_blob_encode(const fido_blob_t *b)
85{
86 if (b == NULL || b->ptr == NULL)
87 return (NULL);
88
89 return (cbor_build_bytestring(b->ptr, b->len));
90}
91
92int
93fido_blob_decode(const cbor_item_t *item, fido_blob_t *b)
94{
95 return (cbor_bytestring_copy(item, &b->ptr, &b->len));
96}
97
98int
99fido_blob_is_empty(const fido_blob_t *b)
100{
101 return (b->ptr == NULL || b->len == 0);
102}
diff --git a/src/blob.h b/src/blob.h
new file mode 100644
index 0000000..24fdc23
--- /dev/null
+++ b/src/blob.h
@@ -0,0 +1,28 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _BLOB_H
8#define _BLOB_H
9
10typedef struct fido_blob {
11 unsigned char *ptr;
12 size_t len;
13} fido_blob_t;
14
15typedef struct fido_blob_array {
16 fido_blob_t *ptr;
17 size_t len;
18} fido_blob_array_t;
19
20cbor_item_t *fido_blob_encode(const fido_blob_t *);
21fido_blob_t *fido_blob_new(void);
22int fido_blob_decode(const cbor_item_t *, fido_blob_t *);
23int fido_blob_is_empty(const fido_blob_t *);
24int fido_blob_set(fido_blob_t *, const unsigned char *, size_t);
25void fido_blob_free(fido_blob_t **);
26void fido_free_blob_array(fido_blob_array_t *);
27
28#endif /* !_BLOB_H */
diff --git a/src/buf.c b/src/buf.c
new file mode 100644
index 0000000..4646476
--- /dev/null
+++ b/src/buf.c
@@ -0,0 +1,34 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8#include "fido.h"
9
10int
11fido_buf_read(const unsigned char **buf, size_t *len, void *dst, size_t count)
12{
13 if (count > *len)
14 return (-1);
15
16 memcpy(dst, *buf, count);
17 *buf += count;
18 *len -= count;
19
20 return (0);
21}
22
23int
24fido_buf_write(unsigned char **buf, size_t *len, const void *src, size_t count)
25{
26 if (count > *len)
27 return (-1);
28
29 memcpy(*buf, src, count);
30 *buf += count;
31 *len -= count;
32
33 return (0);
34}
diff --git a/src/cbor.c b/src/cbor.c
new file mode 100644
index 0000000..3e03592
--- /dev/null
+++ b/src/cbor.c
@@ -0,0 +1,1520 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/evp.h>
8#include <openssl/hmac.h>
9#include <openssl/sha.h>
10
11#include <string.h>
12#include "fido.h"
13
14static int
15check_key_type(cbor_item_t *item)
16{
17 if (item->type == CBOR_TYPE_UINT || item->type == CBOR_TYPE_NEGINT ||
18 item->type == CBOR_TYPE_STRING)
19 return (0);
20
21 fido_log_debug("%s: invalid type: %d", __func__, item->type);
22
23 return (-1);
24}
25
26/*
27 * Validate CTAP2 canonical CBOR encoding rules for maps.
28 */
29static int
30ctap_check_cbor(cbor_item_t *prev, cbor_item_t *curr)
31{
32 size_t curr_len;
33 size_t prev_len;
34
35 if (check_key_type(prev) < 0 || check_key_type(curr) < 0)
36 return (-1);
37
38 if (prev->type != curr->type) {
39 if (prev->type < curr->type)
40 return (0);
41 fido_log_debug("%s: unsorted types", __func__);
42 return (-1);
43 }
44
45 if (curr->type == CBOR_TYPE_UINT || curr->type == CBOR_TYPE_NEGINT) {
46 if (cbor_int_get_width(curr) >= cbor_int_get_width(prev) &&
47 cbor_get_int(curr) > cbor_get_int(prev))
48 return (0);
49 } else {
50 curr_len = cbor_string_length(curr);
51 prev_len = cbor_string_length(prev);
52
53 if (curr_len > prev_len || (curr_len == prev_len &&
54 memcmp(cbor_string_handle(prev), cbor_string_handle(curr),
55 curr_len) < 0))
56 return (0);
57 }
58
59 fido_log_debug("%s: invalid cbor", __func__);
60
61 return (-1);
62}
63
64int
65cbor_map_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *,
66 const cbor_item_t *, void *))
67{
68 struct cbor_pair *v;
69 size_t n;
70
71 if ((v = cbor_map_handle(item)) == NULL) {
72 fido_log_debug("%s: cbor_map_handle", __func__);
73 return (-1);
74 }
75
76 n = cbor_map_size(item);
77
78 for (size_t i = 0; i < n; i++) {
79 if (v[i].key == NULL || v[i].value == NULL) {
80 fido_log_debug("%s: key=%p, value=%p for i=%zu",
81 __func__, (void *)v[i].key, (void *)v[i].value, i);
82 return (-1);
83 }
84 if (i && ctap_check_cbor(v[i - 1].key, v[i].key) < 0) {
85 fido_log_debug("%s: ctap_check_cbor", __func__);
86 return (-1);
87 }
88 if (f(v[i].key, v[i].value, arg) < 0) {
89 fido_log_debug("%s: iterator < 0 on i=%zu", __func__,
90 i);
91 return (-1);
92 }
93 }
94
95 return (0);
96}
97
98int
99cbor_array_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *,
100 void *))
101{
102 cbor_item_t **v;
103 size_t n;
104
105 if ((v = cbor_array_handle(item)) == NULL) {
106 fido_log_debug("%s: cbor_array_handle", __func__);
107 return (-1);
108 }
109
110 n = cbor_array_size(item);
111
112 for (size_t i = 0; i < n; i++)
113 if (v[i] == NULL || f(v[i], arg) < 0) {
114 fido_log_debug("%s: iterator < 0 on i=%zu,%p",
115 __func__, i, (void *)v[i]);
116 return (-1);
117 }
118
119 return (0);
120}
121
122int
123cbor_parse_reply(const unsigned char *blob, size_t blob_len, void *arg,
124 int(*parser)(const cbor_item_t *, const cbor_item_t *, void *))
125{
126 cbor_item_t *item = NULL;
127 struct cbor_load_result cbor;
128 int r;
129
130 if (blob_len < 1) {
131 fido_log_debug("%s: blob_len=%zu", __func__, blob_len);
132 r = FIDO_ERR_RX;
133 goto fail;
134 }
135
136 if (blob[0] != FIDO_OK) {
137 fido_log_debug("%s: blob[0]=0x%02x", __func__, blob[0]);
138 r = blob[0];
139 goto fail;
140 }
141
142 if ((item = cbor_load(blob + 1, blob_len - 1, &cbor)) == NULL) {
143 fido_log_debug("%s: cbor_load", __func__);
144 r = FIDO_ERR_RX_NOT_CBOR;
145 goto fail;
146 }
147
148 if (cbor_isa_map(item) == false ||
149 cbor_map_is_definite(item) == false) {
150 fido_log_debug("%s: cbor type", __func__);
151 r = FIDO_ERR_RX_INVALID_CBOR;
152 goto fail;
153 }
154
155 if (cbor_map_iter(item, arg, parser) < 0) {
156 fido_log_debug("%s: cbor_map_iter", __func__);
157 r = FIDO_ERR_RX_INVALID_CBOR;
158 goto fail;
159 }
160
161 r = FIDO_OK;
162fail:
163 if (item != NULL)
164 cbor_decref(&item);
165
166 return (r);
167}
168
169void
170cbor_vector_free(cbor_item_t **item, size_t len)
171{
172 for (size_t i = 0; i < len; i++)
173 if (item[i] != NULL)
174 cbor_decref(&item[i]);
175}
176
177int
178cbor_bytestring_copy(const cbor_item_t *item, unsigned char **buf, size_t *len)
179{
180 if (*buf != NULL || *len != 0) {
181 fido_log_debug("%s: dup", __func__);
182 return (-1);
183 }
184
185 if (cbor_isa_bytestring(item) == false ||
186 cbor_bytestring_is_definite(item) == false) {
187 fido_log_debug("%s: cbor type", __func__);
188 return (-1);
189 }
190
191 *len = cbor_bytestring_length(item);
192 if ((*buf = malloc(*len)) == NULL) {
193 *len = 0;
194 return (-1);
195 }
196
197 memcpy(*buf, cbor_bytestring_handle(item), *len);
198
199 return (0);
200}
201
202int
203cbor_string_copy(const cbor_item_t *item, char **str)
204{
205 size_t len;
206
207 if (*str != NULL) {
208 fido_log_debug("%s: dup", __func__);
209 return (-1);
210 }
211
212 if (cbor_isa_string(item) == false ||
213 cbor_string_is_definite(item) == false) {
214 fido_log_debug("%s: cbor type", __func__);
215 return (-1);
216 }
217
218 if ((len = cbor_string_length(item)) == SIZE_MAX ||
219 (*str = malloc(len + 1)) == NULL)
220 return (-1);
221
222 memcpy(*str, cbor_string_handle(item), len);
223 (*str)[len] = '\0';
224
225 return (0);
226}
227
228int
229cbor_add_bytestring(cbor_item_t *item, const char *key,
230 const unsigned char *value, size_t value_len)
231{
232 struct cbor_pair pair;
233 int ok = -1;
234
235 memset(&pair, 0, sizeof(pair));
236
237 if ((pair.key = cbor_build_string(key)) == NULL ||
238 (pair.value = cbor_build_bytestring(value, value_len)) == NULL) {
239 fido_log_debug("%s: cbor_build", __func__);
240 goto fail;
241 }
242
243 if (!cbor_map_add(item, pair)) {
244 fido_log_debug("%s: cbor_map_add", __func__);
245 goto fail;
246 }
247
248 ok = 0;
249fail:
250 if (pair.key)
251 cbor_decref(&pair.key);
252 if (pair.value)
253 cbor_decref(&pair.value);
254
255 return (ok);
256}
257
258int
259cbor_add_string(cbor_item_t *item, const char *key, const char *value)
260{
261 struct cbor_pair pair;
262 int ok = -1;
263
264 memset(&pair, 0, sizeof(pair));
265
266 if ((pair.key = cbor_build_string(key)) == NULL ||
267 (pair.value = cbor_build_string(value)) == NULL) {
268 fido_log_debug("%s: cbor_build", __func__);
269 goto fail;
270 }
271
272 if (!cbor_map_add(item, pair)) {
273 fido_log_debug("%s: cbor_map_add", __func__);
274 goto fail;
275 }
276
277 ok = 0;
278fail:
279 if (pair.key)
280 cbor_decref(&pair.key);
281 if (pair.value)
282 cbor_decref(&pair.value);
283
284 return (ok);
285}
286
287int
288cbor_add_bool(cbor_item_t *item, const char *key, fido_opt_t value)
289{
290 struct cbor_pair pair;
291 int ok = -1;
292
293 memset(&pair, 0, sizeof(pair));
294
295 if ((pair.key = cbor_build_string(key)) == NULL ||
296 (pair.value = cbor_build_bool(value == FIDO_OPT_TRUE)) == NULL) {
297 fido_log_debug("%s: cbor_build", __func__);
298 goto fail;
299 }
300
301 if (!cbor_map_add(item, pair)) {
302 fido_log_debug("%s: cbor_map_add", __func__);
303 goto fail;
304 }
305
306 ok = 0;
307fail:
308 if (pair.key)
309 cbor_decref(&pair.key);
310 if (pair.value)
311 cbor_decref(&pair.value);
312
313 return (ok);
314}
315
316static int
317cbor_add_arg(cbor_item_t *item, uint8_t n, cbor_item_t *arg)
318{
319 struct cbor_pair pair;
320 int ok = -1;
321
322 memset(&pair, 0, sizeof(pair));
323
324 if (arg == NULL)
325 return (0); /* empty argument */
326
327 if ((pair.key = cbor_build_uint8(n)) == NULL) {
328 fido_log_debug("%s: cbor_build", __func__);
329 goto fail;
330 }
331
332 pair.value = arg;
333
334 if (!cbor_map_add(item, pair)) {
335 fido_log_debug("%s: cbor_map_add", __func__);
336 goto fail;
337 }
338
339 ok = 0;
340fail:
341 if (pair.key)
342 cbor_decref(&pair.key);
343
344 return (ok);
345}
346
347cbor_item_t *
348cbor_flatten_vector(cbor_item_t *argv[], size_t argc)
349{
350 cbor_item_t *map;
351 uint8_t i;
352
353 if (argc > UINT8_MAX - 1)
354 return (NULL);
355
356 if ((map = cbor_new_definite_map(argc)) == NULL)
357 return (NULL);
358
359 for (i = 0; i < argc; i++)
360 if (cbor_add_arg(map, i + 1, argv[i]) < 0)
361 break;
362
363 if (i != argc) {
364 cbor_decref(&map);
365 map = NULL;
366 }
367
368 return (map);
369}
370
371int
372cbor_build_frame(uint8_t cmd, cbor_item_t *argv[], size_t argc, fido_blob_t *f)
373{
374 cbor_item_t *flat = NULL;
375 unsigned char *cbor = NULL;
376 size_t cbor_len;
377 size_t cbor_alloc_len;
378 int ok = -1;
379
380 if ((flat = cbor_flatten_vector(argv, argc)) == NULL)
381 goto fail;
382
383 cbor_len = cbor_serialize_alloc(flat, &cbor, &cbor_alloc_len);
384 if (cbor_len == 0 || cbor_len == SIZE_MAX) {
385 fido_log_debug("%s: cbor_len=%zu", __func__, cbor_len);
386 goto fail;
387 }
388
389 if ((f->ptr = malloc(cbor_len + 1)) == NULL)
390 goto fail;
391
392 f->len = cbor_len + 1;
393 f->ptr[0] = cmd;
394 memcpy(f->ptr + 1, cbor, f->len - 1);
395
396 ok = 0;
397fail:
398 if (flat != NULL)
399 cbor_decref(&flat);
400
401 free(cbor);
402
403 return (ok);
404}
405
406cbor_item_t *
407cbor_encode_rp_entity(const fido_rp_t *rp)
408{
409 cbor_item_t *item = NULL;
410
411 if ((item = cbor_new_definite_map(2)) == NULL)
412 return (NULL);
413
414 if ((rp->id && cbor_add_string(item, "id", rp->id) < 0) ||
415 (rp->name && cbor_add_string(item, "name", rp->name) < 0)) {
416 cbor_decref(&item);
417 return (NULL);
418 }
419
420 return (item);
421}
422
423cbor_item_t *
424cbor_encode_user_entity(const fido_user_t *user)
425{
426 cbor_item_t *item = NULL;
427 const fido_blob_t *id = &user->id;
428 const char *display = user->display_name;
429
430 if ((item = cbor_new_definite_map(4)) == NULL)
431 return (NULL);
432
433 if ((id->ptr && cbor_add_bytestring(item, "id", id->ptr, id->len) < 0) ||
434 (user->icon && cbor_add_string(item, "icon", user->icon) < 0) ||
435 (user->name && cbor_add_string(item, "name", user->name) < 0) ||
436 (display && cbor_add_string(item, "displayName", display) < 0)) {
437 cbor_decref(&item);
438 return (NULL);
439 }
440
441 return (item);
442}
443
444cbor_item_t *
445cbor_encode_pubkey_param(int cose_alg)
446{
447 cbor_item_t *item = NULL;
448 cbor_item_t *body = NULL;
449 struct cbor_pair alg;
450 int ok = -1;
451
452 memset(&alg, 0, sizeof(alg));
453
454 if ((item = cbor_new_definite_array(1)) == NULL ||
455 (body = cbor_new_definite_map(2)) == NULL ||
456 cose_alg > -1 || cose_alg < INT16_MIN)
457 goto fail;
458
459 alg.key = cbor_build_string("alg");
460
461 if (-cose_alg - 1 > UINT8_MAX)
462 alg.value = cbor_build_negint16((uint16_t)(-cose_alg - 1));
463 else
464 alg.value = cbor_build_negint8((uint8_t)(-cose_alg - 1));
465
466 if (alg.key == NULL || alg.value == NULL) {
467 fido_log_debug("%s: cbor_build", __func__);
468 goto fail;
469 }
470
471 if (cbor_map_add(body, alg) == false ||
472 cbor_add_string(body, "type", "public-key") < 0 ||
473 cbor_array_push(item, body) == false)
474 goto fail;
475
476 ok = 0;
477fail:
478 if (ok < 0) {
479 if (item != NULL) {
480 cbor_decref(&item);
481 item = NULL;
482 }
483 }
484
485 if (body != NULL)
486 cbor_decref(&body);
487 if (alg.key != NULL)
488 cbor_decref(&alg.key);
489 if (alg.value != NULL)
490 cbor_decref(&alg.value);
491
492 return (item);
493}
494
495cbor_item_t *
496cbor_encode_pubkey(const fido_blob_t *pubkey)
497{
498 cbor_item_t *cbor_key = NULL;
499
500 if ((cbor_key = cbor_new_definite_map(2)) == NULL ||
501 cbor_add_bytestring(cbor_key, "id", pubkey->ptr, pubkey->len) < 0 ||
502 cbor_add_string(cbor_key, "type", "public-key") < 0) {
503 if (cbor_key)
504 cbor_decref(&cbor_key);
505 return (NULL);
506 }
507
508 return (cbor_key);
509}
510
511cbor_item_t *
512cbor_encode_pubkey_list(const fido_blob_array_t *list)
513{
514 cbor_item_t *array = NULL;
515 cbor_item_t *key = NULL;
516
517 if ((array = cbor_new_definite_array(list->len)) == NULL)
518 goto fail;
519
520 for (size_t i = 0; i < list->len; i++) {
521 if ((key = cbor_encode_pubkey(&list->ptr[i])) == NULL ||
522 cbor_array_push(array, key) == false)
523 goto fail;
524 cbor_decref(&key);
525 }
526
527 return (array);
528fail:
529 if (key != NULL)
530 cbor_decref(&key);
531 if (array != NULL)
532 cbor_decref(&array);
533
534 return (NULL);
535}
536
537cbor_item_t *
538cbor_encode_extensions(int ext)
539{
540 cbor_item_t *item = NULL;
541
542 if (ext == 0 || ext != FIDO_EXT_HMAC_SECRET)
543 return (NULL);
544
545 if ((item = cbor_new_definite_map(1)) == NULL)
546 return (NULL);
547
548 if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) {
549 cbor_decref(&item);
550 return (NULL);
551 }
552
553 return (item);
554}
555
556cbor_item_t *
557cbor_encode_options(fido_opt_t rk, fido_opt_t uv)
558{
559 cbor_item_t *item = NULL;
560
561 if ((item = cbor_new_definite_map(2)) == NULL)
562 return (NULL);
563
564 if ((rk != FIDO_OPT_OMIT && cbor_add_bool(item, "rk", rk) < 0) ||
565 (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) {
566 cbor_decref(&item);
567 return (NULL);
568 }
569
570 return (item);
571}
572
573cbor_item_t *
574cbor_encode_assert_options(fido_opt_t up, fido_opt_t uv)
575{
576 cbor_item_t *item = NULL;
577
578 if ((item = cbor_new_definite_map(2)) == NULL)
579 return (NULL);
580
581 if ((up != FIDO_OPT_OMIT && cbor_add_bool(item, "up", up) < 0) ||
582 (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) {
583 cbor_decref(&item);
584 return (NULL);
585 }
586
587 return (item);
588}
589
590cbor_item_t *
591cbor_encode_pin_auth(const fido_blob_t *hmac_key, const fido_blob_t *data)
592{
593 const EVP_MD *md = NULL;
594 unsigned char dgst[SHA256_DIGEST_LENGTH];
595 unsigned int dgst_len;
596
597 if ((md = EVP_sha256()) == NULL || HMAC(md, hmac_key->ptr,
598 (int)hmac_key->len, data->ptr, (int)data->len, dgst,
599 &dgst_len) == NULL || dgst_len != SHA256_DIGEST_LENGTH)
600 return (NULL);
601
602 return (cbor_build_bytestring(dgst, 16));
603}
604
605cbor_item_t *
606cbor_encode_pin_opt(void)
607{
608 return (cbor_build_uint8(1));
609}
610
611cbor_item_t *
612cbor_encode_pin_enc(const fido_blob_t *key, const fido_blob_t *pin)
613{
614 fido_blob_t pe;
615 cbor_item_t *item = NULL;
616
617 if (aes256_cbc_enc(key, pin, &pe) < 0)
618 return (NULL);
619
620 item = cbor_build_bytestring(pe.ptr, pe.len);
621 free(pe.ptr);
622
623 return (item);
624}
625
626static int
627sha256(const unsigned char *data, size_t data_len, fido_blob_t *digest)
628{
629 if ((digest->ptr = calloc(1, SHA256_DIGEST_LENGTH)) == NULL)
630 return (-1);
631
632 digest->len = SHA256_DIGEST_LENGTH;
633
634 if (SHA256(data, data_len, digest->ptr) != digest->ptr) {
635 free(digest->ptr);
636 digest->ptr = NULL;
637 digest->len = 0;
638 return (-1);
639 }
640
641 return (0);
642}
643
644cbor_item_t *
645cbor_encode_change_pin_auth(const fido_blob_t *key, const fido_blob_t *new_pin,
646 const fido_blob_t *pin)
647{
648 unsigned char dgst[SHA256_DIGEST_LENGTH];
649 unsigned int dgst_len;
650 cbor_item_t *item = NULL;
651 const EVP_MD *md = NULL;
652#if OPENSSL_VERSION_NUMBER < 0x10100000L
653 HMAC_CTX ctx;
654#else
655 HMAC_CTX *ctx = NULL;
656#endif
657 fido_blob_t *npe = NULL; /* new pin, encrypted */
658 fido_blob_t *ph = NULL; /* pin hash */
659 fido_blob_t *phe = NULL; /* pin hash, encrypted */
660 int ok = -1;
661
662 if ((npe = fido_blob_new()) == NULL ||
663 (ph = fido_blob_new()) == NULL ||
664 (phe = fido_blob_new()) == NULL)
665 goto fail;
666
667 if (aes256_cbc_enc(key, new_pin, npe) < 0) {
668 fido_log_debug("%s: aes256_cbc_enc 1", __func__);
669 goto fail;
670 }
671
672 if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) {
673 fido_log_debug("%s: sha256", __func__);
674 goto fail;
675 }
676
677 ph->len = 16; /* first 16 bytes */
678
679 if (aes256_cbc_enc(key, ph, phe) < 0) {
680 fido_log_debug("%s: aes256_cbc_enc 2", __func__);
681 goto fail;
682 }
683
684#if OPENSSL_VERSION_NUMBER < 0x10100000L
685 HMAC_CTX_init(&ctx);
686
687 if ((md = EVP_sha256()) == NULL ||
688 HMAC_Init_ex(&ctx, key->ptr, (int)key->len, md, NULL) == 0 ||
689 HMAC_Update(&ctx, npe->ptr, (int)npe->len) == 0 ||
690 HMAC_Update(&ctx, phe->ptr, (int)phe->len) == 0 ||
691 HMAC_Final(&ctx, dgst, &dgst_len) == 0 || dgst_len != 32) {
692 fido_log_debug("%s: HMAC", __func__);
693 goto fail;
694 }
695#else
696 if ((ctx = HMAC_CTX_new()) == NULL ||
697 (md = EVP_sha256()) == NULL ||
698 HMAC_Init_ex(ctx, key->ptr, (int)key->len, md, NULL) == 0 ||
699 HMAC_Update(ctx, npe->ptr, (int)npe->len) == 0 ||
700 HMAC_Update(ctx, phe->ptr, (int)phe->len) == 0 ||
701 HMAC_Final(ctx, dgst, &dgst_len) == 0 || dgst_len != 32) {
702 fido_log_debug("%s: HMAC", __func__);
703 goto fail;
704 }
705#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
706
707 if ((item = cbor_build_bytestring(dgst, 16)) == NULL) {
708 fido_log_debug("%s: cbor_build_bytestring", __func__);
709 goto fail;
710 }
711
712 ok = 0;
713fail:
714 fido_blob_free(&npe);
715 fido_blob_free(&ph);
716 fido_blob_free(&phe);
717
718#if OPENSSL_VERSION_NUMBER >= 0x10100000L
719 if (ctx != NULL)
720 HMAC_CTX_free(ctx);
721#endif
722
723 if (ok < 0) {
724 if (item != NULL) {
725 cbor_decref(&item);
726 item = NULL;
727 }
728 }
729
730 return (item);
731}
732
733cbor_item_t *
734cbor_encode_set_pin_auth(const fido_blob_t *key, const fido_blob_t *pin)
735{
736 const EVP_MD *md = NULL;
737 unsigned char dgst[SHA256_DIGEST_LENGTH];
738 unsigned int dgst_len;
739 cbor_item_t *item = NULL;
740 fido_blob_t *pe = NULL;
741
742 if ((pe = fido_blob_new()) == NULL)
743 goto fail;
744
745 if (aes256_cbc_enc(key, pin, pe) < 0) {
746 fido_log_debug("%s: aes256_cbc_enc", __func__);
747 goto fail;
748 }
749
750 if ((md = EVP_sha256()) == NULL || key->len != 32 || HMAC(md, key->ptr,
751 (int)key->len, pe->ptr, (int)pe->len, dgst, &dgst_len) == NULL ||
752 dgst_len != SHA256_DIGEST_LENGTH) {
753 fido_log_debug("%s: HMAC", __func__);
754 goto fail;
755 }
756
757 item = cbor_build_bytestring(dgst, 16);
758fail:
759 fido_blob_free(&pe);
760
761 return (item);
762}
763
764cbor_item_t *
765cbor_encode_pin_hash_enc(const fido_blob_t *shared, const fido_blob_t *pin)
766{
767 cbor_item_t *item = NULL;
768 fido_blob_t *ph = NULL;
769 fido_blob_t *phe = NULL;
770
771 if ((ph = fido_blob_new()) == NULL || (phe = fido_blob_new()) == NULL)
772 goto fail;
773
774 if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) {
775 fido_log_debug("%s: SHA256", __func__);
776 goto fail;
777 }
778
779 ph->len = 16; /* first 16 bytes */
780
781 if (aes256_cbc_enc(shared, ph, phe) < 0) {
782 fido_log_debug("%s: aes256_cbc_enc", __func__);
783 goto fail;
784 }
785
786 item = cbor_build_bytestring(phe->ptr, phe->len);
787fail:
788 fido_blob_free(&ph);
789 fido_blob_free(&phe);
790
791 return (item);
792}
793
794cbor_item_t *
795cbor_encode_hmac_secret_param(const fido_blob_t *ecdh, const es256_pk_t *pk,
796 const fido_blob_t *hmac_salt)
797{
798 cbor_item_t *item = NULL;
799 cbor_item_t *param = NULL;
800 cbor_item_t *argv[3];
801 struct cbor_pair pair;
802
803 memset(argv, 0, sizeof(argv));
804 memset(&pair, 0, sizeof(pair));
805
806 if (ecdh == NULL || pk == NULL || hmac_salt->ptr == NULL) {
807 fido_log_debug("%s: ecdh=%p, pk=%p, hmac_salt->ptr=%p",
808 __func__, (const void *)ecdh, (const void *)pk,
809 (const void *)hmac_salt->ptr);
810 goto fail;
811 }
812
813 if (hmac_salt->len != 32 && hmac_salt->len != 64) {
814 fido_log_debug("%s: hmac_salt->len=%zu", __func__,
815 hmac_salt->len);
816 goto fail;
817 }
818
819 /* XXX not pin, but salt */
820 if ((argv[0] = es256_pk_encode(pk, 1)) == NULL ||
821 (argv[1] = cbor_encode_pin_enc(ecdh, hmac_salt)) == NULL ||
822 (argv[2] = cbor_encode_set_pin_auth(ecdh, hmac_salt)) == NULL) {
823 fido_log_debug("%s: cbor encode", __func__);
824 goto fail;
825 }
826
827 if ((param = cbor_flatten_vector(argv, 3)) == NULL) {
828 fido_log_debug("%s: cbor_flatten_vector", __func__);
829 goto fail;
830 }
831
832 if ((item = cbor_new_definite_map(1)) == NULL) {
833 fido_log_debug("%s: cbor_new_definite_map", __func__);
834 goto fail;
835 }
836
837 if ((pair.key = cbor_build_string("hmac-secret")) == NULL) {
838 fido_log_debug("%s: cbor_build", __func__);
839 goto fail;
840 }
841
842 pair.value = param;
843
844 if (!cbor_map_add(item, pair)) {
845 fido_log_debug("%s: cbor_map_add", __func__);
846 cbor_decref(&item);
847 item = NULL;
848 goto fail;
849 }
850
851fail:
852 for (size_t i = 0; i < 3; i++)
853 if (argv[i] != NULL)
854 cbor_decref(&argv[i]);
855
856 if (param != NULL)
857 cbor_decref(&param);
858 if (pair.key != NULL)
859 cbor_decref(&pair.key);
860
861 return (item);
862}
863
864int
865cbor_decode_fmt(const cbor_item_t *item, char **fmt)
866{
867 char *type = NULL;
868
869 if (cbor_string_copy(item, &type) < 0) {
870 fido_log_debug("%s: cbor_string_copy", __func__);
871 return (-1);
872 }
873
874 if (strcmp(type, "packed") && strcmp(type, "fido-u2f")) {
875 fido_log_debug("%s: type=%s", __func__, type);
876 free(type);
877 return (-1);
878 }
879
880 *fmt = type;
881
882 return (0);
883}
884
885struct cose_key {
886 int kty;
887 int alg;
888 int crv;
889};
890
891static int
892find_cose_alg(const cbor_item_t *key, const cbor_item_t *val, void *arg)
893{
894 struct cose_key *cose_key = arg;
895
896 if (cbor_isa_uint(key) == true &&
897 cbor_int_get_width(key) == CBOR_INT_8) {
898 switch (cbor_get_uint8(key)) {
899 case 1:
900 if (cbor_isa_uint(val) == false ||
901 cbor_get_int(val) > INT_MAX || cose_key->kty != 0) {
902 fido_log_debug("%s: kty", __func__);
903 return (-1);
904 }
905
906 cose_key->kty = (int)cbor_get_int(val);
907
908 break;
909 case 3:
910 if (cbor_isa_negint(val) == false ||
911 cbor_get_int(val) > INT_MAX || cose_key->alg != 0) {
912 fido_log_debug("%s: alg", __func__);
913 return (-1);
914 }
915
916 cose_key->alg = -(int)cbor_get_int(val) - 1;
917
918 break;
919 }
920 } else if (cbor_isa_negint(key) == true &&
921 cbor_int_get_width(key) == CBOR_INT_8) {
922 if (cbor_get_uint8(key) == 0) {
923 /* get crv if not rsa, otherwise ignore */
924 if (cbor_isa_uint(val) == true &&
925 cbor_get_int(val) <= INT_MAX &&
926 cose_key->crv == 0)
927 cose_key->crv = (int)cbor_get_int(val);
928 }
929 }
930
931 return (0);
932}
933
934static int
935get_cose_alg(const cbor_item_t *item, int *cose_alg)
936{
937 struct cose_key cose_key;
938
939 memset(&cose_key, 0, sizeof(cose_key));
940
941 *cose_alg = 0;
942
943 if (cbor_isa_map(item) == false ||
944 cbor_map_is_definite(item) == false ||
945 cbor_map_iter(item, &cose_key, find_cose_alg) < 0) {
946 fido_log_debug("%s: cbor type", __func__);
947 return (-1);
948 }
949
950 switch (cose_key.alg) {
951 case COSE_ES256:
952 if (cose_key.kty != COSE_KTY_EC2 ||
953 cose_key.crv != COSE_P256) {
954 fido_log_debug("%s: invalid kty/crv", __func__);
955 return (-1);
956 }
957
958 break;
959 case COSE_EDDSA:
960 if (cose_key.kty != COSE_KTY_OKP ||
961 cose_key.crv != COSE_ED25519) {
962 fido_log_debug("%s: invalid kty/crv", __func__);
963 return (-1);
964 }
965
966 break;
967 case COSE_RS256:
968 if (cose_key.kty != COSE_KTY_RSA) {
969 fido_log_debug("%s: invalid kty/crv", __func__);
970 return (-1);
971 }
972
973 break;
974 default:
975 fido_log_debug("%s: unknown alg %d", __func__, cose_key.alg);
976
977 return (-1);
978 }
979
980 *cose_alg = cose_key.alg;
981
982 return (0);
983}
984
985int
986cbor_decode_pubkey(const cbor_item_t *item, int *type, void *key)
987{
988 if (get_cose_alg(item, type) < 0) {
989 fido_log_debug("%s: get_cose_alg", __func__);
990 return (-1);
991 }
992
993 switch (*type) {
994 case COSE_ES256:
995 if (es256_pk_decode(item, key) < 0) {
996 fido_log_debug("%s: es256_pk_decode", __func__);
997 return (-1);
998 }
999 break;
1000 case COSE_RS256:
1001 if (rs256_pk_decode(item, key) < 0) {
1002 fido_log_debug("%s: rs256_pk_decode", __func__);
1003 return (-1);
1004 }
1005 break;
1006 case COSE_EDDSA:
1007 if (eddsa_pk_decode(item, key) < 0) {
1008 fido_log_debug("%s: eddsa_pk_decode", __func__);
1009 return (-1);
1010 }
1011 break;
1012 default:
1013 fido_log_debug("%s: invalid cose_alg %d", __func__, *type);
1014 return (-1);
1015 }
1016
1017 return (0);
1018}
1019
1020static int
1021decode_attcred(const unsigned char **buf, size_t *len, int cose_alg,
1022 fido_attcred_t *attcred)
1023{
1024 cbor_item_t *item = NULL;
1025 struct cbor_load_result cbor;
1026 uint16_t id_len;
1027 int ok = -1;
1028
1029 fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf,
1030 *len);
1031
1032 if (fido_buf_read(buf, len, &attcred->aaguid,
1033 sizeof(attcred->aaguid)) < 0) {
1034 fido_log_debug("%s: fido_buf_read aaguid", __func__);
1035 return (-1);
1036 }
1037
1038 if (fido_buf_read(buf, len, &id_len, sizeof(id_len)) < 0) {
1039 fido_log_debug("%s: fido_buf_read id_len", __func__);
1040 return (-1);
1041 }
1042
1043 attcred->id.len = (size_t)be16toh(id_len);
1044 if ((attcred->id.ptr = malloc(attcred->id.len)) == NULL)
1045 return (-1);
1046
1047 fido_log_debug("%s: attcred->id.len=%zu", __func__, attcred->id.len);
1048
1049 if (fido_buf_read(buf, len, attcred->id.ptr, attcred->id.len) < 0) {
1050 fido_log_debug("%s: fido_buf_read id", __func__);
1051 return (-1);
1052 }
1053
1054 if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
1055 fido_log_debug("%s: cbor_load", __func__);
1056 fido_log_xxd(*buf, *len);
1057 goto fail;
1058 }
1059
1060 if (cbor_decode_pubkey(item, &attcred->type, &attcred->pubkey) < 0) {
1061 fido_log_debug("%s: cbor_decode_pubkey", __func__);
1062 goto fail;
1063 }
1064
1065 if (attcred->type != cose_alg) {
1066 fido_log_debug("%s: cose_alg mismatch (%d != %d)", __func__,
1067 attcred->type, cose_alg);
1068 goto fail;
1069 }
1070
1071 *buf += cbor.read;
1072 *len -= cbor.read;
1073
1074 ok = 0;
1075fail:
1076 if (item != NULL)
1077 cbor_decref(&item);
1078
1079 return (ok);
1080}
1081
1082static int
1083decode_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1084{
1085 int *authdata_ext = arg;
1086 char *type = NULL;
1087 int ok = -1;
1088
1089 if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) {
1090 fido_log_debug("%s: cbor type", __func__);
1091 ok = 0; /* ignore */
1092 goto out;
1093 }
1094
1095 if (cbor_isa_float_ctrl(val) == false ||
1096 cbor_float_get_width(val) != CBOR_FLOAT_0 ||
1097 cbor_is_bool(val) == false || *authdata_ext != 0) {
1098 fido_log_debug("%s: cbor type", __func__);
1099 goto out;
1100 }
1101
1102 if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE)
1103 *authdata_ext |= FIDO_EXT_HMAC_SECRET;
1104
1105 ok = 0;
1106out:
1107 free(type);
1108
1109 return (ok);
1110}
1111
1112static int
1113decode_extensions(const unsigned char **buf, size_t *len, int *authdata_ext)
1114{
1115 cbor_item_t *item = NULL;
1116 struct cbor_load_result cbor;
1117 int ok = -1;
1118
1119 fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf,
1120 *len);
1121
1122 *authdata_ext = 0;
1123
1124 if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
1125 fido_log_debug("%s: cbor_load", __func__);
1126 fido_log_xxd(*buf, *len);
1127 goto fail;
1128 }
1129
1130 if (cbor_isa_map(item) == false ||
1131 cbor_map_is_definite(item) == false ||
1132 cbor_map_size(item) != 1 ||
1133 cbor_map_iter(item, authdata_ext, decode_extension) < 0) {
1134 fido_log_debug("%s: cbor type", __func__);
1135 goto fail;
1136 }
1137
1138 *buf += cbor.read;
1139 *len -= cbor.read;
1140
1141 ok = 0;
1142fail:
1143 if (item != NULL)
1144 cbor_decref(&item);
1145
1146 return (ok);
1147}
1148
1149static int
1150decode_hmac_secret_aux(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1151{
1152 fido_blob_t *out = arg;
1153 char *type = NULL;
1154 int ok = -1;
1155
1156 if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) {
1157 fido_log_debug("%s: cbor type", __func__);
1158 ok = 0; /* ignore */
1159 goto out;
1160 }
1161
1162 ok = cbor_bytestring_copy(val, &out->ptr, &out->len);
1163out:
1164 free(type);
1165
1166 return (ok);
1167}
1168
1169static int
1170decode_hmac_secret(const unsigned char **buf, size_t *len, fido_blob_t *out)
1171{
1172 cbor_item_t *item = NULL;
1173 struct cbor_load_result cbor;
1174 int ok = -1;
1175
1176 fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf,
1177 *len);
1178
1179 if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
1180 fido_log_debug("%s: cbor_load", __func__);
1181 fido_log_xxd(*buf, *len);
1182 goto fail;
1183 }
1184
1185 if (cbor_isa_map(item) == false ||
1186 cbor_map_is_definite(item) == false ||
1187 cbor_map_size(item) != 1 ||
1188 cbor_map_iter(item, out, decode_hmac_secret_aux) < 0) {
1189 fido_log_debug("%s: cbor type", __func__);
1190 goto fail;
1191 }
1192
1193 *buf += cbor.read;
1194 *len -= cbor.read;
1195
1196 ok = 0;
1197fail:
1198 if (item != NULL)
1199 cbor_decref(&item);
1200
1201 return (ok);
1202}
1203
1204int
1205cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
1206 fido_blob_t *authdata_cbor, fido_authdata_t *authdata,
1207 fido_attcred_t *attcred, int *authdata_ext)
1208{
1209 const unsigned char *buf = NULL;
1210 size_t len;
1211 size_t alloc_len;
1212
1213 if (cbor_isa_bytestring(item) == false ||
1214 cbor_bytestring_is_definite(item) == false) {
1215 fido_log_debug("%s: cbor type", __func__);
1216 return (-1);
1217 }
1218
1219 if (authdata_cbor->ptr != NULL ||
1220 (authdata_cbor->len = cbor_serialize_alloc(item,
1221 &authdata_cbor->ptr, &alloc_len)) == 0) {
1222 fido_log_debug("%s: cbor_serialize_alloc", __func__);
1223 return (-1);
1224 }
1225
1226 buf = cbor_bytestring_handle(item);
1227 len = cbor_bytestring_length(item);
1228
1229 fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len);
1230
1231 if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
1232 fido_log_debug("%s: fido_buf_read", __func__);
1233 return (-1);
1234 }
1235
1236 authdata->sigcount = be32toh(authdata->sigcount);
1237
1238 if (attcred != NULL) {
1239 if ((authdata->flags & CTAP_AUTHDATA_ATT_CRED) == 0 ||
1240 decode_attcred(&buf, &len, cose_alg, attcred) < 0)
1241 return (-1);
1242 }
1243
1244 if (authdata_ext != NULL) {
1245 if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 &&
1246 decode_extensions(&buf, &len, authdata_ext) < 0)
1247 return (-1);
1248 }
1249
1250 /* XXX we should probably ensure that len == 0 at this point */
1251
1252 return (FIDO_OK);
1253}
1254
1255int
1256cbor_decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor,
1257 fido_authdata_t *authdata, int *authdata_ext, fido_blob_t *hmac_secret_enc)
1258{
1259 const unsigned char *buf = NULL;
1260 size_t len;
1261 size_t alloc_len;
1262
1263 if (cbor_isa_bytestring(item) == false ||
1264 cbor_bytestring_is_definite(item) == false) {
1265 fido_log_debug("%s: cbor type", __func__);
1266 return (-1);
1267 }
1268
1269 if (authdata_cbor->ptr != NULL ||
1270 (authdata_cbor->len = cbor_serialize_alloc(item,
1271 &authdata_cbor->ptr, &alloc_len)) == 0) {
1272 fido_log_debug("%s: cbor_serialize_alloc", __func__);
1273 return (-1);
1274 }
1275
1276 buf = cbor_bytestring_handle(item);
1277 len = cbor_bytestring_length(item);
1278
1279 fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len);
1280
1281 if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
1282 fido_log_debug("%s: fido_buf_read", __func__);
1283 return (-1);
1284 }
1285
1286 authdata->sigcount = be32toh(authdata->sigcount);
1287
1288 *authdata_ext = 0;
1289 if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0) {
1290 /* XXX semantic leap: extensions -> hmac_secret */
1291 if (decode_hmac_secret(&buf, &len, hmac_secret_enc) < 0) {
1292 fido_log_debug("%s: decode_hmac_secret", __func__);
1293 return (-1);
1294 }
1295 *authdata_ext = FIDO_EXT_HMAC_SECRET;
1296 }
1297
1298 /* XXX we should probably ensure that len == 0 at this point */
1299
1300 return (FIDO_OK);
1301}
1302
1303static int
1304decode_x5c(const cbor_item_t *item, void *arg)
1305{
1306 fido_blob_t *x5c = arg;
1307
1308 if (x5c->len)
1309 return (0); /* ignore */
1310
1311 return (cbor_bytestring_copy(item, &x5c->ptr, &x5c->len));
1312}
1313
1314static int
1315decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1316{
1317 fido_attstmt_t *attstmt = arg;
1318 char *name = NULL;
1319 int ok = -1;
1320
1321 if (cbor_string_copy(key, &name) < 0) {
1322 fido_log_debug("%s: cbor type", __func__);
1323 ok = 0; /* ignore */
1324 goto out;
1325 }
1326
1327 if (!strcmp(name, "alg")) {
1328 if (cbor_isa_negint(val) == false ||
1329 cbor_int_get_width(val) != CBOR_INT_8 ||
1330 cbor_get_uint8(val) != -COSE_ES256 - 1) {
1331 fido_log_debug("%s: alg", __func__);
1332 goto out;
1333 }
1334 } else if (!strcmp(name, "sig")) {
1335 if (cbor_bytestring_copy(val, &attstmt->sig.ptr,
1336 &attstmt->sig.len) < 0) {
1337 fido_log_debug("%s: sig", __func__);
1338 goto out;
1339 }
1340 } else if (!strcmp(name, "x5c")) {
1341 if (cbor_isa_array(val) == false ||
1342 cbor_array_is_definite(val) == false ||
1343 cbor_array_iter(val, &attstmt->x5c, decode_x5c) < 0) {
1344 fido_log_debug("%s: x5c", __func__);
1345 goto out;
1346 }
1347 }
1348
1349 ok = 0;
1350out:
1351 free(name);
1352
1353 return (ok);
1354}
1355
1356int
1357cbor_decode_attstmt(const cbor_item_t *item, fido_attstmt_t *attstmt)
1358{
1359 if (cbor_isa_map(item) == false ||
1360 cbor_map_is_definite(item) == false ||
1361 cbor_map_iter(item, attstmt, decode_attstmt_entry) < 0) {
1362 fido_log_debug("%s: cbor type", __func__);
1363 return (-1);
1364 }
1365
1366 return (0);
1367}
1368
1369int
1370cbor_decode_uint64(const cbor_item_t *item, uint64_t *n)
1371{
1372 if (cbor_isa_uint(item) == false) {
1373 fido_log_debug("%s: cbor type", __func__);
1374 return (-1);
1375 }
1376
1377 *n = cbor_get_int(item);
1378
1379 return (0);
1380}
1381
1382static int
1383decode_cred_id_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1384{
1385 fido_blob_t *id = arg;
1386 char *name = NULL;
1387 int ok = -1;
1388
1389 if (cbor_string_copy(key, &name) < 0) {
1390 fido_log_debug("%s: cbor type", __func__);
1391 ok = 0; /* ignore */
1392 goto out;
1393 }
1394
1395 if (!strcmp(name, "id"))
1396 if (cbor_bytestring_copy(val, &id->ptr, &id->len) < 0) {
1397 fido_log_debug("%s: cbor_bytestring_copy", __func__);
1398 goto out;
1399 }
1400
1401 ok = 0;
1402out:
1403 free(name);
1404
1405 return (ok);
1406}
1407
1408int
1409cbor_decode_cred_id(const cbor_item_t *item, fido_blob_t *id)
1410{
1411 if (cbor_isa_map(item) == false ||
1412 cbor_map_is_definite(item) == false ||
1413 cbor_map_iter(item, id, decode_cred_id_entry) < 0) {
1414 fido_log_debug("%s: cbor type", __func__);
1415 return (-1);
1416 }
1417
1418 return (0);
1419}
1420
1421static int
1422decode_user_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1423{
1424 fido_user_t *user = arg;
1425 char *name = NULL;
1426 int ok = -1;
1427
1428 if (cbor_string_copy(key, &name) < 0) {
1429 fido_log_debug("%s: cbor type", __func__);
1430 ok = 0; /* ignore */
1431 goto out;
1432 }
1433
1434 if (!strcmp(name, "icon")) {
1435 if (cbor_string_copy(val, &user->icon) < 0) {
1436 fido_log_debug("%s: icon", __func__);
1437 goto out;
1438 }
1439 } else if (!strcmp(name, "name")) {
1440 if (cbor_string_copy(val, &user->name) < 0) {
1441 fido_log_debug("%s: name", __func__);
1442 goto out;
1443 }
1444 } else if (!strcmp(name, "displayName")) {
1445 if (cbor_string_copy(val, &user->display_name) < 0) {
1446 fido_log_debug("%s: display_name", __func__);
1447 goto out;
1448 }
1449 } else if (!strcmp(name, "id")) {
1450 if (cbor_bytestring_copy(val, &user->id.ptr, &user->id.len) < 0) {
1451 fido_log_debug("%s: id", __func__);
1452 goto out;
1453 }
1454 }
1455
1456 ok = 0;
1457out:
1458 free(name);
1459
1460 return (ok);
1461}
1462
1463int
1464cbor_decode_user(const cbor_item_t *item, fido_user_t *user)
1465{
1466 if (cbor_isa_map(item) == false ||
1467 cbor_map_is_definite(item) == false ||
1468 cbor_map_iter(item, user, decode_user_entry) < 0) {
1469 fido_log_debug("%s: cbor type", __func__);
1470 return (-1);
1471 }
1472
1473 return (0);
1474}
1475
1476static int
1477decode_rp_entity_entry(const cbor_item_t *key, const cbor_item_t *val,
1478 void *arg)
1479{
1480 fido_rp_t *rp = arg;
1481 char *name = NULL;
1482 int ok = -1;
1483
1484 if (cbor_string_copy(key, &name) < 0) {
1485 fido_log_debug("%s: cbor type", __func__);
1486 ok = 0; /* ignore */
1487 goto out;
1488 }
1489
1490 if (!strcmp(name, "id")) {
1491 if (cbor_string_copy(val, &rp->id) < 0) {
1492 fido_log_debug("%s: id", __func__);
1493 goto out;
1494 }
1495 } else if (!strcmp(name, "name")) {
1496 if (cbor_string_copy(val, &rp->name) < 0) {
1497 fido_log_debug("%s: name", __func__);
1498 goto out;
1499 }
1500 }
1501
1502 ok = 0;
1503out:
1504 free(name);
1505
1506 return (ok);
1507}
1508
1509int
1510cbor_decode_rp_entity(const cbor_item_t *item, fido_rp_t *rp)
1511{
1512 if (cbor_isa_map(item) == false ||
1513 cbor_map_is_definite(item) == false ||
1514 cbor_map_iter(item, rp, decode_rp_entity_entry) < 0) {
1515 fido_log_debug("%s: cbor type", __func__);
1516 return (-1);
1517 }
1518
1519 return (0);
1520}
diff --git a/src/cred.c b/src/cred.c
new file mode 100644
index 0000000..c4e1edb
--- /dev/null
+++ b/src/cred.c
@@ -0,0 +1,1034 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/ec.h>
8#include <openssl/evp.h>
9#include <openssl/sha.h>
10#include <openssl/x509.h>
11
12#include <string.h>
13#include "fido.h"
14#include "fido/es256.h"
15
16static int
17parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
18{
19 fido_cred_t *cred = arg;
20
21 if (cbor_isa_uint(key) == false ||
22 cbor_int_get_width(key) != CBOR_INT_8) {
23 fido_log_debug("%s: cbor type", __func__);
24 return (0); /* ignore */
25 }
26
27 switch (cbor_get_uint8(key)) {
28 case 1: /* fmt */
29 return (cbor_decode_fmt(val, &cred->fmt));
30 case 2: /* authdata */
31 return (cbor_decode_cred_authdata(val, cred->type,
32 &cred->authdata_cbor, &cred->authdata, &cred->attcred,
33 &cred->authdata_ext));
34 case 3: /* attestation statement */
35 return (cbor_decode_attstmt(val, &cred->attstmt));
36 default: /* ignore */
37 fido_log_debug("%s: cbor type", __func__);
38 return (0);
39 }
40}
41
42static int
43fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
44{
45 fido_blob_t f;
46 fido_blob_t *ecdh = NULL;
47 es256_pk_t *pk = NULL;
48 cbor_item_t *argv[9];
49 int r;
50
51 memset(&f, 0, sizeof(f));
52 memset(argv, 0, sizeof(argv));
53
54 if (cred->cdh.ptr == NULL || cred->type == 0) {
55 fido_log_debug("%s: cdh=%p, type=%d", __func__,
56 (void *)cred->cdh.ptr, cred->type);
57 r = FIDO_ERR_INVALID_ARGUMENT;
58 goto fail;
59 }
60
61 if ((argv[0] = fido_blob_encode(&cred->cdh)) == NULL ||
62 (argv[1] = cbor_encode_rp_entity(&cred->rp)) == NULL ||
63 (argv[2] = cbor_encode_user_entity(&cred->user)) == NULL ||
64 (argv[3] = cbor_encode_pubkey_param(cred->type)) == NULL) {
65 fido_log_debug("%s: cbor encode", __func__);
66 r = FIDO_ERR_INTERNAL;
67 goto fail;
68 }
69
70 /* excluded credentials */
71 if (cred->excl.len)
72 if ((argv[4] = cbor_encode_pubkey_list(&cred->excl)) == NULL) {
73 fido_log_debug("%s: cbor_encode_pubkey_list", __func__);
74 r = FIDO_ERR_INTERNAL;
75 goto fail;
76 }
77
78 /* extensions */
79 if (cred->ext)
80 if ((argv[5] = cbor_encode_extensions(cred->ext)) == NULL) {
81 fido_log_debug("%s: cbor_encode_extensions", __func__);
82 r = FIDO_ERR_INTERNAL;
83 goto fail;
84 }
85
86 /* options */
87 if (cred->rk != FIDO_OPT_OMIT || cred->uv != FIDO_OPT_OMIT)
88 if ((argv[6] = cbor_encode_options(cred->rk,
89 cred->uv)) == NULL) {
90 fido_log_debug("%s: cbor_encode_options", __func__);
91 r = FIDO_ERR_INTERNAL;
92 goto fail;
93 }
94
95 /* pin authentication */
96 if (pin) {
97 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
98 fido_log_debug("%s: fido_do_ecdh", __func__);
99 goto fail;
100 }
101 if ((r = cbor_add_pin_params(dev, &cred->cdh, pk, ecdh, pin,
102 &argv[7], &argv[8])) != FIDO_OK) {
103 fido_log_debug("%s: cbor_add_pin_params", __func__);
104 goto fail;
105 }
106 }
107
108 /* framing and transmission */
109 if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, 9, &f) < 0 ||
110 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
111 fido_log_debug("%s: fido_tx", __func__);
112 r = FIDO_ERR_TX;
113 goto fail;
114 }
115
116 r = FIDO_OK;
117fail:
118 es256_pk_free(&pk);
119 fido_blob_free(&ecdh);
120 cbor_vector_free(argv, nitems(argv));
121 free(f.ptr);
122
123 return (r);
124}
125
126static int
127fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int ms)
128{
129 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
130 unsigned char reply[2048];
131 int reply_len;
132 int r;
133
134 fido_cred_reset_rx(cred);
135
136 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
137 fido_log_debug("%s: fido_rx", __func__);
138 return (FIDO_ERR_RX);
139 }
140
141 if ((r = cbor_parse_reply(reply, (size_t)reply_len, cred,
142 parse_makecred_reply)) != FIDO_OK) {
143 fido_log_debug("%s: parse_makecred_reply", __func__);
144 return (r);
145 }
146
147 if (cred->fmt == NULL || fido_blob_is_empty(&cred->authdata_cbor) ||
148 fido_blob_is_empty(&cred->attcred.id) ||
149 fido_blob_is_empty(&cred->attstmt.sig)) {
150 fido_cred_reset_rx(cred);
151 return (FIDO_ERR_INVALID_CBOR);
152 }
153
154 return (FIDO_OK);
155}
156
157static int
158fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin, int ms)
159{
160 int r;
161
162 if ((r = fido_dev_make_cred_tx(dev, cred, pin)) != FIDO_OK ||
163 (r = fido_dev_make_cred_rx(dev, cred, ms)) != FIDO_OK)
164 return (r);
165
166 return (FIDO_OK);
167}
168
169int
170fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
171{
172 if (fido_dev_is_fido2(dev) == false) {
173 if (pin != NULL || cred->rk == FIDO_OPT_TRUE || cred->ext != 0)
174 return (FIDO_ERR_UNSUPPORTED_OPTION);
175 return (u2f_register(dev, cred, -1));
176 }
177
178 return (fido_dev_make_cred_wait(dev, cred, pin, -1));
179}
180
181static int
182check_extensions(int authdata_ext, int ext)
183{
184 if (authdata_ext != ext) {
185 fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__,
186 authdata_ext, ext);
187 return (-1);
188 }
189
190 return (0);
191}
192
193int
194fido_check_rp_id(const char *id, const unsigned char *obtained_hash)
195{
196 unsigned char expected_hash[SHA256_DIGEST_LENGTH];
197
198 explicit_bzero(expected_hash, sizeof(expected_hash));
199
200 if (SHA256((const unsigned char *)id, strlen(id),
201 expected_hash) != expected_hash) {
202 fido_log_debug("%s: sha256", __func__);
203 return (-1);
204 }
205
206 return (timingsafe_bcmp(expected_hash, obtained_hash,
207 SHA256_DIGEST_LENGTH));
208}
209
210static int
211get_signed_hash_packed(fido_blob_t *dgst, const fido_blob_t *clientdata,
212 const fido_blob_t *authdata_cbor)
213{
214 cbor_item_t *item = NULL;
215 unsigned char *authdata_ptr = NULL;
216 size_t authdata_len;
217 struct cbor_load_result cbor;
218 SHA256_CTX ctx;
219 int ok = -1;
220
221 if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len,
222 &cbor)) == NULL) {
223 fido_log_debug("%s: cbor_load", __func__);
224 goto fail;
225 }
226
227 if (cbor_isa_bytestring(item) == false ||
228 cbor_bytestring_is_definite(item) == false) {
229 fido_log_debug("%s: cbor type", __func__);
230 goto fail;
231 }
232
233 authdata_ptr = cbor_bytestring_handle(item);
234 authdata_len = cbor_bytestring_length(item);
235
236 if (dgst->len != SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 ||
237 SHA256_Update(&ctx, authdata_ptr, authdata_len) == 0 ||
238 SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 ||
239 SHA256_Final(dgst->ptr, &ctx) == 0) {
240 fido_log_debug("%s: sha256", __func__);
241 goto fail;
242 }
243
244 ok = 0;
245fail:
246 if (item != NULL)
247 cbor_decref(&item);
248
249 return (ok);
250}
251
252static int
253get_signed_hash_u2f(fido_blob_t *dgst, const unsigned char *rp_id,
254 size_t rp_id_len, const fido_blob_t *clientdata, const fido_blob_t *id,
255 const es256_pk_t *pk)
256{
257 const uint8_t zero = 0;
258 const uint8_t four = 4; /* uncompressed point */
259 SHA256_CTX ctx;
260
261 if (dgst->len != SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 ||
262 SHA256_Update(&ctx, &zero, sizeof(zero)) == 0 ||
263 SHA256_Update(&ctx, rp_id, rp_id_len) == 0 ||
264 SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 ||
265 SHA256_Update(&ctx, id->ptr, id->len) == 0 ||
266 SHA256_Update(&ctx, &four, sizeof(four)) == 0 ||
267 SHA256_Update(&ctx, pk->x, sizeof(pk->x)) == 0 ||
268 SHA256_Update(&ctx, pk->y, sizeof(pk->y)) == 0 ||
269 SHA256_Final(dgst->ptr, &ctx) == 0) {
270 fido_log_debug("%s: sha256", __func__);
271 return (-1);
272 }
273
274 return (0);
275}
276
277static int
278verify_sig(const fido_blob_t *dgst, const fido_blob_t *x5c,
279 const fido_blob_t *sig)
280{
281 BIO *rawcert = NULL;
282 X509 *cert = NULL;
283 EVP_PKEY *pkey = NULL;
284 EC_KEY *ec;
285 int ok = -1;
286
287 /* openssl needs ints */
288 if (dgst->len > INT_MAX || x5c->len > INT_MAX || sig->len > INT_MAX) {
289 fido_log_debug("%s: dgst->len=%zu, x5c->len=%zu, sig->len=%zu",
290 __func__, dgst->len, x5c->len, sig->len);
291 return (-1);
292 }
293
294 /* fetch key from x509 */
295 if ((rawcert = BIO_new_mem_buf(x5c->ptr, (int)x5c->len)) == NULL ||
296 (cert = d2i_X509_bio(rawcert, NULL)) == NULL ||
297 (pkey = X509_get_pubkey(cert)) == NULL ||
298 (ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
299 fido_log_debug("%s: x509 key", __func__);
300 goto fail;
301 }
302
303 if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr,
304 (int)sig->len, ec) != 1) {
305 fido_log_debug("%s: ECDSA_verify", __func__);
306 goto fail;
307 }
308
309 ok = 0;
310fail:
311 if (rawcert != NULL)
312 BIO_free(rawcert);
313 if (cert != NULL)
314 X509_free(cert);
315 if (pkey != NULL)
316 EVP_PKEY_free(pkey);
317
318 return (ok);
319}
320
321int
322fido_cred_verify(const fido_cred_t *cred)
323{
324 unsigned char buf[SHA256_DIGEST_LENGTH];
325 fido_blob_t dgst;
326 int r;
327
328 dgst.ptr = buf;
329 dgst.len = sizeof(buf);
330
331 /* do we have everything we need? */
332 if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL ||
333 cred->attstmt.x5c.ptr == NULL || cred->attstmt.sig.ptr == NULL ||
334 cred->fmt == NULL || cred->attcred.id.ptr == NULL ||
335 cred->rp.id == NULL) {
336 fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, "
337 "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr,
338 (void *)cred->authdata_cbor.ptr,
339 (void *)cred->attstmt.x5c.ptr,
340 (void *)cred->attstmt.sig.ptr, (void *)cred->fmt,
341 (void *)cred->attcred.id.ptr, cred->rp.id);
342 r = FIDO_ERR_INVALID_ARGUMENT;
343 goto out;
344 }
345
346 if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) {
347 fido_log_debug("%s: fido_check_rp_id", __func__);
348 r = FIDO_ERR_INVALID_PARAM;
349 goto out;
350 }
351
352 if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE,
353 cred->uv) < 0) {
354 fido_log_debug("%s: fido_check_flags", __func__);
355 r = FIDO_ERR_INVALID_PARAM;
356 goto out;
357 }
358
359 if (check_extensions(cred->authdata_ext, cred->ext) < 0) {
360 fido_log_debug("%s: check_extensions", __func__);
361 r = FIDO_ERR_INVALID_PARAM;
362 goto out;
363 }
364
365 if (!strcmp(cred->fmt, "packed")) {
366 if (get_signed_hash_packed(&dgst, &cred->cdh,
367 &cred->authdata_cbor) < 0) {
368 fido_log_debug("%s: get_signed_hash_packed", __func__);
369 r = FIDO_ERR_INTERNAL;
370 goto out;
371 }
372 } else {
373 if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
374 sizeof(cred->authdata.rp_id_hash), &cred->cdh,
375 &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
376 fido_log_debug("%s: get_signed_hash_u2f", __func__);
377 r = FIDO_ERR_INTERNAL;
378 goto out;
379 }
380 }
381
382 if (verify_sig(&dgst, &cred->attstmt.x5c, &cred->attstmt.sig) < 0) {
383 fido_log_debug("%s: verify_sig", __func__);
384 r = FIDO_ERR_INVALID_SIG;
385 goto out;
386 }
387
388 r = FIDO_OK;
389out:
390 explicit_bzero(buf, sizeof(buf));
391
392 return (r);
393}
394
395int
396fido_cred_verify_self(const fido_cred_t *cred)
397{
398 unsigned char buf[SHA256_DIGEST_LENGTH];
399 fido_blob_t dgst;
400 int ok = -1;
401 int r;
402
403 dgst.ptr = buf;
404 dgst.len = sizeof(buf);
405
406 /* do we have everything we need? */
407 if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL ||
408 cred->attstmt.x5c.ptr != NULL || cred->attstmt.sig.ptr == NULL ||
409 cred->fmt == NULL || cred->attcred.id.ptr == NULL ||
410 cred->rp.id == NULL) {
411 fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, "
412 "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr,
413 (void *)cred->authdata_cbor.ptr,
414 (void *)cred->attstmt.x5c.ptr,
415 (void *)cred->attstmt.sig.ptr, (void *)cred->fmt,
416 (void *)cred->attcred.id.ptr, cred->rp.id);
417 r = FIDO_ERR_INVALID_ARGUMENT;
418 goto out;
419 }
420
421 if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) {
422 fido_log_debug("%s: fido_check_rp_id", __func__);
423 r = FIDO_ERR_INVALID_PARAM;
424 goto out;
425 }
426
427 if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE,
428 cred->uv) < 0) {
429 fido_log_debug("%s: fido_check_flags", __func__);
430 r = FIDO_ERR_INVALID_PARAM;
431 goto out;
432 }
433
434 if (check_extensions(cred->authdata_ext, cred->ext) < 0) {
435 fido_log_debug("%s: check_extensions", __func__);
436 r = FIDO_ERR_INVALID_PARAM;
437 goto out;
438 }
439
440 if (!strcmp(cred->fmt, "packed")) {
441 if (get_signed_hash_packed(&dgst, &cred->cdh,
442 &cred->authdata_cbor) < 0) {
443 fido_log_debug("%s: get_signed_hash_packed", __func__);
444 r = FIDO_ERR_INTERNAL;
445 goto out;
446 }
447 } else {
448 if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
449 sizeof(cred->authdata.rp_id_hash), &cred->cdh,
450 &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
451 fido_log_debug("%s: get_signed_hash_u2f", __func__);
452 r = FIDO_ERR_INTERNAL;
453 goto out;
454 }
455 }
456
457 switch (cred->attcred.type) {
458 case COSE_ES256:
459 ok = fido_verify_sig_es256(&dgst, &cred->attcred.pubkey.es256,
460 &cred->attstmt.sig);
461 break;
462 case COSE_RS256:
463 ok = fido_verify_sig_rs256(&dgst, &cred->attcred.pubkey.rs256,
464 &cred->attstmt.sig);
465 break;
466 case COSE_EDDSA:
467 ok = fido_verify_sig_eddsa(&dgst, &cred->attcred.pubkey.eddsa,
468 &cred->attstmt.sig);
469 break;
470 default:
471 fido_log_debug("%s: unsupported cose_alg %d", __func__,
472 cred->attcred.type);
473 r = FIDO_ERR_UNSUPPORTED_OPTION;
474 goto out;
475 }
476
477 if (ok < 0)
478 r = FIDO_ERR_INVALID_SIG;
479 else
480 r = FIDO_OK;
481
482out:
483 explicit_bzero(buf, sizeof(buf));
484
485 return (r);
486}
487
488fido_cred_t *
489fido_cred_new(void)
490{
491 return (calloc(1, sizeof(fido_cred_t)));
492}
493
494static void
495fido_cred_clean_authdata(fido_cred_t *cred)
496{
497 free(cred->authdata_cbor.ptr);
498 free(cred->attcred.id.ptr);
499
500 memset(&cred->authdata_ext, 0, sizeof(cred->authdata_ext));
501 memset(&cred->authdata_cbor, 0, sizeof(cred->authdata_cbor));
502 memset(&cred->authdata, 0, sizeof(cred->authdata));
503 memset(&cred->attcred, 0, sizeof(cred->attcred));
504}
505
506void
507fido_cred_reset_tx(fido_cred_t *cred)
508{
509 free(cred->cdh.ptr);
510 free(cred->rp.id);
511 free(cred->rp.name);
512 free(cred->user.id.ptr);
513 free(cred->user.icon);
514 free(cred->user.name);
515 free(cred->user.display_name);
516 fido_free_blob_array(&cred->excl);
517
518 memset(&cred->cdh, 0, sizeof(cred->cdh));
519 memset(&cred->rp, 0, sizeof(cred->rp));
520 memset(&cred->user, 0, sizeof(cred->user));
521 memset(&cred->excl, 0, sizeof(cred->excl));
522
523 cred->type = 0;
524 cred->ext = 0;
525 cred->rk = FIDO_OPT_OMIT;
526 cred->uv = FIDO_OPT_OMIT;
527}
528
529static void
530fido_cred_clean_x509(fido_cred_t *cred)
531{
532 free(cred->attstmt.x5c.ptr);
533 cred->attstmt.x5c.ptr = NULL;
534 cred->attstmt.x5c.len = 0;
535}
536
537static void
538fido_cred_clean_sig(fido_cred_t *cred)
539{
540 free(cred->attstmt.sig.ptr);
541 cred->attstmt.sig.ptr = NULL;
542 cred->attstmt.sig.len = 0;
543}
544
545void
546fido_cred_reset_rx(fido_cred_t *cred)
547{
548 free(cred->fmt);
549 cred->fmt = NULL;
550
551 fido_cred_clean_authdata(cred);
552 fido_cred_clean_x509(cred);
553 fido_cred_clean_sig(cred);
554}
555
556void
557fido_cred_free(fido_cred_t **cred_p)
558{
559 fido_cred_t *cred;
560
561 if (cred_p == NULL || (cred = *cred_p) == NULL)
562 return;
563
564 fido_cred_reset_tx(cred);
565 fido_cred_reset_rx(cred);
566
567 free(cred);
568
569 *cred_p = NULL;
570}
571
572int
573fido_cred_set_authdata(fido_cred_t *cred, const unsigned char *ptr, size_t len)
574{
575 cbor_item_t *item = NULL;
576 struct cbor_load_result cbor;
577 int r;
578
579 fido_cred_clean_authdata(cred);
580
581 if (ptr == NULL || len == 0) {
582 r = FIDO_ERR_INVALID_ARGUMENT;
583 goto fail;
584 }
585
586 if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
587 fido_log_debug("%s: cbor_load", __func__);
588 r = FIDO_ERR_INVALID_ARGUMENT;
589 goto fail;
590 }
591
592 if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
593 &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
594 fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
595 r = FIDO_ERR_INVALID_ARGUMENT;
596 goto fail;
597 }
598
599 r = FIDO_OK;
600fail:
601 if (item != NULL)
602 cbor_decref(&item);
603
604 if (r != FIDO_OK)
605 fido_cred_clean_authdata(cred);
606
607 return (r);
608
609}
610
611int
612fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr,
613 size_t len)
614{
615 cbor_item_t *item = NULL;
616 int r;
617
618 fido_cred_clean_authdata(cred);
619
620 if (ptr == NULL || len == 0) {
621 r = FIDO_ERR_INVALID_ARGUMENT;
622 goto fail;
623 }
624
625 if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
626 fido_log_debug("%s: cbor_build_bytestring", __func__);
627 r = FIDO_ERR_INTERNAL;
628 goto fail;
629 }
630
631 if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
632 &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
633 fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
634 r = FIDO_ERR_INVALID_ARGUMENT;
635 goto fail;
636 }
637
638 r = FIDO_OK;
639fail:
640 if (item != NULL)
641 cbor_decref(&item);
642
643 if (r != FIDO_OK)
644 fido_cred_clean_authdata(cred);
645
646 return (r);
647
648}
649
650int
651fido_cred_set_x509(fido_cred_t *cred, const unsigned char *ptr, size_t len)
652{
653 unsigned char *x509;
654
655 fido_cred_clean_x509(cred);
656
657 if (ptr == NULL || len == 0)
658 return (FIDO_ERR_INVALID_ARGUMENT);
659 if ((x509 = malloc(len)) == NULL)
660 return (FIDO_ERR_INTERNAL);
661
662 memcpy(x509, ptr, len);
663 cred->attstmt.x5c.ptr = x509;
664 cred->attstmt.x5c.len = len;
665
666 return (FIDO_OK);
667}
668
669int
670fido_cred_set_sig(fido_cred_t *cred, const unsigned char *ptr, size_t len)
671{
672 unsigned char *sig;
673
674 fido_cred_clean_sig(cred);
675
676 if (ptr == NULL || len == 0)
677 return (FIDO_ERR_INVALID_ARGUMENT);
678 if ((sig = malloc(len)) == NULL)
679 return (FIDO_ERR_INTERNAL);
680
681 memcpy(sig, ptr, len);
682 cred->attstmt.sig.ptr = sig;
683 cred->attstmt.sig.len = len;
684
685 return (FIDO_OK);
686}
687
688int
689fido_cred_exclude(fido_cred_t *cred, const unsigned char *id_ptr, size_t id_len)
690{
691 fido_blob_t id_blob;
692 fido_blob_t *list_ptr;
693
694 memset(&id_blob, 0, sizeof(id_blob));
695
696 if (fido_blob_set(&id_blob, id_ptr, id_len) < 0)
697 return (FIDO_ERR_INVALID_ARGUMENT);
698
699 if (cred->excl.len == SIZE_MAX) {
700 free(id_blob.ptr);
701 return (FIDO_ERR_INVALID_ARGUMENT);
702 }
703
704 if ((list_ptr = recallocarray(cred->excl.ptr, cred->excl.len,
705 cred->excl.len + 1, sizeof(fido_blob_t))) == NULL) {
706 free(id_blob.ptr);
707 return (FIDO_ERR_INTERNAL);
708 }
709
710 list_ptr[cred->excl.len++] = id_blob;
711 cred->excl.ptr = list_ptr;
712
713 return (FIDO_OK);
714}
715
716int
717fido_cred_set_clientdata_hash(fido_cred_t *cred, const unsigned char *hash,
718 size_t hash_len)
719{
720 if (fido_blob_set(&cred->cdh, hash, hash_len) < 0)
721 return (FIDO_ERR_INVALID_ARGUMENT);
722
723 return (FIDO_OK);
724}
725
726int
727fido_cred_set_rp(fido_cred_t *cred, const char *id, const char *name)
728{
729 fido_rp_t *rp = &cred->rp;
730
731 if (rp->id != NULL) {
732 free(rp->id);
733 rp->id = NULL;
734 }
735 if (rp->name != NULL) {
736 free(rp->name);
737 rp->name = NULL;
738 }
739
740 if (id != NULL && (rp->id = strdup(id)) == NULL)
741 goto fail;
742 if (name != NULL && (rp->name = strdup(name)) == NULL)
743 goto fail;
744
745 return (FIDO_OK);
746fail:
747 free(rp->id);
748 free(rp->name);
749 rp->id = NULL;
750 rp->name = NULL;
751
752 return (FIDO_ERR_INTERNAL);
753}
754
755int
756fido_cred_set_user(fido_cred_t *cred, const unsigned char *user_id,
757 size_t user_id_len, const char *name, const char *display_name,
758 const char *icon)
759{
760 fido_user_t *up = &cred->user;
761
762 if (up->id.ptr != NULL) {
763 free(up->id.ptr);
764 up->id.ptr = NULL;
765 up->id.len = 0;
766 }
767 if (up->name != NULL) {
768 free(up->name);
769 up->name = NULL;
770 }
771 if (up->display_name != NULL) {
772 free(up->display_name);
773 up->display_name = NULL;
774 }
775 if (up->icon != NULL) {
776 free(up->icon);
777 up->icon = NULL;
778 }
779
780 if (user_id != NULL) {
781 if ((up->id.ptr = malloc(user_id_len)) == NULL)
782 goto fail;
783 memcpy(up->id.ptr, user_id, user_id_len);
784 up->id.len = user_id_len;
785 }
786 if (name != NULL && (up->name = strdup(name)) == NULL)
787 goto fail;
788 if (display_name != NULL &&
789 (up->display_name = strdup(display_name)) == NULL)
790 goto fail;
791 if (icon != NULL && (up->icon = strdup(icon)) == NULL)
792 goto fail;
793
794 return (FIDO_OK);
795fail:
796 free(up->id.ptr);
797 free(up->name);
798 free(up->display_name);
799 free(up->icon);
800
801 up->id.ptr = NULL;
802 up->id.len = 0;
803 up->name = NULL;
804 up->display_name = NULL;
805 up->icon = NULL;
806
807 return (FIDO_ERR_INTERNAL);
808}
809
810int
811fido_cred_set_extensions(fido_cred_t *cred, int ext)
812{
813 if (ext != 0 && ext != FIDO_EXT_HMAC_SECRET)
814 return (FIDO_ERR_INVALID_ARGUMENT);
815
816 cred->ext = ext;
817
818 return (FIDO_OK);
819}
820
821int
822fido_cred_set_options(fido_cred_t *cred, bool rk, bool uv)
823{
824 cred->rk = rk ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
825 cred->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
826
827 return (FIDO_OK);
828}
829
830int
831fido_cred_set_rk(fido_cred_t *cred, fido_opt_t rk)
832{
833 cred->rk = rk;
834
835 return (FIDO_OK);
836}
837
838int
839fido_cred_set_uv(fido_cred_t *cred, fido_opt_t uv)
840{
841 cred->uv = uv;
842
843 return (FIDO_OK);
844}
845
846int
847fido_cred_set_fmt(fido_cred_t *cred, const char *fmt)
848{
849 free(cred->fmt);
850 cred->fmt = NULL;
851
852 if (fmt == NULL)
853 return (FIDO_ERR_INVALID_ARGUMENT);
854
855 if (strcmp(fmt, "packed") && strcmp(fmt, "fido-u2f"))
856 return (FIDO_ERR_INVALID_ARGUMENT);
857
858 if ((cred->fmt = strdup(fmt)) == NULL)
859 return (FIDO_ERR_INTERNAL);
860
861 return (FIDO_OK);
862}
863
864int
865fido_cred_set_type(fido_cred_t *cred, int cose_alg)
866{
867 if ((cose_alg != COSE_ES256 && cose_alg != COSE_RS256 &&
868 cose_alg != COSE_EDDSA) || cred->type != 0)
869 return (FIDO_ERR_INVALID_ARGUMENT);
870
871 cred->type = cose_alg;
872
873 return (FIDO_OK);
874}
875
876int
877fido_cred_type(const fido_cred_t *cred)
878{
879 return (cred->type);
880}
881
882uint8_t
883fido_cred_flags(const fido_cred_t *cred)
884{
885 return (cred->authdata.flags);
886}
887
888const unsigned char *
889fido_cred_clientdata_hash_ptr(const fido_cred_t *cred)
890{
891 return (cred->cdh.ptr);
892}
893
894size_t
895fido_cred_clientdata_hash_len(const fido_cred_t *cred)
896{
897 return (cred->cdh.len);
898}
899
900const unsigned char *
901fido_cred_x5c_ptr(const fido_cred_t *cred)
902{
903 return (cred->attstmt.x5c.ptr);
904}
905
906size_t
907fido_cred_x5c_len(const fido_cred_t *cred)
908{
909 return (cred->attstmt.x5c.len);
910}
911
912const unsigned char *
913fido_cred_sig_ptr(const fido_cred_t *cred)
914{
915 return (cred->attstmt.sig.ptr);
916}
917
918size_t
919fido_cred_sig_len(const fido_cred_t *cred)
920{
921 return (cred->attstmt.sig.len);
922}
923
924const unsigned char *
925fido_cred_authdata_ptr(const fido_cred_t *cred)
926{
927 return (cred->authdata_cbor.ptr);
928}
929
930size_t
931fido_cred_authdata_len(const fido_cred_t *cred)
932{
933 return (cred->authdata_cbor.len);
934}
935
936const unsigned char *
937fido_cred_pubkey_ptr(const fido_cred_t *cred)
938{
939 const void *ptr;
940
941 switch (cred->attcred.type) {
942 case COSE_ES256:
943 ptr = &cred->attcred.pubkey.es256;
944 break;
945 case COSE_RS256:
946 ptr = &cred->attcred.pubkey.rs256;
947 break;
948 case COSE_EDDSA:
949 ptr = &cred->attcred.pubkey.eddsa;
950 break;
951 default:
952 ptr = NULL;
953 break;
954 }
955
956 return (ptr);
957}
958
959size_t
960fido_cred_pubkey_len(const fido_cred_t *cred)
961{
962 size_t len;
963
964 switch (cred->attcred.type) {
965 case COSE_ES256:
966 len = sizeof(cred->attcred.pubkey.es256);
967 break;
968 case COSE_RS256:
969 len = sizeof(cred->attcred.pubkey.rs256);
970 break;
971 case COSE_EDDSA:
972 len = sizeof(cred->attcred.pubkey.eddsa);
973 break;
974 default:
975 len = 0;
976 break;
977 }
978
979 return (len);
980}
981
982const unsigned char *
983fido_cred_id_ptr(const fido_cred_t *cred)
984{
985 return (cred->attcred.id.ptr);
986}
987
988size_t
989fido_cred_id_len(const fido_cred_t *cred)
990{
991 return (cred->attcred.id.len);
992}
993
994const char *
995fido_cred_fmt(const fido_cred_t *cred)
996{
997 return (cred->fmt);
998}
999
1000const char *
1001fido_cred_rp_id(const fido_cred_t *cred)
1002{
1003 return (cred->rp.id);
1004}
1005
1006const char *
1007fido_cred_rp_name(const fido_cred_t *cred)
1008{
1009 return (cred->rp.name);
1010}
1011
1012const char *
1013fido_cred_user_name(const fido_cred_t *cred)
1014{
1015 return (cred->user.name);
1016}
1017
1018const char *
1019fido_cred_display_name(const fido_cred_t *cred)
1020{
1021 return (cred->user.display_name);
1022}
1023
1024const unsigned char *
1025fido_cred_user_id_ptr(const fido_cred_t *cred)
1026{
1027 return (cred->user.id.ptr);
1028}
1029
1030size_t
1031fido_cred_user_id_len(const fido_cred_t *cred)
1032{
1033 return (cred->user.id.len);
1034}
diff --git a/src/credman.c b/src/credman.c
new file mode 100644
index 0000000..76327e5
--- /dev/null
+++ b/src/credman.c
@@ -0,0 +1,736 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/sha.h>
8
9#include <string.h>
10
11#include "fido.h"
12#include "fido/credman.h"
13#include "fido/es256.h"
14
15#define CMD_CRED_METADATA 0x01
16#define CMD_RP_BEGIN 0x02
17#define CMD_RP_NEXT 0x03
18#define CMD_RK_BEGIN 0x04
19#define CMD_RK_NEXT 0x05
20#define CMD_DELETE_CRED 0x06
21
22static int
23credman_grow_array(void **ptr, size_t *n_alloc, size_t *n_rx, size_t n,
24 size_t size)
25{
26 void *new_ptr;
27
28#ifdef FIDO_FUZZ
29 if (n > UINT8_MAX) {
30 fido_log_debug("%s: n > UINT8_MAX", __func__);
31 return (-1);
32 }
33#endif
34
35 if (n < *n_alloc)
36 return (0);
37
38 /* sanity check */
39 if (*n_rx > 0 || *n_rx > *n_alloc || n < *n_alloc) {
40 fido_log_debug("%s: n=%zu, n_rx=%zu, n_alloc=%zu", __func__, n,
41 *n_rx, *n_alloc);
42 return (-1);
43 }
44
45 if ((new_ptr = recallocarray(*ptr, *n_alloc, n, size)) == NULL)
46 return (-1);
47
48 *ptr = new_ptr;
49 *n_alloc = n;
50
51 return (0);
52}
53
54static int
55credman_prepare_hmac(uint8_t cmd, const fido_blob_t *body, cbor_item_t **param,
56 fido_blob_t *hmac_data)
57{
58 cbor_item_t *param_cbor[2];
59 size_t n;
60 int ok = -1;
61
62 memset(&param_cbor, 0, sizeof(param_cbor));
63
64 if (body == NULL)
65 return (fido_blob_set(hmac_data, &cmd, sizeof(cmd)));
66
67 switch (cmd) {
68 case CMD_RK_BEGIN:
69 n = 1;
70 param_cbor[n - 1] = fido_blob_encode(body);
71 break;
72 case CMD_DELETE_CRED:
73 n = 2;
74 param_cbor[n - 1] = cbor_encode_pubkey(body);
75 break;
76 default:
77 fido_log_debug("%s: unknown cmd=0x%02x", __func__, cmd);
78 return (-1);
79 }
80
81 if (param_cbor[n - 1] == NULL) {
82 fido_log_debug("%s: cbor encode", __func__);
83 return (-1);
84 }
85 if ((*param = cbor_flatten_vector(param_cbor, n)) == NULL) {
86 fido_log_debug("%s: cbor_flatten_vector", __func__);
87 goto fail;
88 }
89 if (cbor_build_frame(cmd, param_cbor, n, hmac_data) < 0) {
90 fido_log_debug("%s: cbor_build_frame", __func__);
91 goto fail;
92 }
93
94 ok = 0;
95fail:
96 cbor_vector_free(param_cbor, nitems(param_cbor));
97
98 return (ok);
99}
100
101static int
102credman_tx(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *param,
103 const char *pin)
104{
105 fido_blob_t f;
106 fido_blob_t *ecdh = NULL;
107 fido_blob_t hmac;
108 es256_pk_t *pk = NULL;
109 cbor_item_t *argv[4];
110 int r = FIDO_ERR_INTERNAL;
111
112 memset(&f, 0, sizeof(f));
113 memset(&hmac, 0, sizeof(hmac));
114 memset(&argv, 0, sizeof(argv));
115
116 /* subCommand */
117 if ((argv[0] = cbor_build_uint8(cmd)) == NULL) {
118 fido_log_debug("%s: cbor encode", __func__);
119 goto fail;
120 }
121
122 /* pinProtocol, pinAuth */
123 if (pin != NULL) {
124 if (credman_prepare_hmac(cmd, param, &argv[1], &hmac) < 0) {
125 fido_log_debug("%s: credman_prepare_hmac", __func__);
126 goto fail;
127 }
128 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
129 fido_log_debug("%s: fido_do_ecdh", __func__);
130 goto fail;
131 }
132 if ((r = cbor_add_pin_params(dev, &hmac, pk, ecdh, pin,
133 &argv[3], &argv[2])) != FIDO_OK) {
134 fido_log_debug("%s: cbor_add_pin_params", __func__);
135 goto fail;
136 }
137 }
138
139 /* framing and transmission */
140 if (cbor_build_frame(CTAP_CBOR_CRED_MGMT_PRE, argv, 4, &f) < 0 ||
141 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
142 fido_log_debug("%s: fido_tx", __func__);
143 r = FIDO_ERR_TX;
144 goto fail;
145 }
146
147 r = FIDO_OK;
148fail:
149 es256_pk_free(&pk);
150 fido_blob_free(&ecdh);
151 cbor_vector_free(argv, nitems(argv));
152 free(f.ptr);
153 free(hmac.ptr);
154
155 return (r);
156}
157
158static int
159credman_parse_metadata(const cbor_item_t *key, const cbor_item_t *val,
160 void *arg)
161{
162 fido_credman_metadata_t *metadata = arg;
163
164 if (cbor_isa_uint(key) == false ||
165 cbor_int_get_width(key) != CBOR_INT_8) {
166 fido_log_debug("%s: cbor type", __func__);
167 return (0); /* ignore */
168 }
169
170 switch (cbor_get_uint8(key)) {
171 case 1:
172 return (cbor_decode_uint64(val, &metadata->rk_existing));
173 case 2:
174 return (cbor_decode_uint64(val, &metadata->rk_remaining));
175 default:
176 fido_log_debug("%s: cbor type", __func__);
177 return (0); /* ignore */
178 }
179}
180
181static int
182credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int ms)
183{
184 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
185 unsigned char reply[512];
186 int reply_len;
187 int r;
188
189 memset(metadata, 0, sizeof(*metadata));
190
191 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
192 fido_log_debug("%s: fido_rx", __func__);
193 return (FIDO_ERR_RX);
194 }
195
196 if ((r = cbor_parse_reply(reply, (size_t)reply_len, metadata,
197 credman_parse_metadata)) != FIDO_OK) {
198 fido_log_debug("%s: credman_parse_metadata", __func__);
199 return (r);
200 }
201
202 return (FIDO_OK);
203}
204
205static int
206credman_get_metadata_wait(fido_dev_t *dev, fido_credman_metadata_t *metadata,
207 const char *pin, int ms)
208{
209 int r;
210
211 if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin)) != FIDO_OK ||
212 (r = credman_rx_metadata(dev, metadata, ms)) != FIDO_OK)
213 return (r);
214
215 return (FIDO_OK);
216}
217
218int
219fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata,
220 const char *pin)
221{
222 if (fido_dev_is_fido2(dev) == false)
223 return (FIDO_ERR_INVALID_COMMAND);
224 if (pin == NULL)
225 return (FIDO_ERR_INVALID_ARGUMENT);
226
227 return (credman_get_metadata_wait(dev, metadata, pin, -1));
228}
229
230static int
231credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg)
232{
233 fido_cred_t *cred = arg;
234
235 if (cbor_isa_uint(key) == false ||
236 cbor_int_get_width(key) != CBOR_INT_8) {
237 fido_log_debug("%s: cbor type", __func__);
238 return (0); /* ignore */
239 }
240
241 switch (cbor_get_uint8(key)) {
242 case 6: /* user entity */
243 return (cbor_decode_user(val, &cred->user));
244 case 7:
245 return (cbor_decode_cred_id(val, &cred->attcred.id));
246 case 8:
247 if (cbor_decode_pubkey(val, &cred->attcred.type,
248 &cred->attcred.pubkey) < 0)
249 return (-1);
250 cred->type = cred->attcred.type; /* XXX */
251 return (0);
252 default:
253 fido_log_debug("%s: cbor type", __func__);
254 return (0); /* ignore */
255 }
256}
257
258static void
259credman_reset_rk(fido_credman_rk_t *rk)
260{
261 for (size_t i = 0; i < rk->n_alloc; i++) {
262 fido_cred_reset_tx(&rk->ptr[i]);
263 fido_cred_reset_rx(&rk->ptr[i]);
264 }
265
266 free(rk->ptr);
267 rk->ptr = NULL;
268 memset(rk, 0, sizeof(*rk));
269}
270
271static int
272credman_parse_rk_count(const cbor_item_t *key, const cbor_item_t *val,
273 void *arg)
274{
275 fido_credman_rk_t *rk = arg;
276 uint64_t n;
277
278 /* totalCredentials */
279 if (cbor_isa_uint(key) == false ||
280 cbor_int_get_width(key) != CBOR_INT_8 ||
281 cbor_get_uint8(key) != 9) {
282 fido_log_debug("%s: cbor_type", __func__);
283 return (0); /* ignore */
284 }
285
286 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
287 fido_log_debug("%s: cbor_decode_uint64", __func__);
288 return (-1);
289 }
290
291 if (credman_grow_array((void **)&rk->ptr, &rk->n_alloc, &rk->n_rx,
292 (size_t)n, sizeof(*rk->ptr)) < 0) {
293 fido_log_debug("%s: credman_grow_array", __func__);
294 return (-1);
295 }
296
297 return (0);
298}
299
300static int
301credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms)
302{
303 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
304 unsigned char reply[2048];
305 int reply_len;
306 int r;
307
308 credman_reset_rk(rk);
309
310 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
311 fido_log_debug("%s: fido_rx", __func__);
312 return (FIDO_ERR_RX);
313 }
314
315 /* adjust as needed */
316 if ((r = cbor_parse_reply(reply, (size_t)reply_len, rk,
317 credman_parse_rk_count)) != FIDO_OK) {
318 fido_log_debug("%s: credman_parse_rk_count", __func__);
319 return (r);
320 }
321
322 if (rk->n_alloc == 0) {
323 fido_log_debug("%s: n_alloc=0", __func__);
324 return (FIDO_OK);
325 }
326
327 /* parse the first rk */
328 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rk->ptr[0],
329 credman_parse_rk)) != FIDO_OK) {
330 fido_log_debug("%s: credman_parse_rk", __func__);
331 return (r);
332 }
333
334 rk->n_rx++;
335
336 return (FIDO_OK);
337}
338
339static int
340credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms)
341{
342 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
343 unsigned char reply[2048];
344 int reply_len;
345 int r;
346
347 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
348 fido_log_debug("%s: fido_rx", __func__);
349 return (FIDO_ERR_RX);
350 }
351
352 /* sanity check */
353 if (rk->n_rx >= rk->n_alloc) {
354 fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rk->n_rx,
355 rk->n_alloc);
356 return (FIDO_ERR_INTERNAL);
357 }
358
359 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rk->ptr[rk->n_rx],
360 credman_parse_rk)) != FIDO_OK) {
361 fido_log_debug("%s: credman_parse_rk", __func__);
362 return (r);
363 }
364
365 return (FIDO_OK);
366}
367
368static int
369credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk,
370 const char *pin, int ms)
371{
372 fido_blob_t rp_dgst;
373 uint8_t dgst[SHA256_DIGEST_LENGTH];
374 int r;
375
376 if (SHA256((const unsigned char *)rp_id, strlen(rp_id), dgst) != dgst) {
377 fido_log_debug("%s: sha256", __func__);
378 return (FIDO_ERR_INTERNAL);
379 }
380
381 rp_dgst.ptr = dgst;
382 rp_dgst.len = sizeof(dgst);
383
384 if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin)) != FIDO_OK ||
385 (r = credman_rx_rk(dev, rk, ms)) != FIDO_OK)
386 return (r);
387
388 while (rk->n_rx < rk->n_alloc) {
389 if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL)) != FIDO_OK ||
390 (r = credman_rx_next_rk(dev, rk, ms)) != FIDO_OK)
391 return (r);
392 rk->n_rx++;
393 }
394
395 return (FIDO_OK);
396}
397
398int
399fido_credman_get_dev_rk(fido_dev_t *dev, const char *rp_id,
400 fido_credman_rk_t *rk, const char *pin)
401{
402 if (fido_dev_is_fido2(dev) == false)
403 return (FIDO_ERR_INVALID_COMMAND);
404 if (pin == NULL)
405 return (FIDO_ERR_INVALID_ARGUMENT);
406
407 return (credman_get_rk_wait(dev, rp_id, rk, pin, -1));
408}
409
410static int
411credman_del_rk_wait(fido_dev_t *dev, const unsigned char *cred_id,
412 size_t cred_id_len, const char *pin, int ms)
413{
414 fido_blob_t cred;
415 int r;
416
417 memset(&cred, 0, sizeof(cred));
418
419 if (fido_blob_set(&cred, cred_id, cred_id_len) < 0)
420 return (FIDO_ERR_INVALID_ARGUMENT);
421
422 if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin)) != FIDO_OK ||
423 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
424 goto fail;
425
426 r = FIDO_OK;
427fail:
428 free(cred.ptr);
429
430 return (r);
431}
432
433int
434fido_credman_del_dev_rk(fido_dev_t *dev, const unsigned char *cred_id,
435 size_t cred_id_len, const char *pin)
436{
437 if (fido_dev_is_fido2(dev) == false)
438 return (FIDO_ERR_INVALID_COMMAND);
439 if (pin == NULL)
440 return (FIDO_ERR_INVALID_ARGUMENT);
441
442 return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, -1));
443}
444
445static int
446credman_parse_rp(const cbor_item_t *key, const cbor_item_t *val, void *arg)
447{
448 struct fido_credman_single_rp *rp = arg;
449
450 if (cbor_isa_uint(key) == false ||
451 cbor_int_get_width(key) != CBOR_INT_8) {
452 fido_log_debug("%s: cbor type", __func__);
453 return (0); /* ignore */
454 }
455
456 switch (cbor_get_uint8(key)) {
457 case 3:
458 return (cbor_decode_rp_entity(val, &rp->rp_entity));
459 case 4:
460 return (fido_blob_decode(val, &rp->rp_id_hash));
461 default:
462 fido_log_debug("%s: cbor type", __func__);
463 return (0); /* ignore */
464 }
465}
466
467static void
468credman_reset_rp(fido_credman_rp_t *rp)
469{
470 for (size_t i = 0; i < rp->n_alloc; i++) {
471 free(rp->ptr[i].rp_entity.id);
472 free(rp->ptr[i].rp_entity.name);
473 rp->ptr[i].rp_entity.id = NULL;
474 rp->ptr[i].rp_entity.name = NULL;
475 free(rp->ptr[i].rp_id_hash.ptr);
476 memset(&rp->ptr[i].rp_id_hash, 0,
477 sizeof(rp->ptr[i].rp_id_hash));
478 }
479
480 free(rp->ptr);
481 rp->ptr = NULL;
482 memset(rp, 0, sizeof(*rp));
483}
484
485static int
486credman_parse_rp_count(const cbor_item_t *key, const cbor_item_t *val,
487 void *arg)
488{
489 fido_credman_rp_t *rp = arg;
490 uint64_t n;
491
492 /* totalRPs */
493 if (cbor_isa_uint(key) == false ||
494 cbor_int_get_width(key) != CBOR_INT_8 ||
495 cbor_get_uint8(key) != 5) {
496 fido_log_debug("%s: cbor_type", __func__);
497 return (0); /* ignore */
498 }
499
500 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
501 fido_log_debug("%s: cbor_decode_uint64", __func__);
502 return (-1);
503 }
504
505 if (credman_grow_array((void **)&rp->ptr, &rp->n_alloc, &rp->n_rx,
506 (size_t)n, sizeof(*rp->ptr)) < 0) {
507 fido_log_debug("%s: credman_grow_array", __func__);
508 return (-1);
509 }
510
511 return (0);
512}
513
514static int
515credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms)
516{
517 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
518 unsigned char reply[2048];
519 int reply_len;
520 int r;
521
522 credman_reset_rp(rp);
523
524 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
525 fido_log_debug("%s: fido_rx", __func__);
526 return (FIDO_ERR_RX);
527 }
528
529 /* adjust as needed */
530 if ((r = cbor_parse_reply(reply, (size_t)reply_len, rp,
531 credman_parse_rp_count)) != FIDO_OK) {
532 fido_log_debug("%s: credman_parse_rp_count", __func__);
533 return (r);
534 }
535
536 if (rp->n_alloc == 0) {
537 fido_log_debug("%s: n_alloc=0", __func__);
538 return (FIDO_OK);
539 }
540
541 /* parse the first rp */
542 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rp->ptr[0],
543 credman_parse_rp)) != FIDO_OK) {
544 fido_log_debug("%s: credman_parse_rp", __func__);
545 return (r);
546 }
547
548 rp->n_rx++;
549
550 return (FIDO_OK);
551}
552
553static int
554credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms)
555{
556 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
557 unsigned char reply[2048];
558 int reply_len;
559 int r;
560
561 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
562 fido_log_debug("%s: fido_rx", __func__);
563 return (FIDO_ERR_RX);
564 }
565
566 /* sanity check */
567 if (rp->n_rx >= rp->n_alloc) {
568 fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rp->n_rx,
569 rp->n_alloc);
570 return (FIDO_ERR_INTERNAL);
571 }
572
573 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rp->ptr[rp->n_rx],
574 credman_parse_rp)) != FIDO_OK) {
575 fido_log_debug("%s: credman_parse_rp", __func__);
576 return (r);
577 }
578
579 return (FIDO_OK);
580}
581
582static int
583credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin,
584 int ms)
585{
586 int r;
587
588 if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin)) != FIDO_OK ||
589 (r = credman_rx_rp(dev, rp, ms)) != FIDO_OK)
590 return (r);
591
592 while (rp->n_rx < rp->n_alloc) {
593 if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL)) != FIDO_OK ||
594 (r = credman_rx_next_rp(dev, rp, ms)) != FIDO_OK)
595 return (r);
596 rp->n_rx++;
597 }
598
599 return (FIDO_OK);
600}
601
602int
603fido_credman_get_dev_rp(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin)
604{
605 if (fido_dev_is_fido2(dev) == false)
606 return (FIDO_ERR_INVALID_COMMAND);
607 if (pin == NULL)
608 return (FIDO_ERR_INVALID_ARGUMENT);
609
610 return (credman_get_rp_wait(dev, rp, pin, -1));
611}
612
613fido_credman_rk_t *
614fido_credman_rk_new(void)
615{
616 return (calloc(1, sizeof(fido_credman_rk_t)));
617}
618
619void
620fido_credman_rk_free(fido_credman_rk_t **rk_p)
621{
622 fido_credman_rk_t *rk;
623
624 if (rk_p == NULL || (rk = *rk_p) == NULL)
625 return;
626
627 credman_reset_rk(rk);
628 free(rk);
629 *rk_p = NULL;
630}
631
632size_t
633fido_credman_rk_count(const fido_credman_rk_t *rk)
634{
635 return (rk->n_rx);
636}
637
638const fido_cred_t *
639fido_credman_rk(const fido_credman_rk_t *rk, size_t idx)
640{
641 if (idx >= rk->n_alloc)
642 return (NULL);
643
644 return (&rk->ptr[idx]);
645}
646
647fido_credman_metadata_t *
648fido_credman_metadata_new(void)
649{
650 return (calloc(1, sizeof(fido_credman_metadata_t)));
651}
652
653void
654fido_credman_metadata_free(fido_credman_metadata_t **metadata_p)
655{
656 fido_credman_metadata_t *metadata;
657
658 if (metadata_p == NULL || (metadata = *metadata_p) == NULL)
659 return;
660
661 free(metadata);
662 *metadata_p = NULL;
663}
664
665uint64_t
666fido_credman_rk_existing(const fido_credman_metadata_t *metadata)
667{
668 return (metadata->rk_existing);
669}
670
671uint64_t
672fido_credman_rk_remaining(const fido_credman_metadata_t *metadata)
673{
674 return (metadata->rk_remaining);
675}
676
677fido_credman_rp_t *
678fido_credman_rp_new(void)
679{
680 return (calloc(1, sizeof(fido_credman_rp_t)));
681}
682
683void
684fido_credman_rp_free(fido_credman_rp_t **rp_p)
685{
686 fido_credman_rp_t *rp;
687
688 if (rp_p == NULL || (rp = *rp_p) == NULL)
689 return;
690
691 credman_reset_rp(rp);
692 free(rp);
693 *rp_p = NULL;
694}
695
696size_t
697fido_credman_rp_count(const fido_credman_rp_t *rp)
698{
699 return (rp->n_rx);
700}
701
702const char *
703fido_credman_rp_id(const fido_credman_rp_t *rp, size_t idx)
704{
705 if (idx >= rp->n_alloc)
706 return (NULL);
707
708 return (rp->ptr[idx].rp_entity.id);
709}
710
711const char *
712fido_credman_rp_name(const fido_credman_rp_t *rp, size_t idx)
713{
714 if (idx >= rp->n_alloc)
715 return (NULL);
716
717 return (rp->ptr[idx].rp_entity.name);
718}
719
720size_t
721fido_credman_rp_id_hash_len(const fido_credman_rp_t *rp, size_t idx)
722{
723 if (idx >= rp->n_alloc)
724 return (0);
725
726 return (rp->ptr[idx].rp_id_hash.len);
727}
728
729const unsigned char *
730fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *rp, size_t idx)
731{
732 if (idx >= rp->n_alloc)
733 return (NULL);
734
735 return (rp->ptr[idx].rp_id_hash.ptr);
736}
diff --git a/src/dev.c b/src/dev.c
new file mode 100644
index 0000000..d0efac7
--- /dev/null
+++ b/src/dev.c
@@ -0,0 +1,284 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <sys/types.h>
8#include <sys/stat.h>
9
10#include <fcntl.h>
11#include <stdint.h>
12#include <stdlib.h>
13#include <string.h>
14#ifdef HAVE_UNISTD_H
15#include <unistd.h>
16#endif
17
18#include "fido.h"
19
20#if defined(_WIN32)
21#include <windows.h>
22
23#include <winternl.h>
24#include <winerror.h>
25#include <stdio.h>
26#include <bcrypt.h>
27#include <sal.h>
28
29static int
30obtain_nonce(uint64_t *nonce)
31{
32 NTSTATUS status;
33
34 status = BCryptGenRandom(NULL, (unsigned char *)nonce, sizeof(*nonce),
35 BCRYPT_USE_SYSTEM_PREFERRED_RNG);
36
37 if (!NT_SUCCESS(status))
38 return (-1);
39
40 return (0);
41}
42#elif defined(HAS_DEV_URANDOM)
43static int
44obtain_nonce(uint64_t *nonce)
45{
46 int fd = -1;
47 int ok = -1;
48 ssize_t r;
49
50 if ((fd = open(FIDO_RANDOM_DEV, O_RDONLY)) < 0)
51 goto fail;
52 if ((r = read(fd, nonce, sizeof(*nonce))) < 0 ||
53 (size_t)r != sizeof(*nonce))
54 goto fail;
55
56 ok = 0;
57fail:
58 if (fd != -1)
59 close(fd);
60
61 return (ok);
62}
63#else
64#error "please provide an implementation of obtain_nonce() for your platform"
65#endif /* _WIN32 */
66
67static int
68fido_dev_open_tx(fido_dev_t *dev, const char *path)
69{
70 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_INIT;
71
72 if (dev->io_handle != NULL) {
73 fido_log_debug("%s: handle=%p", __func__, dev->io_handle);
74 return (FIDO_ERR_INVALID_ARGUMENT);
75 }
76
77 if (dev->io.open == NULL || dev->io.close == NULL) {
78 fido_log_debug("%s: NULL open/close", __func__);
79 return (FIDO_ERR_INVALID_ARGUMENT);
80 }
81
82 if (obtain_nonce(&dev->nonce) < 0) {
83 fido_log_debug("%s: obtain_nonce", __func__);
84 return (FIDO_ERR_INTERNAL);
85 }
86
87 if ((dev->io_handle = dev->io.open(path)) == NULL) {
88 fido_log_debug("%s: dev->io.open", __func__);
89 return (FIDO_ERR_INTERNAL);
90 }
91
92 if (fido_tx(dev, cmd, &dev->nonce, sizeof(dev->nonce)) < 0) {
93 fido_log_debug("%s: fido_tx", __func__);
94 dev->io.close(dev->io_handle);
95 dev->io_handle = NULL;
96 return (FIDO_ERR_TX);
97 }
98
99 return (FIDO_OK);
100}
101
102static int
103fido_dev_open_rx(fido_dev_t *dev, int ms)
104{
105 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_INIT;
106 int n;
107
108 if ((n = fido_rx(dev, cmd, &dev->attr, sizeof(dev->attr), ms)) < 0) {
109 fido_log_debug("%s: fido_rx", __func__);
110 goto fail;
111 }
112
113#ifdef FIDO_FUZZ
114 dev->attr.nonce = dev->nonce;
115#endif
116
117 if ((size_t)n != sizeof(dev->attr) || dev->attr.nonce != dev->nonce) {
118 fido_log_debug("%s: invalid nonce", __func__);
119 goto fail;
120 }
121
122 dev->cid = dev->attr.cid;
123
124 return (FIDO_OK);
125fail:
126 dev->io.close(dev->io_handle);
127 dev->io_handle = NULL;
128
129 return (FIDO_ERR_RX);
130}
131
132static int
133fido_dev_open_wait(fido_dev_t *dev, const char *path, int ms)
134{
135 int r;
136
137 if ((r = fido_dev_open_tx(dev, path)) != FIDO_OK ||
138 (r = fido_dev_open_rx(dev, ms)) != FIDO_OK)
139 return (r);
140
141 return (FIDO_OK);
142}
143
144int
145fido_dev_open(fido_dev_t *dev, const char *path)
146{
147 return (fido_dev_open_wait(dev, path, -1));
148}
149
150int
151fido_dev_close(fido_dev_t *dev)
152{
153 if (dev->io_handle == NULL || dev->io.close == NULL)
154 return (FIDO_ERR_INVALID_ARGUMENT);
155
156 dev->io.close(dev->io_handle);
157 dev->io_handle = NULL;
158
159 return (FIDO_OK);
160}
161
162int
163fido_dev_cancel(fido_dev_t *dev)
164{
165 if (fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CANCEL, NULL, 0) < 0)
166 return (FIDO_ERR_TX);
167
168 return (FIDO_OK);
169}
170
171int
172fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io)
173{
174 if (dev->io_handle != NULL) {
175 fido_log_debug("%s: NULL handle", __func__);
176 return (FIDO_ERR_INVALID_ARGUMENT);
177 }
178
179 if (io == NULL || io->open == NULL || io->close == NULL ||
180 io->read == NULL || io->write == NULL) {
181 fido_log_debug("%s: NULL function", __func__);
182 return (FIDO_ERR_INVALID_ARGUMENT);
183 }
184
185 dev->io.open = io->open;
186 dev->io.close = io->close;
187 dev->io.read = io->read;
188 dev->io.write = io->write;
189
190 return (FIDO_OK);
191}
192
193void
194fido_init(int flags)
195{
196 if (flags & FIDO_DEBUG || getenv("FIDO_DEBUG") != NULL)
197 fido_log_init();
198}
199
200fido_dev_t *
201fido_dev_new(void)
202{
203 fido_dev_t *dev;
204 fido_dev_io_t io;
205
206 if ((dev = calloc(1, sizeof(*dev))) == NULL)
207 return (NULL);
208
209 dev->cid = CTAP_CID_BROADCAST;
210
211 io.open = fido_hid_open;
212 io.close = fido_hid_close;
213 io.read = fido_hid_read;
214 io.write = fido_hid_write;
215
216 if (fido_dev_set_io_functions(dev, &io) != FIDO_OK) {
217 fido_log_debug("%s: fido_dev_set_io_functions", __func__);
218 fido_dev_free(&dev);
219 return (NULL);
220 }
221
222 return (dev);
223}
224
225void
226fido_dev_free(fido_dev_t **dev_p)
227{
228 fido_dev_t *dev;
229
230 if (dev_p == NULL || (dev = *dev_p) == NULL)
231 return;
232
233 free(dev);
234
235 *dev_p = NULL;
236}
237
238uint8_t
239fido_dev_protocol(const fido_dev_t *dev)
240{
241 return (dev->attr.protocol);
242}
243
244uint8_t
245fido_dev_major(const fido_dev_t *dev)
246{
247 return (dev->attr.major);
248}
249
250uint8_t
251fido_dev_minor(const fido_dev_t *dev)
252{
253 return (dev->attr.minor);
254}
255
256uint8_t
257fido_dev_build(const fido_dev_t *dev)
258{
259 return (dev->attr.build);
260}
261
262uint8_t
263fido_dev_flags(const fido_dev_t *dev)
264{
265 return (dev->attr.flags);
266}
267
268bool
269fido_dev_is_fido2(const fido_dev_t *dev)
270{
271 return (dev->attr.flags & FIDO_CAP_CBOR);
272}
273
274void
275fido_dev_force_u2f(fido_dev_t *dev)
276{
277 dev->attr.flags &= ~FIDO_CAP_CBOR;
278}
279
280void
281fido_dev_force_fido2(fido_dev_t *dev)
282{
283 dev->attr.flags |= FIDO_CAP_CBOR;
284}
diff --git a/src/diff_exports.sh b/src/diff_exports.sh
new file mode 100755
index 0000000..7920f47
--- /dev/null
+++ b/src/diff_exports.sh
@@ -0,0 +1,23 @@
1#!/bin/bash -u
2
3# Copyright (c) 2018 Yubico AB. All rights reserved.
4# Use of this source code is governed by a BSD-style
5# license that can be found in the LICENSE file.
6
7[[ ! -f export.gnu || ! -f export.llvm || ! -f export.msvc ]] && exit 1
8
9TMPDIR=$(mktemp -d)
10GNU=${TMPDIR}/gnu
11LLVM=${TMPDIR}/llvm
12MSVC=${TMPDIR}/msvc
13
14egrep -o $'([^*{}\t]+);$' export.gnu | tr -d ';' | sort > ${GNU}
15sed 's/^_//g' export.llvm | sort > ${LLVM}
16egrep -v "^EXPORTS$" export.msvc | sort > ${MSVC}
17diff -u ${GNU} ${LLVM} && diff -u ${MSVC} ${LLVM}
18ERROR=$?
19
20rm ${GNU} ${LLVM} ${MSVC}
21rmdir ${TMPDIR}
22
23exit ${ERROR}
diff --git a/src/ecdh.c b/src/ecdh.c
new file mode 100644
index 0000000..7f25c7b
--- /dev/null
+++ b/src/ecdh.c
@@ -0,0 +1,121 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/evp.h>
8#include <openssl/sha.h>
9
10#include "fido.h"
11#include "fido/es256.h"
12
13static int
14do_ecdh(const es256_sk_t *sk, const es256_pk_t *pk, fido_blob_t **ecdh)
15{
16 EVP_PKEY *pk_evp = NULL;
17 EVP_PKEY *sk_evp = NULL;
18 EVP_PKEY_CTX *ctx = NULL;
19 fido_blob_t *secret = NULL;
20 int ok = -1;
21
22 *ecdh = NULL;
23
24 /* allocate blobs for secret & ecdh */
25 if ((secret = fido_blob_new()) == NULL ||
26 (*ecdh = fido_blob_new()) == NULL)
27 goto fail;
28
29 /* wrap the keys as openssl objects */
30 if ((pk_evp = es256_pk_to_EVP_PKEY(pk)) == NULL ||
31 (sk_evp = es256_sk_to_EVP_PKEY(sk)) == NULL) {
32 fido_log_debug("%s: es256_to_EVP_PKEY", __func__);
33 goto fail;
34 }
35
36 /* set ecdh parameters */
37 if ((ctx = EVP_PKEY_CTX_new(sk_evp, NULL)) == NULL ||
38 EVP_PKEY_derive_init(ctx) <= 0 ||
39 EVP_PKEY_derive_set_peer(ctx, pk_evp) <= 0) {
40 fido_log_debug("%s: EVP_PKEY_derive_init", __func__);
41 goto fail;
42 }
43
44 /* perform ecdh */
45 if (EVP_PKEY_derive(ctx, NULL, &secret->len) <= 0 ||
46 (secret->ptr = calloc(1, secret->len)) == NULL ||
47 EVP_PKEY_derive(ctx, secret->ptr, &secret->len) <= 0) {
48 fido_log_debug("%s: EVP_PKEY_derive", __func__);
49 goto fail;
50 }
51
52 /* use sha256 as a kdf on the resulting secret */
53 (*ecdh)->len = SHA256_DIGEST_LENGTH;
54 if (((*ecdh)->ptr = calloc(1, (*ecdh)->len)) == NULL ||
55 SHA256(secret->ptr, secret->len, (*ecdh)->ptr) != (*ecdh)->ptr) {
56 fido_log_debug("%s: sha256", __func__);
57 goto fail;
58 }
59
60 ok = 0;
61fail:
62 if (pk_evp != NULL)
63 EVP_PKEY_free(pk_evp);
64 if (sk_evp != NULL)
65 EVP_PKEY_free(sk_evp);
66 if (ctx != NULL)
67 EVP_PKEY_CTX_free(ctx);
68 if (ok < 0)
69 fido_blob_free(ecdh);
70
71 fido_blob_free(&secret);
72
73 return (ok);
74}
75
76int
77fido_do_ecdh(fido_dev_t *dev, es256_pk_t **pk, fido_blob_t **ecdh)
78{
79 es256_sk_t *sk = NULL; /* our private key */
80 es256_pk_t *ak = NULL; /* authenticator's public key */
81 int r;
82
83 *pk = NULL; /* our public key; returned */
84 *ecdh = NULL; /* shared ecdh secret; returned */
85
86 if ((sk = es256_sk_new()) == NULL || (*pk = es256_pk_new()) == NULL) {
87 r = FIDO_ERR_INTERNAL;
88 goto fail;
89 }
90
91 if (es256_sk_create(sk) < 0 || es256_derive_pk(sk, *pk) < 0) {
92 fido_log_debug("%s: es256_derive_pk", __func__);
93 r = FIDO_ERR_INTERNAL;
94 goto fail;
95 }
96
97 if ((ak = es256_pk_new()) == NULL ||
98 fido_dev_authkey(dev, ak) != FIDO_OK) {
99 fido_log_debug("%s: fido_dev_authkey", __func__);
100 r = FIDO_ERR_INTERNAL;
101 goto fail;
102 }
103
104 if (do_ecdh(sk, ak, ecdh) < 0) {
105 fido_log_debug("%s: do_ecdh", __func__);
106 r = FIDO_ERR_INTERNAL;
107 goto fail;
108 }
109
110 r = FIDO_OK;
111fail:
112 es256_sk_free(&sk);
113 es256_pk_free(&ak);
114
115 if (r != FIDO_OK) {
116 es256_pk_free(pk);
117 fido_blob_free(ecdh);
118 }
119
120 return (r);
121}
diff --git a/src/eddsa.c b/src/eddsa.c
new file mode 100644
index 0000000..92a0222
--- /dev/null
+++ b/src/eddsa.c
@@ -0,0 +1,169 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/bn.h>
8#include <openssl/ec.h>
9#include <openssl/evp.h>
10#include <openssl/obj_mac.h>
11
12#include <string.h>
13#include "fido.h"
14#include "fido/eddsa.h"
15
16#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10101000L
17EVP_PKEY *
18EVP_PKEY_new_raw_public_key(int type, ENGINE *e, const unsigned char *key,
19 size_t keylen)
20{
21 (void)type;
22 (void)e;
23 (void)key;
24 (void)keylen;
25
26 return (NULL);
27}
28
29int
30EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub,
31 size_t *len)
32{
33 (void)pkey;
34 (void)pub;
35 (void)len;
36
37 return (0);
38}
39
40int
41EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, size_t siglen,
42 const unsigned char *tbs, size_t tbslen)
43{
44 (void)ctx;
45 (void)sigret;
46 (void)siglen;
47 (void)tbs;
48 (void)tbslen;
49
50 return (0);
51}
52#endif /* LIBRESSL_VERSION_NUMBER || OPENSSL_VERSION_NUMBER < 0x10101000L */
53
54#if OPENSSL_VERSION_NUMBER < 0x10100000L
55EVP_MD_CTX *
56EVP_MD_CTX_new(void)
57{
58 return (NULL);
59}
60
61void
62EVP_MD_CTX_free(EVP_MD_CTX *ctx)
63{
64 (void)ctx;
65}
66#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
67
68static int
69decode_coord(const cbor_item_t *item, void *xy, size_t xy_len)
70{
71 if (cbor_isa_bytestring(item) == false ||
72 cbor_bytestring_is_definite(item) == false ||
73 cbor_bytestring_length(item) != xy_len) {
74 fido_log_debug("%s: cbor type", __func__);
75 return (-1);
76 }
77
78 memcpy(xy, cbor_bytestring_handle(item), xy_len);
79
80 return (0);
81}
82
83static int
84decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg)
85{
86 eddsa_pk_t *k = arg;
87
88 if (cbor_isa_negint(key) == false ||
89 cbor_int_get_width(key) != CBOR_INT_8)
90 return (0); /* ignore */
91
92 switch (cbor_get_uint8(key)) {
93 case 1: /* x coordinate */
94 return (decode_coord(val, &k->x, sizeof(k->x)));
95 }
96
97 return (0); /* ignore */
98}
99
100int
101eddsa_pk_decode(const cbor_item_t *item, eddsa_pk_t *k)
102{
103 if (cbor_isa_map(item) == false ||
104 cbor_map_is_definite(item) == false ||
105 cbor_map_iter(item, k, decode_pubkey_point) < 0) {
106 fido_log_debug("%s: cbor type", __func__);
107 return (-1);
108 }
109
110 return (0);
111}
112
113eddsa_pk_t *
114eddsa_pk_new(void)
115{
116 return (calloc(1, sizeof(eddsa_pk_t)));
117}
118
119void
120eddsa_pk_free(eddsa_pk_t **pkp)
121{
122 eddsa_pk_t *pk;
123
124 if (pkp == NULL || (pk = *pkp) == NULL)
125 return;
126
127 explicit_bzero(pk, sizeof(*pk));
128 free(pk);
129
130 *pkp = NULL;
131}
132
133int
134eddsa_pk_from_ptr(eddsa_pk_t *pk, const void *ptr, size_t len)
135{
136 if (len < sizeof(*pk))
137 return (FIDO_ERR_INVALID_ARGUMENT);
138
139 memcpy(pk, ptr, sizeof(*pk));
140
141 return (FIDO_OK);
142}
143
144EVP_PKEY *
145eddsa_pk_to_EVP_PKEY(const eddsa_pk_t *k)
146{
147 EVP_PKEY *pkey = NULL;
148
149 if ((pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, k->x,
150 sizeof(k->x))) == NULL)
151 fido_log_debug("%s: EVP_PKEY_new_raw_public_key", __func__);
152
153 return (pkey);
154}
155
156int
157eddsa_pk_from_EVP_PKEY(eddsa_pk_t *pk, const EVP_PKEY *pkey)
158{
159 size_t len = 0;
160
161 if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1 ||
162 len != sizeof(pk->x))
163 return (FIDO_ERR_INTERNAL);
164 if (EVP_PKEY_get_raw_public_key(pkey, pk->x, &len) != 1 ||
165 len != sizeof(pk->x))
166 return (FIDO_ERR_INTERNAL);
167
168 return (FIDO_OK);
169}
diff --git a/src/err.c b/src/err.c
new file mode 100644
index 0000000..5d3efd4
--- /dev/null
+++ b/src/err.c
@@ -0,0 +1,122 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include "fido/err.h"
8
9const char *
10fido_strerr(int n)
11{
12 switch (n) {
13 case FIDO_ERR_SUCCESS:
14 return "FIDO_ERR_SUCCESS";
15 case FIDO_ERR_INVALID_COMMAND:
16 return "FIDO_ERR_INVALID_COMMAND";
17 case FIDO_ERR_INVALID_PARAMETER:
18 return "FIDO_ERR_INVALID_PARAMETER";
19 case FIDO_ERR_INVALID_LENGTH:
20 return "FIDO_ERR_INVALID_LENGTH";
21 case FIDO_ERR_INVALID_SEQ:
22 return "FIDO_ERR_INVALID_SEQ";
23 case FIDO_ERR_TIMEOUT:
24 return "FIDO_ERR_TIMEOUT";
25 case FIDO_ERR_CHANNEL_BUSY:
26 return "FIDO_ERR_CHANNEL_BUSY";
27 case FIDO_ERR_LOCK_REQUIRED:
28 return "FIDO_ERR_LOCK_REQUIRED";
29 case FIDO_ERR_INVALID_CHANNEL:
30 return "FIDO_ERR_INVALID_CHANNEL";
31 case FIDO_ERR_CBOR_UNEXPECTED_TYPE:
32 return "FIDO_ERR_UNEXPECTED_TYPE";
33 case FIDO_ERR_INVALID_CBOR:
34 return "FIDO_ERR_INVALID_CBOR";
35 case FIDO_ERR_MISSING_PARAMETER:
36 return "FIDO_ERR_MISSING_PARAMETER";
37 case FIDO_ERR_LIMIT_EXCEEDED:
38 return "FIDO_ERR_LIMIT_EXCEEDED";
39 case FIDO_ERR_UNSUPPORTED_EXTENSION:
40 return "FIDO_ERR_UNSUPPORTED_EXTENSION";
41 case FIDO_ERR_CREDENTIAL_EXCLUDED:
42 return "FIDO_ERR_CREDENTIAL_EXCLUDED";
43 case FIDO_ERR_PROCESSING:
44 return "FIDO_ERR_PROCESSING";
45 case FIDO_ERR_INVALID_CREDENTIAL:
46 return "FIDO_ERR_INVALID_CREDENTIAL";
47 case FIDO_ERR_USER_ACTION_PENDING:
48 return "FIDO_ERR_ACTION_PENDING";
49 case FIDO_ERR_OPERATION_PENDING:
50 return "FIDO_ERR_OPERATION_PENDING";
51 case FIDO_ERR_NO_OPERATIONS:
52 return "FIDO_ERR_NO_OPERATIONS";
53 case FIDO_ERR_UNSUPPORTED_ALGORITHM:
54 return "FIDO_ERR_UNSUPPORTED_ALGORITHM";
55 case FIDO_ERR_OPERATION_DENIED:
56 return "FIDO_ERR_OPERATION_DENIED";
57 case FIDO_ERR_KEY_STORE_FULL:
58 return "FIDO_ERR_STORE_FULL";
59 case FIDO_ERR_NOT_BUSY:
60 return "FIDO_ERR_NOT_BUSY";
61 case FIDO_ERR_NO_OPERATION_PENDING:
62 return "FIDO_ERR_OPERATION_PENDING";
63 case FIDO_ERR_UNSUPPORTED_OPTION:
64 return "FIDO_ERR_UNSUPPORTED_OPTION";
65 case FIDO_ERR_INVALID_OPTION:
66 return "FIDO_ERR_INVALID_OPTION";
67 case FIDO_ERR_KEEPALIVE_CANCEL:
68 return "FIDO_ERR_KEEPALIVE_CANCEL";
69 case FIDO_ERR_NO_CREDENTIALS:
70 return "FIDO_ERR_NO_CREDENTIALS";
71 case FIDO_ERR_USER_ACTION_TIMEOUT:
72 return "FIDO_ERR_ACTION_TIMEOUT";
73 case FIDO_ERR_NOT_ALLOWED:
74 return "FIDO_ERR_NOT_ALLOWED";
75 case FIDO_ERR_PIN_INVALID:
76 return "FIDO_ERR_PIN_INVALID";
77 case FIDO_ERR_PIN_BLOCKED:
78 return "FIDO_ERR_PIN_BLOCKED";
79 case FIDO_ERR_PIN_AUTH_INVALID:
80 return "FIDO_ERR_AUTH_INVALID";
81 case FIDO_ERR_PIN_AUTH_BLOCKED:
82 return "FIDO_ERR_AUTH_BLOCKED";
83 case FIDO_ERR_PIN_NOT_SET:
84 return "FIDO_ERR_NOT_SET";
85 case FIDO_ERR_PIN_REQUIRED:
86 return "FIDO_ERR_PIN_REQUIRED";
87 case FIDO_ERR_PIN_POLICY_VIOLATION:
88 return "FIDO_ERR_POLICY_VIOLATION";
89 case FIDO_ERR_PIN_TOKEN_EXPIRED:
90 return "FIDO_ERR_TOKEN_EXPIRED";
91 case FIDO_ERR_REQUEST_TOO_LARGE:
92 return "FIDO_ERR_TOO_LARGE";
93 case FIDO_ERR_ACTION_TIMEOUT:
94 return "FIDO_ERR_ACTION_TIMEOUT";
95 case FIDO_ERR_UP_REQUIRED:
96 return "FIDO_ERR_UP_REQUIRED";
97 case FIDO_ERR_ERR_OTHER:
98 return "FIDO_ERR_OTHER";
99 case FIDO_ERR_SPEC_LAST:
100 return "FIDO_ERR_SPEC_LAST";
101 case FIDO_ERR_TX:
102 return "FIDO_ERR_TX";
103 case FIDO_ERR_RX:
104 return "FIDO_ERR_RX";
105 case FIDO_ERR_RX_NOT_CBOR:
106 return "FIDO_ERR_RX_NOT_CBOR";
107 case FIDO_ERR_RX_INVALID_CBOR:
108 return "FIDO_ERR_RX_INVALID_CBOR";
109 case FIDO_ERR_INVALID_PARAM:
110 return "FIDO_ERR_INVALID_PARAM";
111 case FIDO_ERR_INVALID_SIG:
112 return "FIDO_ERR_INVALID_SIG";
113 case FIDO_ERR_INVALID_ARGUMENT:
114 return "FIDO_ERR_INVALID_ARGUMENT";
115 case FIDO_ERR_USER_PRESENCE_REQUIRED:
116 return "FIDO_ERR_USER_PRESENCE_REQUIRED";
117 case FIDO_ERR_INTERNAL:
118 return "FIDO_ERR_INTERNAL";
119 default:
120 return "FIDO_ERR_UNKNOWN";
121 }
122}
diff --git a/src/es256.c b/src/es256.c
new file mode 100644
index 0000000..c8fd9f4
--- /dev/null
+++ b/src/es256.c
@@ -0,0 +1,434 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/bn.h>
8#include <openssl/ec.h>
9#include <openssl/evp.h>
10#include <openssl/obj_mac.h>
11
12#include <string.h>
13#include "fido.h"
14#include "fido/es256.h"
15
16static int
17decode_coord(const cbor_item_t *item, void *xy, size_t xy_len)
18{
19 if (cbor_isa_bytestring(item) == false ||
20 cbor_bytestring_is_definite(item) == false ||
21 cbor_bytestring_length(item) != xy_len) {
22 fido_log_debug("%s: cbor type", __func__);
23 return (-1);
24 }
25
26 memcpy(xy, cbor_bytestring_handle(item), xy_len);
27
28 return (0);
29}
30
31static int
32decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg)
33{
34 es256_pk_t *k = arg;
35
36 if (cbor_isa_negint(key) == false ||
37 cbor_int_get_width(key) != CBOR_INT_8)
38 return (0); /* ignore */
39
40 switch (cbor_get_uint8(key)) {
41 case 1: /* x coordinate */
42 return (decode_coord(val, &k->x, sizeof(k->x)));
43 case 2: /* y coordinate */
44 return (decode_coord(val, &k->y, sizeof(k->y)));
45 }
46
47 return (0); /* ignore */
48}
49
50int
51es256_pk_decode(const cbor_item_t *item, es256_pk_t *k)
52{
53 if (cbor_isa_map(item) == false ||
54 cbor_map_is_definite(item) == false ||
55 cbor_map_iter(item, k, decode_pubkey_point) < 0) {
56 fido_log_debug("%s: cbor type", __func__);
57 return (-1);
58 }
59
60 return (0);
61}
62
63cbor_item_t *
64es256_pk_encode(const es256_pk_t *pk, int ecdh)
65{
66 cbor_item_t *item = NULL;
67 struct cbor_pair argv[5];
68 int alg;
69 int ok = -1;
70
71 memset(argv, 0, sizeof(argv));
72
73 if ((item = cbor_new_definite_map(5)) == NULL)
74 goto fail;
75
76 /* kty */
77 if ((argv[0].key = cbor_build_uint8(1)) == NULL ||
78 (argv[0].value = cbor_build_uint8(2)) == NULL ||
79 !cbor_map_add(item, argv[0]))
80 goto fail;
81
82 /*
83 * "The COSEAlgorithmIdentifier used is -25 (ECDH-ES +
84 * HKDF-256) although this is NOT the algorithm actually
85 * used. Setting this to a different value may result in
86 * compatibility issues."
87 */
88 if (ecdh)
89 alg = COSE_ECDH_ES256;
90 else
91 alg = COSE_ES256;
92
93 /* alg */
94 if ((argv[1].key = cbor_build_uint8(3)) == NULL ||
95 (argv[1].value = cbor_build_negint8(-alg - 1)) == NULL ||
96 !cbor_map_add(item, argv[1]))
97 goto fail;
98
99 /* crv */
100 if ((argv[2].key = cbor_build_negint8(0)) == NULL ||
101 (argv[2].value = cbor_build_uint8(1)) == NULL ||
102 !cbor_map_add(item, argv[2]))
103 goto fail;
104
105 /* x */
106 if ((argv[3].key = cbor_build_negint8(1)) == NULL ||
107 (argv[3].value = cbor_build_bytestring(pk->x,
108 sizeof(pk->x))) == NULL || !cbor_map_add(item, argv[3]))
109 goto fail;
110
111 /* y */
112 if ((argv[4].key = cbor_build_negint8(2)) == NULL ||
113 (argv[4].value = cbor_build_bytestring(pk->y,
114 sizeof(pk->y))) == NULL || !cbor_map_add(item, argv[4]))
115 goto fail;
116
117 ok = 0;
118fail:
119 if (ok < 0) {
120 if (item != NULL) {
121 cbor_decref(&item);
122 item = NULL;
123 }
124 }
125
126 for (size_t i = 0; i < 5; i++) {
127 if (argv[i].key)
128 cbor_decref(&argv[i].key);
129 if (argv[i].value)
130 cbor_decref(&argv[i].value);
131 }
132
133 return (item);
134}
135
136es256_sk_t *
137es256_sk_new(void)
138{
139 return (calloc(1, sizeof(es256_sk_t)));
140}
141
142void
143es256_sk_free(es256_sk_t **skp)
144{
145 es256_sk_t *sk;
146
147 if (skp == NULL || (sk = *skp) == NULL)
148 return;
149
150 explicit_bzero(sk, sizeof(*sk));
151 free(sk);
152
153 *skp = NULL;
154}
155
156es256_pk_t *
157es256_pk_new(void)
158{
159 return (calloc(1, sizeof(es256_pk_t)));
160}
161
162void
163es256_pk_free(es256_pk_t **pkp)
164{
165 es256_pk_t *pk;
166
167 if (pkp == NULL || (pk = *pkp) == NULL)
168 return;
169
170 explicit_bzero(pk, sizeof(*pk));
171 free(pk);
172
173 *pkp = NULL;
174}
175
176int
177es256_pk_from_ptr(es256_pk_t *pk, const void *ptr, size_t len)
178{
179 if (len < sizeof(*pk))
180 return (FIDO_ERR_INVALID_ARGUMENT);
181
182 memcpy(pk, ptr, sizeof(*pk));
183
184 return (FIDO_OK);
185}
186
187int
188es256_pk_set_x(es256_pk_t *pk, const unsigned char *x)
189{
190 memcpy(pk->x, x, sizeof(pk->x));
191
192 return (0);
193}
194
195int
196es256_pk_set_y(es256_pk_t *pk, const unsigned char *y)
197{
198 memcpy(pk->y, y, sizeof(pk->y));
199
200 return (0);
201}
202
203int
204es256_sk_create(es256_sk_t *key)
205{
206 EVP_PKEY_CTX *pctx = NULL;
207 EVP_PKEY_CTX *kctx = NULL;
208 EVP_PKEY *p = NULL;
209 EVP_PKEY *k = NULL;
210 const EC_KEY *ec;
211 const BIGNUM *d;
212 const int nid = NID_X9_62_prime256v1;
213 int n;
214 int ok = -1;
215
216 if ((pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) == NULL ||
217 EVP_PKEY_paramgen_init(pctx) <= 0 ||
218 EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) <= 0 ||
219 EVP_PKEY_paramgen(pctx, &p) <= 0) {
220 fido_log_debug("%s: EVP_PKEY_paramgen", __func__);
221 goto fail;
222 }
223
224 if ((kctx = EVP_PKEY_CTX_new(p, NULL)) == NULL ||
225 EVP_PKEY_keygen_init(kctx) <= 0 || EVP_PKEY_keygen(kctx, &k) <= 0) {
226 fido_log_debug("%s: EVP_PKEY_keygen", __func__);
227 goto fail;
228 }
229
230 if ((ec = EVP_PKEY_get0_EC_KEY(k)) == NULL ||
231 (d = EC_KEY_get0_private_key(ec)) == NULL ||
232 (n = BN_num_bytes(d)) < 0 || (size_t)n > sizeof(key->d) ||
233 (n = BN_bn2bin(d, key->d)) < 0 || (size_t)n > sizeof(key->d)) {
234 fido_log_debug("%s: EC_KEY_get0_private_key", __func__);
235 goto fail;
236 }
237
238 ok = 0;
239fail:
240 if (p != NULL)
241 EVP_PKEY_free(p);
242 if (k != NULL)
243 EVP_PKEY_free(k);
244 if (pctx != NULL)
245 EVP_PKEY_CTX_free(pctx);
246 if (kctx != NULL)
247 EVP_PKEY_CTX_free(kctx);
248
249 return (ok);
250}
251
252EVP_PKEY *
253es256_pk_to_EVP_PKEY(const es256_pk_t *k)
254{
255 BN_CTX *bnctx = NULL;
256 EC_KEY *ec = NULL;
257 EC_POINT *q = NULL;
258 EVP_PKEY *pkey = NULL;
259 BIGNUM *x = NULL;
260 BIGNUM *y = NULL;
261 const EC_GROUP *g = NULL;
262 const int nid = NID_X9_62_prime256v1;
263 int ok = -1;
264
265 if ((bnctx = BN_CTX_new()) == NULL ||
266 (x = BN_CTX_get(bnctx)) == NULL ||
267 (y = BN_CTX_get(bnctx)) == NULL)
268 goto fail;
269
270 if (BN_bin2bn(k->x, sizeof(k->x), x) == NULL ||
271 BN_bin2bn(k->y, sizeof(k->y), y) == NULL) {
272 fido_log_debug("%s: BN_bin2bn", __func__);
273 goto fail;
274 }
275
276 if ((ec = EC_KEY_new_by_curve_name(nid)) == NULL ||
277 (g = EC_KEY_get0_group(ec)) == NULL) {
278 fido_log_debug("%s: EC_KEY init", __func__);
279 goto fail;
280 }
281
282 if ((q = EC_POINT_new(g)) == NULL ||
283 EC_POINT_set_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 ||
284 EC_KEY_set_public_key(ec, q) == 0) {
285 fido_log_debug("%s: EC_KEY_set_public_key", __func__);
286 goto fail;
287 }
288
289 if ((pkey = EVP_PKEY_new()) == NULL ||
290 EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) {
291 fido_log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__);
292 goto fail;
293 }
294
295 ec = NULL; /* at this point, ec belongs to evp */
296
297 ok = 0;
298fail:
299 if (bnctx != NULL)
300 BN_CTX_free(bnctx);
301 if (ec != NULL)
302 EC_KEY_free(ec);
303 if (q != NULL)
304 EC_POINT_free(q);
305 if (ok < 0 && pkey != NULL) {
306 EVP_PKEY_free(pkey);
307 pkey = NULL;
308 }
309
310 return (pkey);
311}
312
313int
314es256_pk_from_EC_KEY(es256_pk_t *pk, const EC_KEY *ec)
315{
316 BN_CTX *ctx = NULL;
317 BIGNUM *x = NULL;
318 BIGNUM *y = NULL;
319 const EC_POINT *q = NULL;
320 const EC_GROUP *g = NULL;
321 int ok = FIDO_ERR_INTERNAL;
322 int n;
323
324 if ((q = EC_KEY_get0_public_key(ec)) == NULL ||
325 (g = EC_KEY_get0_group(ec)) == NULL)
326 goto fail;
327
328 if ((ctx = BN_CTX_new()) == NULL ||
329 (x = BN_CTX_get(ctx)) == NULL ||
330 (y = BN_CTX_get(ctx)) == NULL)
331 goto fail;
332
333 if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, ctx) == 0 ||
334 (n = BN_num_bytes(x)) < 0 || (size_t)n > sizeof(pk->x) ||
335 (n = BN_num_bytes(y)) < 0 || (size_t)n > sizeof(pk->y)) {
336 fido_log_debug("%s: EC_POINT_get_affine_coordinates_GFp",
337 __func__);
338 goto fail;
339 }
340
341 if ((n = BN_bn2bin(x, pk->x)) < 0 || (size_t)n > sizeof(pk->x) ||
342 (n = BN_bn2bin(y, pk->y)) < 0 || (size_t)n > sizeof(pk->y)) {
343 fido_log_debug("%s: BN_bn2bin", __func__);
344 goto fail;
345 }
346
347 ok = FIDO_OK;
348fail:
349 if (ctx != NULL)
350 BN_CTX_free(ctx);
351
352 return (ok);
353}
354
355EVP_PKEY *
356es256_sk_to_EVP_PKEY(const es256_sk_t *k)
357{
358 BN_CTX *bnctx = NULL;
359 EC_KEY *ec = NULL;
360 EVP_PKEY *pkey = NULL;
361 BIGNUM *d = NULL;
362 const int nid = NID_X9_62_prime256v1;
363 int ok = -1;
364
365 if ((bnctx = BN_CTX_new()) == NULL || (d = BN_CTX_get(bnctx)) == NULL ||
366 BN_bin2bn(k->d, sizeof(k->d), d) == NULL) {
367 fido_log_debug("%s: BN_bin2bn", __func__);
368 goto fail;
369 }
370
371 if ((ec = EC_KEY_new_by_curve_name(nid)) == NULL ||
372 EC_KEY_set_private_key(ec, d) == 0) {
373 fido_log_debug("%s: EC_KEY_set_private_key", __func__);
374 goto fail;
375 }
376
377 if ((pkey = EVP_PKEY_new()) == NULL ||
378 EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) {
379 fido_log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__);
380 goto fail;
381 }
382
383 ec = NULL; /* at this point, ec belongs to evp */
384
385 ok = 0;
386fail:
387 if (bnctx != NULL)
388 BN_CTX_free(bnctx);
389 if (ec != NULL)
390 EC_KEY_free(ec);
391 if (ok < 0 && pkey != NULL) {
392 EVP_PKEY_free(pkey);
393 pkey = NULL;
394 }
395
396 return (pkey);
397}
398
399int
400es256_derive_pk(const es256_sk_t *sk, es256_pk_t *pk)
401{
402 BIGNUM *d = NULL;
403 EC_KEY *ec = NULL;
404 EC_POINT *q = NULL;
405 const EC_GROUP *g = NULL;
406 const int nid = NID_X9_62_prime256v1;
407 int ok = -1;
408
409 if ((d = BN_bin2bn(sk->d, (int)sizeof(sk->d), NULL)) == NULL ||
410 (ec = EC_KEY_new_by_curve_name(nid)) == NULL ||
411 (g = EC_KEY_get0_group(ec)) == NULL ||
412 (q = EC_POINT_new(g)) == NULL) {
413 fido_log_debug("%s: get", __func__);
414 goto fail;
415 }
416
417 if (EC_POINT_mul(g, q, d, NULL, NULL, NULL) == 0 ||
418 EC_KEY_set_public_key(ec, q) == 0 ||
419 es256_pk_from_EC_KEY(pk, ec) != FIDO_OK) {
420 fido_log_debug("%s: set", __func__);
421 goto fail;
422 }
423
424 ok = 0;
425fail:
426 if (d != NULL)
427 BN_clear_free(d);
428 if (q != NULL)
429 EC_POINT_free(q);
430 if (ec != NULL)
431 EC_KEY_free(ec);
432
433 return (ok);
434}
diff --git a/src/export.gnu b/src/export.gnu
new file mode 100644
index 0000000..f111e33
--- /dev/null
+++ b/src/export.gnu
@@ -0,0 +1,183 @@
1{
2 global:
3 eddsa_pk_free;
4 eddsa_pk_from_EVP_PKEY;
5 eddsa_pk_from_ptr;
6 eddsa_pk_new;
7 eddsa_pk_to_EVP_PKEY;
8 es256_pk_free;
9 es256_pk_from_EC_KEY;
10 es256_pk_from_ptr;
11 es256_pk_new;
12 es256_pk_to_EVP_PKEY;
13 fido_assert_allow_cred;
14 fido_assert_authdata_len;
15 fido_assert_authdata_ptr;
16 fido_assert_clientdata_hash_len;
17 fido_assert_clientdata_hash_ptr;
18 fido_assert_count;
19 fido_assert_flags;
20 fido_assert_free;
21 fido_assert_hmac_secret_len;
22 fido_assert_hmac_secret_ptr;
23 fido_assert_id_len;
24 fido_assert_id_ptr;
25 fido_assert_new;
26 fido_assert_rp_id;
27 fido_assert_set_authdata;
28 fido_assert_set_authdata_raw;
29 fido_assert_set_clientdata_hash;
30 fido_assert_set_count;
31 fido_assert_set_extensions;
32 fido_assert_set_hmac_salt;
33 fido_assert_set_options;
34 fido_assert_set_rp;
35 fido_assert_set_sig;
36 fido_assert_set_up;
37 fido_assert_set_uv;
38 fido_assert_sigcount;
39 fido_assert_sig_len;
40 fido_assert_sig_ptr;
41 fido_assert_user_display_name;
42 fido_assert_user_icon;
43 fido_assert_user_id_len;
44 fido_assert_user_id_ptr;
45 fido_assert_user_name;
46 fido_assert_verify;
47 fido_bio_dev_enroll_begin;
48 fido_bio_dev_enroll_cancel;
49 fido_bio_dev_enroll_continue;
50 fido_bio_dev_enroll_remove;
51 fido_bio_dev_get_info;
52 fido_bio_dev_get_template_array;
53 fido_bio_dev_set_template_name;
54 fido_bio_enroll_free;
55 fido_bio_enroll_last_status;
56 fido_bio_enroll_new;
57 fido_bio_enroll_remaining_samples;
58 fido_bio_info_free;
59 fido_bio_info_max_samples;
60 fido_bio_info_new;
61 fido_bio_info_type;
62 fido_bio_template;
63 fido_bio_template_array_count;
64 fido_bio_template_array_free;
65 fido_bio_template_array_new;
66 fido_bio_template_free;
67 fido_bio_template_id_len;
68 fido_bio_template_id_ptr;
69 fido_bio_template_name;
70 fido_bio_template_new;
71 fido_bio_template_set_id;
72 fido_bio_template_set_name;
73 fido_cbor_info_aaguid_len;
74 fido_cbor_info_aaguid_ptr;
75 fido_cbor_info_extensions_len;
76 fido_cbor_info_extensions_ptr;
77 fido_cbor_info_free;
78 fido_cbor_info_maxmsgsiz;
79 fido_cbor_info_new;
80 fido_cbor_info_options_len;
81 fido_cbor_info_options_name_ptr;
82 fido_cbor_info_options_value_ptr;
83 fido_cbor_info_protocols_len;
84 fido_cbor_info_protocols_ptr;
85 fido_cbor_info_versions_len;
86 fido_cbor_info_versions_ptr;
87 fido_cred_authdata_len;
88 fido_cred_authdata_ptr;
89 fido_cred_clientdata_hash_len;
90 fido_cred_clientdata_hash_ptr;
91 fido_cred_display_name;
92 fido_cred_exclude;
93 fido_cred_flags;
94 fido_cred_fmt;
95 fido_cred_free;
96 fido_cred_id_len;
97 fido_cred_id_ptr;
98 fido_credman_del_dev_rk;
99 fido_credman_get_dev_metadata;
100 fido_credman_get_dev_rk;
101 fido_credman_get_dev_rp;
102 fido_credman_metadata_free;
103 fido_credman_metadata_new;
104 fido_credman_rk;
105 fido_credman_rk_count;
106 fido_credman_rk_existing;
107 fido_credman_rk_free;
108 fido_credman_rk_new;
109 fido_credman_rk_remaining;
110 fido_credman_rp_count;
111 fido_credman_rp_free;
112 fido_credman_rp_id;
113 fido_credman_rp_id_hash_len;
114 fido_credman_rp_id_hash_ptr;
115 fido_credman_rp_name;
116 fido_credman_rp_new;
117 fido_cred_new;
118 fido_cred_pubkey_len;
119 fido_cred_pubkey_ptr;
120 fido_cred_rp_id;
121 fido_cred_rp_name;
122 fido_cred_set_authdata;
123 fido_cred_set_authdata_raw;
124 fido_cred_set_clientdata_hash;
125 fido_cred_set_extensions;
126 fido_cred_set_fmt;
127 fido_cred_set_options;
128 fido_cred_set_rk;
129 fido_cred_set_rp;
130 fido_cred_set_sig;
131 fido_cred_set_type;
132 fido_cred_set_user;
133 fido_cred_set_uv;
134 fido_cred_set_x509;
135 fido_cred_sig_len;
136 fido_cred_sig_ptr;
137 fido_cred_type;
138 fido_cred_user_id_len;
139 fido_cred_user_id_ptr;
140 fido_cred_user_name;
141 fido_cred_verify;
142 fido_cred_verify_self;
143 fido_cred_x5c_len;
144 fido_cred_x5c_ptr;
145 fido_dev_build;
146 fido_dev_cancel;
147 fido_dev_close;
148 fido_dev_flags;
149 fido_dev_force_fido2;
150 fido_dev_force_u2f;
151 fido_dev_free;
152 fido_dev_get_assert;
153 fido_dev_get_cbor_info;
154 fido_dev_get_retry_count;
155 fido_dev_info_free;
156 fido_dev_info_manifest;
157 fido_dev_info_manufacturer_string;
158 fido_dev_info_new;
159 fido_dev_info_path;
160 fido_dev_info_product;
161 fido_dev_info_product_string;
162 fido_dev_info_ptr;
163 fido_dev_info_vendor;
164 fido_dev_is_fido2;
165 fido_dev_major;
166 fido_dev_make_cred;
167 fido_dev_minor;
168 fido_dev_new;
169 fido_dev_open;
170 fido_dev_protocol;
171 fido_dev_reset;
172 fido_dev_set_io_functions;
173 fido_dev_set_pin;
174 fido_init;
175 fido_strerr;
176 rs256_pk_free;
177 rs256_pk_from_ptr;
178 rs256_pk_from_RSA;
179 rs256_pk_new;
180 rs256_pk_to_EVP_PKEY;
181 local:
182 *;
183};
diff --git a/src/export.llvm b/src/export.llvm
new file mode 100644
index 0000000..ef99a26
--- /dev/null
+++ b/src/export.llvm
@@ -0,0 +1,178 @@
1_eddsa_pk_free
2_eddsa_pk_from_EVP_PKEY
3_eddsa_pk_from_ptr
4_eddsa_pk_new
5_eddsa_pk_to_EVP_PKEY
6_es256_pk_free
7_es256_pk_from_EC_KEY
8_es256_pk_from_ptr
9_es256_pk_new
10_es256_pk_to_EVP_PKEY
11_fido_assert_allow_cred
12_fido_assert_authdata_len
13_fido_assert_authdata_ptr
14_fido_assert_clientdata_hash_len
15_fido_assert_clientdata_hash_ptr
16_fido_assert_count
17_fido_assert_flags
18_fido_assert_free
19_fido_assert_hmac_secret_len
20_fido_assert_hmac_secret_ptr
21_fido_assert_id_len
22_fido_assert_id_ptr
23_fido_assert_new
24_fido_assert_rp_id
25_fido_assert_set_authdata
26_fido_assert_set_authdata_raw
27_fido_assert_set_clientdata_hash
28_fido_assert_set_count
29_fido_assert_set_extensions
30_fido_assert_set_hmac_salt
31_fido_assert_set_options
32_fido_assert_set_rp
33_fido_assert_set_sig
34_fido_assert_set_up
35_fido_assert_set_uv
36_fido_assert_sigcount
37_fido_assert_sig_len
38_fido_assert_sig_ptr
39_fido_assert_user_display_name
40_fido_assert_user_icon
41_fido_assert_user_id_len
42_fido_assert_user_id_ptr
43_fido_assert_user_name
44_fido_assert_verify
45_fido_bio_dev_enroll_begin
46_fido_bio_dev_enroll_cancel
47_fido_bio_dev_enroll_continue
48_fido_bio_dev_enroll_remove
49_fido_bio_dev_get_info
50_fido_bio_dev_get_template_array
51_fido_bio_dev_set_template_name
52_fido_bio_enroll_free
53_fido_bio_enroll_last_status
54_fido_bio_enroll_new
55_fido_bio_enroll_remaining_samples
56_fido_bio_info_free
57_fido_bio_info_max_samples
58_fido_bio_info_new
59_fido_bio_info_type
60_fido_bio_template
61_fido_bio_template_array_count
62_fido_bio_template_array_free
63_fido_bio_template_array_new
64_fido_bio_template_free
65_fido_bio_template_id_len
66_fido_bio_template_id_ptr
67_fido_bio_template_name
68_fido_bio_template_new
69_fido_bio_template_set_id
70_fido_bio_template_set_name
71_fido_cbor_info_aaguid_len
72_fido_cbor_info_aaguid_ptr
73_fido_cbor_info_extensions_len
74_fido_cbor_info_extensions_ptr
75_fido_cbor_info_free
76_fido_cbor_info_maxmsgsiz
77_fido_cbor_info_new
78_fido_cbor_info_options_len
79_fido_cbor_info_options_name_ptr
80_fido_cbor_info_options_value_ptr
81_fido_cbor_info_protocols_len
82_fido_cbor_info_protocols_ptr
83_fido_cbor_info_versions_len
84_fido_cbor_info_versions_ptr
85_fido_cred_authdata_len
86_fido_cred_authdata_ptr
87_fido_cred_clientdata_hash_len
88_fido_cred_clientdata_hash_ptr
89_fido_cred_display_name
90_fido_cred_exclude
91_fido_cred_flags
92_fido_cred_fmt
93_fido_cred_free
94_fido_cred_id_len
95_fido_cred_id_ptr
96_fido_credman_del_dev_rk
97_fido_credman_get_dev_metadata
98_fido_credman_get_dev_rk
99_fido_credman_get_dev_rp
100_fido_credman_metadata_free
101_fido_credman_metadata_new
102_fido_credman_rk
103_fido_credman_rk_count
104_fido_credman_rk_existing
105_fido_credman_rk_free
106_fido_credman_rk_new
107_fido_credman_rk_remaining
108_fido_credman_rp_count
109_fido_credman_rp_free
110_fido_credman_rp_id
111_fido_credman_rp_id_hash_len
112_fido_credman_rp_id_hash_ptr
113_fido_credman_rp_name
114_fido_credman_rp_new
115_fido_cred_new
116_fido_cred_pubkey_len
117_fido_cred_pubkey_ptr
118_fido_cred_rp_id
119_fido_cred_rp_name
120_fido_cred_set_authdata
121_fido_cred_set_authdata_raw
122_fido_cred_set_clientdata_hash
123_fido_cred_set_extensions
124_fido_cred_set_fmt
125_fido_cred_set_options
126_fido_cred_set_rk
127_fido_cred_set_rp
128_fido_cred_set_sig
129_fido_cred_set_type
130_fido_cred_set_user
131_fido_cred_set_uv
132_fido_cred_set_x509
133_fido_cred_sig_len
134_fido_cred_sig_ptr
135_fido_cred_type
136_fido_cred_user_id_len
137_fido_cred_user_id_ptr
138_fido_cred_user_name
139_fido_cred_verify
140_fido_cred_verify_self
141_fido_cred_x5c_len
142_fido_cred_x5c_ptr
143_fido_dev_build
144_fido_dev_cancel
145_fido_dev_close
146_fido_dev_flags
147_fido_dev_force_fido2
148_fido_dev_force_u2f
149_fido_dev_free
150_fido_dev_get_assert
151_fido_dev_get_cbor_info
152_fido_dev_get_retry_count
153_fido_dev_info_free
154_fido_dev_info_manifest
155_fido_dev_info_manufacturer_string
156_fido_dev_info_new
157_fido_dev_info_path
158_fido_dev_info_product
159_fido_dev_info_product_string
160_fido_dev_info_ptr
161_fido_dev_info_vendor
162_fido_dev_is_fido2
163_fido_dev_major
164_fido_dev_make_cred
165_fido_dev_minor
166_fido_dev_new
167_fido_dev_open
168_fido_dev_protocol
169_fido_dev_reset
170_fido_dev_set_io_functions
171_fido_dev_set_pin
172_fido_init
173_fido_strerr
174_rs256_pk_free
175_rs256_pk_from_ptr
176_rs256_pk_from_RSA
177_rs256_pk_new
178_rs256_pk_to_EVP_PKEY
diff --git a/src/export.msvc b/src/export.msvc
new file mode 100644
index 0000000..ff5425a
--- /dev/null
+++ b/src/export.msvc
@@ -0,0 +1,179 @@
1EXPORTS
2eddsa_pk_free
3eddsa_pk_from_EVP_PKEY
4eddsa_pk_from_ptr
5eddsa_pk_new
6eddsa_pk_to_EVP_PKEY
7es256_pk_free
8es256_pk_from_EC_KEY
9es256_pk_from_ptr
10es256_pk_new
11es256_pk_to_EVP_PKEY
12fido_assert_allow_cred
13fido_assert_authdata_len
14fido_assert_authdata_ptr
15fido_assert_clientdata_hash_len
16fido_assert_clientdata_hash_ptr
17fido_assert_count
18fido_assert_flags
19fido_assert_free
20fido_assert_hmac_secret_len
21fido_assert_hmac_secret_ptr
22fido_assert_id_len
23fido_assert_id_ptr
24fido_assert_new
25fido_assert_rp_id
26fido_assert_set_authdata
27fido_assert_set_authdata_raw
28fido_assert_set_clientdata_hash
29fido_assert_set_count
30fido_assert_set_extensions
31fido_assert_set_hmac_salt
32fido_assert_set_options
33fido_assert_set_rp
34fido_assert_set_sig
35fido_assert_set_up
36fido_assert_set_uv
37fido_assert_sigcount
38fido_assert_sig_len
39fido_assert_sig_ptr
40fido_assert_user_display_name
41fido_assert_user_icon
42fido_assert_user_id_len
43fido_assert_user_id_ptr
44fido_assert_user_name
45fido_assert_verify
46fido_bio_dev_enroll_begin
47fido_bio_dev_enroll_cancel
48fido_bio_dev_enroll_continue
49fido_bio_dev_enroll_remove
50fido_bio_dev_get_info
51fido_bio_dev_get_template_array
52fido_bio_dev_set_template_name
53fido_bio_enroll_free
54fido_bio_enroll_last_status
55fido_bio_enroll_new
56fido_bio_enroll_remaining_samples
57fido_bio_info_free
58fido_bio_info_max_samples
59fido_bio_info_new
60fido_bio_info_type
61fido_bio_template
62fido_bio_template_array_count
63fido_bio_template_array_free
64fido_bio_template_array_new
65fido_bio_template_free
66fido_bio_template_id_len
67fido_bio_template_id_ptr
68fido_bio_template_name
69fido_bio_template_new
70fido_bio_template_set_id
71fido_bio_template_set_name
72fido_cbor_info_aaguid_len
73fido_cbor_info_aaguid_ptr
74fido_cbor_info_extensions_len
75fido_cbor_info_extensions_ptr
76fido_cbor_info_free
77fido_cbor_info_maxmsgsiz
78fido_cbor_info_new
79fido_cbor_info_options_len
80fido_cbor_info_options_name_ptr
81fido_cbor_info_options_value_ptr
82fido_cbor_info_protocols_len
83fido_cbor_info_protocols_ptr
84fido_cbor_info_versions_len
85fido_cbor_info_versions_ptr
86fido_cred_authdata_len
87fido_cred_authdata_ptr
88fido_cred_clientdata_hash_len
89fido_cred_clientdata_hash_ptr
90fido_cred_display_name
91fido_cred_exclude
92fido_cred_flags
93fido_cred_fmt
94fido_cred_free
95fido_cred_id_len
96fido_cred_id_ptr
97fido_credman_del_dev_rk
98fido_credman_get_dev_metadata
99fido_credman_get_dev_rk
100fido_credman_get_dev_rp
101fido_credman_metadata_free
102fido_credman_metadata_new
103fido_credman_rk
104fido_credman_rk_count
105fido_credman_rk_existing
106fido_credman_rk_free
107fido_credman_rk_new
108fido_credman_rk_remaining
109fido_credman_rp_count
110fido_credman_rp_free
111fido_credman_rp_id
112fido_credman_rp_id_hash_len
113fido_credman_rp_id_hash_ptr
114fido_credman_rp_name
115fido_credman_rp_new
116fido_cred_new
117fido_cred_pubkey_len
118fido_cred_pubkey_ptr
119fido_cred_rp_id
120fido_cred_rp_name
121fido_cred_set_authdata
122fido_cred_set_authdata_raw
123fido_cred_set_clientdata_hash
124fido_cred_set_extensions
125fido_cred_set_fmt
126fido_cred_set_options
127fido_cred_set_rk
128fido_cred_set_rp
129fido_cred_set_sig
130fido_cred_set_type
131fido_cred_set_user
132fido_cred_set_uv
133fido_cred_set_x509
134fido_cred_sig_len
135fido_cred_sig_ptr
136fido_cred_type
137fido_cred_user_id_len
138fido_cred_user_id_ptr
139fido_cred_user_name
140fido_cred_verify
141fido_cred_verify_self
142fido_cred_x5c_len
143fido_cred_x5c_ptr
144fido_dev_build
145fido_dev_cancel
146fido_dev_close
147fido_dev_flags
148fido_dev_force_fido2
149fido_dev_force_u2f
150fido_dev_free
151fido_dev_get_assert
152fido_dev_get_cbor_info
153fido_dev_get_retry_count
154fido_dev_info_free
155fido_dev_info_manifest
156fido_dev_info_manufacturer_string
157fido_dev_info_new
158fido_dev_info_path
159fido_dev_info_product
160fido_dev_info_product_string
161fido_dev_info_ptr
162fido_dev_info_vendor
163fido_dev_is_fido2
164fido_dev_major
165fido_dev_make_cred
166fido_dev_minor
167fido_dev_new
168fido_dev_open
169fido_dev_protocol
170fido_dev_reset
171fido_dev_set_io_functions
172fido_dev_set_pin
173fido_init
174fido_strerr
175rs256_pk_free
176rs256_pk_from_ptr
177rs256_pk_from_RSA
178rs256_pk_new
179rs256_pk_to_EVP_PKEY
diff --git a/src/extern.h b/src/extern.h
new file mode 100644
index 0000000..c35af58
--- /dev/null
+++ b/src/extern.h
@@ -0,0 +1,132 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _EXTERN_H
8#define _EXTERN_H
9
10/* aes256 */
11int aes256_cbc_dec(const fido_blob_t *, const fido_blob_t *, fido_blob_t *);
12int aes256_cbc_enc(const fido_blob_t *, const fido_blob_t *, fido_blob_t *);
13
14/* cbor encoding functions */
15cbor_item_t *cbor_flatten_vector(cbor_item_t **, size_t);
16cbor_item_t *cbor_encode_assert_options(fido_opt_t, fido_opt_t);
17cbor_item_t *cbor_encode_change_pin_auth(const fido_blob_t *,
18 const fido_blob_t *, const fido_blob_t *);
19cbor_item_t *cbor_encode_extensions(int);
20cbor_item_t *cbor_encode_hmac_secret_param(const fido_blob_t *,
21 const es256_pk_t *, const fido_blob_t *);
22cbor_item_t *cbor_encode_options(fido_opt_t, fido_opt_t);
23cbor_item_t *cbor_encode_pin_auth(const fido_blob_t *, const fido_blob_t *);
24cbor_item_t *cbor_encode_pin_enc(const fido_blob_t *, const fido_blob_t *);
25cbor_item_t *cbor_encode_pin_hash_enc(const fido_blob_t *, const fido_blob_t *);
26cbor_item_t *cbor_encode_pin_opt(void);
27cbor_item_t *cbor_encode_pubkey(const fido_blob_t *);
28cbor_item_t *cbor_encode_pubkey_list(const fido_blob_array_t *);
29cbor_item_t *cbor_encode_pubkey_param(int);
30cbor_item_t *cbor_encode_rp_entity(const fido_rp_t *);
31cbor_item_t *cbor_encode_set_pin_auth(const fido_blob_t *, const fido_blob_t *);
32cbor_item_t *cbor_encode_user_entity(const fido_user_t *);
33cbor_item_t *es256_pk_encode(const es256_pk_t *, int);
34
35/* cbor decoding functions */
36int cbor_decode_attstmt(const cbor_item_t *, fido_attstmt_t *);
37int cbor_decode_cred_authdata(const cbor_item_t *, int, fido_blob_t *,
38 fido_authdata_t *, fido_attcred_t *, int *);
39int cbor_decode_assert_authdata(const cbor_item_t *, fido_blob_t *,
40 fido_authdata_t *, int *, fido_blob_t *);
41int cbor_decode_cred_id(const cbor_item_t *, fido_blob_t *);
42int cbor_decode_fmt(const cbor_item_t *, char **);
43int cbor_decode_pubkey(const cbor_item_t *, int *, void *);
44int cbor_decode_rp_entity(const cbor_item_t *, fido_rp_t *);
45int cbor_decode_uint64(const cbor_item_t *, uint64_t *);
46int cbor_decode_user(const cbor_item_t *, fido_user_t *);
47int es256_pk_decode(const cbor_item_t *, es256_pk_t *);
48int rs256_pk_decode(const cbor_item_t *, rs256_pk_t *);
49int eddsa_pk_decode(const cbor_item_t *, eddsa_pk_t *);
50
51/* auxiliary cbor routines */
52int cbor_add_bool(cbor_item_t *, const char *, fido_opt_t);
53int cbor_add_bytestring(cbor_item_t *, const char *, const unsigned char *,
54 size_t);
55int cbor_add_string(cbor_item_t *, const char *, const char *);
56int cbor_array_iter(const cbor_item_t *, void *, int(*)(const cbor_item_t *,
57 void *));
58int cbor_build_frame(uint8_t, cbor_item_t *[], size_t, fido_blob_t *);
59int cbor_bytestring_copy(const cbor_item_t *, unsigned char **, size_t *);
60int cbor_map_iter(const cbor_item_t *, void *, int(*)(const cbor_item_t *,
61 const cbor_item_t *, void *));
62int cbor_string_copy(const cbor_item_t *, char **);
63int cbor_parse_reply(const unsigned char *, size_t, void *,
64 int(*)(const cbor_item_t *, const cbor_item_t *, void *));
65int cbor_add_pin_params(fido_dev_t *, const fido_blob_t *, const es256_pk_t *,
66 const fido_blob_t *,const char *, cbor_item_t **, cbor_item_t **);
67void cbor_vector_free(cbor_item_t **, size_t);
68
69#ifndef nitems
70#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
71#endif
72
73/* buf */
74int fido_buf_read(const unsigned char **, size_t *, void *, size_t);
75int fido_buf_write(unsigned char **, size_t *, const void *, size_t);
76
77/* hid i/o */
78void *fido_hid_open(const char *);
79void fido_hid_close(void *);
80int fido_hid_read(void *, unsigned char *, size_t, int);
81int fido_hid_write(void *, const unsigned char *, size_t);
82
83/* generic i/o */
84int fido_rx_cbor_status(fido_dev_t *, int);
85int fido_rx(fido_dev_t *, uint8_t, void *, size_t, int);
86int fido_tx(fido_dev_t *, uint8_t, const void *, size_t);
87
88/* log */
89#ifdef FIDO_NO_DIAGNOSTIC
90#define fido_log_init(...) do { /* nothing */ } while (0)
91#define fido_log_debug(...) do { /* nothing */ } while (0)
92#define fido_log_xxd(...) do { /* nothing */ } while (0)
93#else
94#ifdef __GNUC__
95void fido_log_init(void);
96void fido_log_debug(const char *, ...)
97 __attribute__((__format__ (printf, 1, 2)));
98void fido_log_xxd(const void *, size_t);
99#else
100void fido_log_init(void);
101void fido_log_debug(const char *, ...);
102void fido_log_xxd(const void *, size_t);
103#endif /* __GNUC__ */
104#endif /* FIDO_NO_DIAGNOSTIC */
105
106/* u2f */
107int u2f_register(fido_dev_t *, fido_cred_t *, int);
108int u2f_authenticate(fido_dev_t *, fido_assert_t *, int);
109
110/* unexposed fido ops */
111int fido_dev_authkey(fido_dev_t *, es256_pk_t *);
112int fido_dev_get_pin_token(fido_dev_t *, const char *, const fido_blob_t *,
113 const es256_pk_t *, fido_blob_t *);
114int fido_do_ecdh(fido_dev_t *, es256_pk_t **, fido_blob_t **);
115
116/* misc */
117void fido_assert_reset_rx(fido_assert_t *);
118void fido_assert_reset_tx(fido_assert_t *);
119void fido_cred_reset_rx(fido_cred_t *);
120void fido_cred_reset_tx(fido_cred_t *);
121int fido_check_rp_id(const char *, const unsigned char *);
122int fido_check_flags(uint8_t, fido_opt_t, fido_opt_t);
123
124/* crypto */
125int fido_verify_sig_es256(const fido_blob_t *, const es256_pk_t *,
126 const fido_blob_t *);
127int fido_verify_sig_rs256(const fido_blob_t *, const rs256_pk_t *,
128 const fido_blob_t *);
129int fido_verify_sig_eddsa(const fido_blob_t *, const eddsa_pk_t *,
130 const fido_blob_t *);
131
132#endif /* !_EXTERN_H */
diff --git a/src/fido.h b/src/fido.h
new file mode 100644
index 0000000..f85a41a
--- /dev/null
+++ b/src/fido.h
@@ -0,0 +1,194 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_H
8#define _FIDO_H
9
10#include <openssl/ec.h>
11#include <openssl/evp.h>
12
13#include <stdbool.h>
14#include <stdint.h>
15#include <stdlib.h>
16
17typedef void *fido_dev_io_open_t(const char *);
18typedef void fido_dev_io_close_t(void *);
19typedef int fido_dev_io_read_t(void *, unsigned char *, size_t, int);
20typedef int fido_dev_io_write_t(void *, const unsigned char *, size_t);
21
22typedef struct fido_dev_io {
23 fido_dev_io_open_t *open;
24 fido_dev_io_close_t *close;
25 fido_dev_io_read_t *read;
26 fido_dev_io_write_t *write;
27} fido_dev_io_t;
28
29typedef enum {
30 FIDO_OPT_OMIT = 0, /* use authenticator's default */
31 FIDO_OPT_FALSE, /* explicitly set option to false */
32 FIDO_OPT_TRUE, /* explicitly set option to true */
33} fido_opt_t;
34
35#ifdef _FIDO_INTERNAL
36#include <cbor.h>
37#include <limits.h>
38
39#include "blob.h"
40#include "../openbsd-compat/openbsd-compat.h"
41#include "iso7816.h"
42#include "types.h"
43#include "extern.h"
44#endif
45
46#include "fido/err.h"
47#include "fido/param.h"
48
49#ifndef _FIDO_INTERNAL
50typedef struct fido_assert fido_assert_t;
51typedef struct fido_cbor_info fido_cbor_info_t;
52typedef struct fido_cred fido_cred_t;
53typedef struct fido_dev fido_dev_t;
54typedef struct fido_dev_info fido_dev_info_t;
55typedef struct es256_pk es256_pk_t;
56typedef struct es256_sk es256_sk_t;
57typedef struct rs256_pk rs256_pk_t;
58typedef struct eddsa_pk eddsa_pk_t;
59#endif
60
61fido_assert_t *fido_assert_new(void);
62fido_cred_t *fido_cred_new(void);
63fido_dev_t *fido_dev_new(void);
64fido_dev_info_t *fido_dev_info_new(size_t);
65fido_cbor_info_t *fido_cbor_info_new(void);
66
67void fido_assert_free(fido_assert_t **);
68void fido_cbor_info_free(fido_cbor_info_t **);
69void fido_cred_free(fido_cred_t **);
70void fido_dev_force_fido2(fido_dev_t *);
71void fido_dev_force_u2f(fido_dev_t *);
72void fido_dev_free(fido_dev_t **);
73void fido_dev_info_free(fido_dev_info_t **, size_t);
74
75/* fido_init() flags. */
76#define FIDO_DEBUG 0x01
77
78void fido_init(int);
79
80const unsigned char *fido_assert_authdata_ptr(const fido_assert_t *, size_t);
81const unsigned char *fido_assert_clientdata_hash_ptr(const fido_assert_t *);
82const unsigned char *fido_assert_hmac_secret_ptr(const fido_assert_t *, size_t);
83const unsigned char *fido_assert_id_ptr(const fido_assert_t *, size_t);
84const unsigned char *fido_assert_sig_ptr(const fido_assert_t *, size_t);
85const unsigned char *fido_assert_user_id_ptr(const fido_assert_t *, size_t);
86
87char **fido_cbor_info_extensions_ptr(const fido_cbor_info_t *);
88char **fido_cbor_info_options_name_ptr(const fido_cbor_info_t *);
89char **fido_cbor_info_versions_ptr(const fido_cbor_info_t *);
90const bool *fido_cbor_info_options_value_ptr(const fido_cbor_info_t *);
91const char *fido_assert_rp_id(const fido_assert_t *);
92const char *fido_assert_user_display_name(const fido_assert_t *, size_t);
93const char *fido_assert_user_icon(const fido_assert_t *, size_t);
94const char *fido_assert_user_name(const fido_assert_t *, size_t);
95const char *fido_cred_display_name(const fido_cred_t *);
96const char *fido_cred_fmt(const fido_cred_t *);
97const char *fido_cred_rp_id(const fido_cred_t *);
98const char *fido_cred_rp_name(const fido_cred_t *);
99const char *fido_cred_user_name(const fido_cred_t *);
100const char *fido_dev_info_manufacturer_string(const fido_dev_info_t *);
101const char *fido_dev_info_path(const fido_dev_info_t *);
102const char *fido_dev_info_product_string(const fido_dev_info_t *);
103const fido_dev_info_t *fido_dev_info_ptr(const fido_dev_info_t *, size_t);
104const uint8_t *fido_cbor_info_protocols_ptr(const fido_cbor_info_t *);
105const unsigned char *fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *);
106const unsigned char *fido_cred_authdata_ptr(const fido_cred_t *);
107const unsigned char *fido_cred_clientdata_hash_ptr(const fido_cred_t *);
108const unsigned char *fido_cred_id_ptr(const fido_cred_t *);
109const unsigned char *fido_cred_user_id_ptr(const fido_cred_t *);
110const unsigned char *fido_cred_pubkey_ptr(const fido_cred_t *);
111const unsigned char *fido_cred_sig_ptr(const fido_cred_t *);
112const unsigned char *fido_cred_x5c_ptr(const fido_cred_t *);
113
114int fido_assert_allow_cred(fido_assert_t *, const unsigned char *, size_t);
115int fido_assert_set_authdata(fido_assert_t *, size_t, const unsigned char *,
116 size_t);
117int fido_assert_set_authdata_raw(fido_assert_t *, size_t, const unsigned char *,
118 size_t);
119int fido_assert_set_clientdata_hash(fido_assert_t *, const unsigned char *,
120 size_t);
121int fido_assert_set_count(fido_assert_t *, size_t);
122int fido_assert_set_extensions(fido_assert_t *, int);
123int fido_assert_set_hmac_salt(fido_assert_t *, const unsigned char *, size_t);
124int fido_assert_set_options(fido_assert_t *, bool, bool) __attribute__((__deprecated__));
125int fido_assert_set_rp(fido_assert_t *, const char *);
126int fido_assert_set_up(fido_assert_t *, fido_opt_t);
127int fido_assert_set_uv(fido_assert_t *, fido_opt_t);
128int fido_assert_set_sig(fido_assert_t *, size_t, const unsigned char *, size_t);
129int fido_assert_verify(const fido_assert_t *, size_t, int, const void *);
130int fido_cred_exclude(fido_cred_t *, const unsigned char *, size_t);
131int fido_cred_set_authdata(fido_cred_t *, const unsigned char *, size_t);
132int fido_cred_set_authdata_raw(fido_cred_t *, const unsigned char *, size_t);
133int fido_cred_set_clientdata_hash(fido_cred_t *, const unsigned char *, size_t);
134int fido_cred_set_extensions(fido_cred_t *, int);
135int fido_cred_set_fmt(fido_cred_t *, const char *);
136int fido_cred_set_options(fido_cred_t *, bool, bool) __attribute__((__deprecated__));
137int fido_cred_set_rk(fido_cred_t *, fido_opt_t);
138int fido_cred_set_rp(fido_cred_t *, const char *, const char *);
139int fido_cred_set_sig(fido_cred_t *, const unsigned char *, size_t);
140int fido_cred_set_type(fido_cred_t *, int);
141int fido_cred_set_uv(fido_cred_t *, fido_opt_t);
142int fido_cred_type(const fido_cred_t *);
143int fido_cred_set_user(fido_cred_t *, const unsigned char *, size_t,
144 const char *, const char *, const char *);
145int fido_cred_set_x509(fido_cred_t *, const unsigned char *, size_t);
146int fido_cred_verify(const fido_cred_t *);
147int fido_cred_verify_self(const fido_cred_t *);
148int fido_dev_cancel(fido_dev_t *);
149int fido_dev_close(fido_dev_t *);
150int fido_dev_get_assert(fido_dev_t *, fido_assert_t *, const char *);
151int fido_dev_get_cbor_info(fido_dev_t *, fido_cbor_info_t *);
152int fido_dev_get_retry_count(fido_dev_t *, int *);
153int fido_dev_info_manifest(fido_dev_info_t *, size_t, size_t *);
154int fido_dev_make_cred(fido_dev_t *, fido_cred_t *, const char *);
155int fido_dev_open(fido_dev_t *, const char *);
156int fido_dev_reset(fido_dev_t *);
157int fido_dev_set_io_functions(fido_dev_t *, const fido_dev_io_t *);
158int fido_dev_set_pin(fido_dev_t *, const char *, const char *);
159
160size_t fido_assert_authdata_len(const fido_assert_t *, size_t);
161size_t fido_assert_clientdata_hash_len(const fido_assert_t *);
162size_t fido_assert_count(const fido_assert_t *);
163size_t fido_assert_hmac_secret_len(const fido_assert_t *, size_t);
164size_t fido_assert_id_len(const fido_assert_t *, size_t);
165size_t fido_assert_sig_len(const fido_assert_t *, size_t);
166size_t fido_assert_user_id_len(const fido_assert_t *, size_t);
167size_t fido_cbor_info_aaguid_len(const fido_cbor_info_t *);
168size_t fido_cbor_info_extensions_len(const fido_cbor_info_t *);
169size_t fido_cbor_info_options_len(const fido_cbor_info_t *);
170size_t fido_cbor_info_protocols_len(const fido_cbor_info_t *);
171size_t fido_cbor_info_versions_len(const fido_cbor_info_t *);
172size_t fido_cred_authdata_len(const fido_cred_t *);
173size_t fido_cred_clientdata_hash_len(const fido_cred_t *);
174size_t fido_cred_id_len(const fido_cred_t *);
175size_t fido_cred_user_id_len(const fido_cred_t *);
176size_t fido_cred_pubkey_len(const fido_cred_t *);
177size_t fido_cred_sig_len(const fido_cred_t *);
178size_t fido_cred_x5c_len(const fido_cred_t *);
179
180uint8_t fido_assert_flags(const fido_assert_t *, size_t);
181uint32_t fido_assert_sigcount(const fido_assert_t *, size_t);
182uint8_t fido_cred_flags(const fido_cred_t *);
183uint8_t fido_dev_protocol(const fido_dev_t *);
184uint8_t fido_dev_major(const fido_dev_t *);
185uint8_t fido_dev_minor(const fido_dev_t *);
186uint8_t fido_dev_build(const fido_dev_t *);
187uint8_t fido_dev_flags(const fido_dev_t *);
188int16_t fido_dev_info_vendor(const fido_dev_info_t *);
189int16_t fido_dev_info_product(const fido_dev_info_t *);
190uint64_t fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *);
191
192bool fido_dev_is_fido2(const fido_dev_t *);
193
194#endif /* !_FIDO_H */
diff --git a/src/fido/bio.h b/src/fido/bio.h
new file mode 100644
index 0000000..31dffe4
--- /dev/null
+++ b/src/fido/bio.h
@@ -0,0 +1,95 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_BIO_H
8#define _FIDO_BIO_H
9
10#include <stdint.h>
11#include <stdlib.h>
12
13#include "fido/err.h"
14#include "fido/param.h"
15
16#ifdef _FIDO_INTERNAL
17struct fido_bio_template {
18 fido_blob_t id;
19 char *name;
20};
21
22struct fido_bio_template_array {
23 struct fido_bio_template *ptr;
24 size_t n_alloc; /* number of allocated entries */
25 size_t n_rx; /* number of populated entries */
26};
27
28struct fido_bio_enroll {
29 uint8_t remaining_samples;
30 uint8_t last_status;
31 fido_blob_t *token;
32};
33
34struct fido_bio_info {
35 uint8_t type;
36 uint8_t max_samples;
37};
38#endif
39
40typedef struct fido_bio_template fido_bio_template_t;
41typedef struct fido_bio_template_array fido_bio_template_array_t;
42typedef struct fido_bio_enroll fido_bio_enroll_t;
43typedef struct fido_bio_info fido_bio_info_t;
44
45#define FIDO_BIO_ENROLL_FP_GOOD 0x00
46#define FIDO_BIO_ENROLL_FP_TOO_HIGH 0x01
47#define FIDO_BIO_ENROLL_FP_TOO_LOW 0x02
48#define FIDO_BIO_ENROLL_FP_TOO_LEFT 0x03
49#define FIDO_BIO_ENROLL_FP_TOO_RIGHT 0x04
50#define FIDO_BIO_ENROLL_FP_TOO_FAST 0x05
51#define FIDO_BIO_ENROLL_FP_TOO_SLOW 0x06
52#define FIDO_BIO_ENROLL_FP_POOR_QUALITY 0x07
53#define FIDO_BIO_ENROLL_FP_TOO_SKEWED 0x08
54#define FIDO_BIO_ENROLL_FP_TOO_SHORT 0x09
55#define FIDO_BIO_ENROLL_FP_MERGE_FAILURE 0x0a
56#define FIDO_BIO_ENROLL_FP_EXISTS 0x0b
57#define FIDO_BIO_ENROLL_FP_DATABASE_FULL 0x0c
58#define FIDO_BIO_ENROLL_NO_USER_ACTIVITY 0x0d
59#define FIDO_BIO_ENROLL_NO_USER_PRESENCE_TRANSITION 0x0e
60
61const char *fido_bio_template_name(const fido_bio_template_t *);
62const fido_bio_template_t *fido_bio_template(const fido_bio_template_array_t *,
63 size_t);
64const unsigned char *fido_bio_template_id_ptr(const fido_bio_template_t *);
65fido_bio_enroll_t *fido_bio_enroll_new(void);
66fido_bio_info_t *fido_bio_info_new(void);
67fido_bio_template_array_t *fido_bio_template_array_new(void);
68fido_bio_template_t *fido_bio_template_new(void);
69int fido_bio_dev_enroll_begin(fido_dev_t *, fido_bio_template_t *,
70 fido_bio_enroll_t *, uint32_t, const char *);
71int fido_bio_dev_enroll_cancel(fido_dev_t *);
72int fido_bio_dev_enroll_continue(fido_dev_t *, const fido_bio_template_t *,
73 fido_bio_enroll_t *, uint32_t);
74int fido_bio_dev_enroll_remove(fido_dev_t *, const fido_bio_template_t *,
75 const char *);
76int fido_bio_dev_get_info(fido_dev_t *, fido_bio_info_t *);
77int fido_bio_dev_get_template_array(fido_dev_t *, fido_bio_template_array_t *,
78 const char *);
79int fido_bio_dev_set_template_name(fido_dev_t *, const fido_bio_template_t *,
80 const char *);
81int fido_bio_template_set_id(fido_bio_template_t *, const unsigned char *,
82 size_t);
83int fido_bio_template_set_name(fido_bio_template_t *, const char *);
84size_t fido_bio_template_array_count(const fido_bio_template_array_t *);
85size_t fido_bio_template_id_len(const fido_bio_template_t *);
86uint8_t fido_bio_enroll_last_status(const fido_bio_enroll_t *);
87uint8_t fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *);
88uint8_t fido_bio_info_max_samples(const fido_bio_info_t *);
89uint8_t fido_bio_info_type(const fido_bio_info_t *);
90void fido_bio_enroll_free(fido_bio_enroll_t **);
91void fido_bio_info_free(fido_bio_info_t **);
92void fido_bio_template_array_free(fido_bio_template_array_t **);
93void fido_bio_template_free(fido_bio_template_t **);
94
95#endif /* !_FIDO_BIO_H */
diff --git a/src/fido/credman.h b/src/fido/credman.h
new file mode 100644
index 0000000..1c7cafe
--- /dev/null
+++ b/src/fido/credman.h
@@ -0,0 +1,74 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_CREDMAN_H
8#define _FIDO_CREDMAN_H
9
10#include <stdint.h>
11#include <stdlib.h>
12
13#include "fido/err.h"
14#include "fido/param.h"
15
16#ifdef _FIDO_INTERNAL
17struct fido_credman_metadata {
18 uint64_t rk_existing;
19 uint64_t rk_remaining;
20};
21
22struct fido_credman_single_rp {
23 fido_rp_t rp_entity;
24 fido_blob_t rp_id_hash;
25};
26
27struct fido_credman_rp {
28 struct fido_credman_single_rp *ptr;
29 size_t n_alloc; /* number of allocated entries */
30 size_t n_rx; /* number of populated entries */
31};
32
33struct fido_credman_rk {
34 fido_cred_t *ptr;
35 size_t n_alloc; /* number of allocated entries */
36 size_t n_rx; /* number of populated entries */
37};
38#endif
39
40typedef struct fido_credman_metadata fido_credman_metadata_t;
41typedef struct fido_credman_rk fido_credman_rk_t;
42typedef struct fido_credman_rp fido_credman_rp_t;
43
44const char *fido_credman_rp_id(const fido_credman_rp_t *, size_t);
45const char *fido_credman_rp_name(const fido_credman_rp_t *, size_t);
46
47const fido_cred_t *fido_credman_rk(const fido_credman_rk_t *, size_t);
48const unsigned char *fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *,
49 size_t);
50
51fido_credman_metadata_t *fido_credman_metadata_new(void);
52fido_credman_rk_t *fido_credman_rk_new(void);
53fido_credman_rp_t *fido_credman_rp_new(void);
54
55int fido_credman_del_dev_rk(fido_dev_t *, const unsigned char *, size_t,
56 const char *);
57int fido_credman_get_dev_metadata(fido_dev_t *, fido_credman_metadata_t *,
58 const char *);
59int fido_credman_get_dev_rk(fido_dev_t *, const char *, fido_credman_rk_t *,
60 const char *);
61int fido_credman_get_dev_rp(fido_dev_t *, fido_credman_rp_t *, const char *);
62
63size_t fido_credman_rk_count(const fido_credman_rk_t *);
64size_t fido_credman_rp_count(const fido_credman_rp_t *);
65size_t fido_credman_rp_id_hash_len(const fido_credman_rp_t *, size_t);
66
67uint64_t fido_credman_rk_existing(const fido_credman_metadata_t *);
68uint64_t fido_credman_rk_remaining(const fido_credman_metadata_t *);
69
70void fido_credman_metadata_free(fido_credman_metadata_t **);
71void fido_credman_rk_free(fido_credman_rk_t **);
72void fido_credman_rp_free(fido_credman_rp_t **);
73
74#endif /* !_FIDO_CREDMAN_H */
diff --git a/src/fido/eddsa.h b/src/fido/eddsa.h
new file mode 100644
index 0000000..9de272d
--- /dev/null
+++ b/src/fido/eddsa.h
@@ -0,0 +1,40 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_EDDSA_H
8#define _FIDO_EDDSA_H
9
10#include <openssl/ec.h>
11
12#include <stdint.h>
13#include <stdlib.h>
14
15eddsa_pk_t *eddsa_pk_new(void);
16void eddsa_pk_free(eddsa_pk_t **);
17EVP_PKEY *eddsa_pk_to_EVP_PKEY(const eddsa_pk_t *);
18
19int eddsa_pk_from_EVP_PKEY(eddsa_pk_t *, const EVP_PKEY *);
20int eddsa_pk_from_ptr(eddsa_pk_t *, const void *, size_t);
21
22#ifdef _FIDO_INTERNAL
23
24#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10101000L
25#define EVP_PKEY_ED25519 EVP_PKEY_NONE
26int EVP_PKEY_get_raw_public_key(const EVP_PKEY *, unsigned char *, size_t *);
27EVP_PKEY *EVP_PKEY_new_raw_public_key(int, ENGINE *, const unsigned char *,
28 size_t);
29int EVP_DigestVerify(EVP_MD_CTX *, const unsigned char *, size_t,
30 const unsigned char *, size_t);
31#endif /* LIBRESSL_VERSION_NUMBER || OPENSSL_VERSION_NUMBER < 0x10101000L */
32
33#if OPENSSL_VERSION_NUMBER < 0x10100000L
34EVP_MD_CTX *EVP_MD_CTX_new(void);
35void EVP_MD_CTX_free(EVP_MD_CTX *);
36#endif
37
38#endif /* _FIDO_INTERNAL */
39
40#endif /* !_FIDO_EDDSA_H */
diff --git a/src/fido/err.h b/src/fido/err.h
new file mode 100644
index 0000000..11f52bc
--- /dev/null
+++ b/src/fido/err.h
@@ -0,0 +1,69 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_ERR_H
8#define _FIDO_ERR_H
9
10#define FIDO_ERR_SUCCESS 0x00
11#define FIDO_ERR_INVALID_COMMAND 0x01
12#define FIDO_ERR_INVALID_PARAMETER 0x02
13#define FIDO_ERR_INVALID_LENGTH 0x03
14#define FIDO_ERR_INVALID_SEQ 0x04
15#define FIDO_ERR_TIMEOUT 0x05
16#define FIDO_ERR_CHANNEL_BUSY 0x06
17#define FIDO_ERR_LOCK_REQUIRED 0x0a
18#define FIDO_ERR_INVALID_CHANNEL 0x0b
19#define FIDO_ERR_CBOR_UNEXPECTED_TYPE 0x11
20#define FIDO_ERR_INVALID_CBOR 0x12
21#define FIDO_ERR_MISSING_PARAMETER 0x14
22#define FIDO_ERR_LIMIT_EXCEEDED 0x15
23#define FIDO_ERR_UNSUPPORTED_EXTENSION 0x16
24#define FIDO_ERR_CREDENTIAL_EXCLUDED 0x19
25#define FIDO_ERR_PROCESSING 0x21
26#define FIDO_ERR_INVALID_CREDENTIAL 0x22
27#define FIDO_ERR_USER_ACTION_PENDING 0x23
28#define FIDO_ERR_OPERATION_PENDING 0x24
29#define FIDO_ERR_NO_OPERATIONS 0x25
30#define FIDO_ERR_UNSUPPORTED_ALGORITHM 0x26
31#define FIDO_ERR_OPERATION_DENIED 0x27
32#define FIDO_ERR_KEY_STORE_FULL 0x28
33#define FIDO_ERR_NOT_BUSY 0x29
34#define FIDO_ERR_NO_OPERATION_PENDING 0x2a
35#define FIDO_ERR_UNSUPPORTED_OPTION 0x2b
36#define FIDO_ERR_INVALID_OPTION 0x2c
37#define FIDO_ERR_KEEPALIVE_CANCEL 0x2d
38#define FIDO_ERR_NO_CREDENTIALS 0x2e
39#define FIDO_ERR_USER_ACTION_TIMEOUT 0x2f
40#define FIDO_ERR_NOT_ALLOWED 0x30
41#define FIDO_ERR_PIN_INVALID 0x31
42#define FIDO_ERR_PIN_BLOCKED 0x32
43#define FIDO_ERR_PIN_AUTH_INVALID 0x33
44#define FIDO_ERR_PIN_AUTH_BLOCKED 0x34
45#define FIDO_ERR_PIN_NOT_SET 0x35
46#define FIDO_ERR_PIN_REQUIRED 0x36
47#define FIDO_ERR_PIN_POLICY_VIOLATION 0x37
48#define FIDO_ERR_PIN_TOKEN_EXPIRED 0x38
49#define FIDO_ERR_REQUEST_TOO_LARGE 0x39
50#define FIDO_ERR_ACTION_TIMEOUT 0x3a
51#define FIDO_ERR_UP_REQUIRED 0x3b
52#define FIDO_ERR_ERR_OTHER 0x7f
53#define FIDO_ERR_SPEC_LAST 0xdf
54
55/* defined internally */
56#define FIDO_OK FIDO_ERR_SUCCESS
57#define FIDO_ERR_TX -1
58#define FIDO_ERR_RX -2
59#define FIDO_ERR_RX_NOT_CBOR -3
60#define FIDO_ERR_RX_INVALID_CBOR -4
61#define FIDO_ERR_INVALID_PARAM -5
62#define FIDO_ERR_INVALID_SIG -6
63#define FIDO_ERR_INVALID_ARGUMENT -7
64#define FIDO_ERR_USER_PRESENCE_REQUIRED -8
65#define FIDO_ERR_INTERNAL -9
66
67const char *fido_strerr(int);
68
69#endif /* _FIDO_ERR_H */
diff --git a/src/fido/es256.h b/src/fido/es256.h
new file mode 100644
index 0000000..d3d13dd
--- /dev/null
+++ b/src/fido/es256.h
@@ -0,0 +1,34 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_ES256_H
8#define _FIDO_ES256_H
9
10#include <openssl/ec.h>
11
12#include <stdint.h>
13#include <stdlib.h>
14
15es256_pk_t *es256_pk_new(void);
16void es256_pk_free(es256_pk_t **);
17EVP_PKEY *es256_pk_to_EVP_PKEY(const es256_pk_t *);
18
19int es256_pk_from_EC_KEY(es256_pk_t *, const EC_KEY *);
20int es256_pk_from_ptr(es256_pk_t *, const void *, size_t);
21
22#ifdef _FIDO_INTERNAL
23es256_sk_t *es256_sk_new(void);
24void es256_sk_free(es256_sk_t **);
25EVP_PKEY *es256_sk_to_EVP_PKEY(const es256_sk_t *);
26
27int es256_derive_pk(const es256_sk_t *, es256_pk_t *);
28int es256_sk_create(es256_sk_t *);
29
30int es256_pk_set_x(es256_pk_t *, const unsigned char *);
31int es256_pk_set_y(es256_pk_t *, const unsigned char *);
32#endif
33
34#endif /* !_FIDO_ES256_H */
diff --git a/src/fido/param.h b/src/fido/param.h
new file mode 100644
index 0000000..9e12ac6
--- /dev/null
+++ b/src/fido/param.h
@@ -0,0 +1,84 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_PARAM_H
8#define _FIDO_PARAM_H
9
10/* Authentication data flags. */
11#define CTAP_AUTHDATA_USER_PRESENT 0x01
12#define CTAP_AUTHDATA_USER_VERIFIED 0x04
13#define CTAP_AUTHDATA_ATT_CRED 0x40
14#define CTAP_AUTHDATA_EXT_DATA 0x80
15
16/* CTAPHID command opcodes. */
17#define CTAP_CMD_PING 0x01
18#define CTAP_CMD_MSG 0x03
19#define CTAP_CMD_LOCK 0x04
20#define CTAP_CMD_INIT 0x06
21#define CTAP_CMD_WINK 0x08
22#define CTAP_CMD_CBOR 0x10
23#define CTAP_CMD_CANCEL 0x11
24#define CTAP_KEEPALIVE 0x3b
25#define CTAP_FRAME_INIT 0x80
26
27/* CTAPHID CBOR command opcodes. */
28#define CTAP_CBOR_MAKECRED 0x01
29#define CTAP_CBOR_ASSERT 0x02
30#define CTAP_CBOR_GETINFO 0x04
31#define CTAP_CBOR_CLIENT_PIN 0x06
32#define CTAP_CBOR_RESET 0x07
33#define CTAP_CBOR_NEXT_ASSERT 0x08
34#define CTAP_CBOR_BIO_ENROLL_PRE 0x40
35#define CTAP_CBOR_CRED_MGMT_PRE 0x41
36
37/* U2F command opcodes. */
38#define U2F_CMD_REGISTER 0x01
39#define U2F_CMD_AUTH 0x02
40
41/* U2F command flags. */
42#define U2F_AUTH_SIGN 0x03
43#define U2F_AUTH_CHECK 0x07
44
45/* ISO7816-4 status words. */
46#define SW_CONDITIONS_NOT_SATISFIED 0x6985
47#define SW_WRONG_DATA 0x6a80
48#define SW_NO_ERROR 0x9000
49
50/* HID Broadcast channel ID. */
51#define CTAP_CID_BROADCAST 0xffffffff
52
53/* Expected size of a HID report in bytes. */
54#define CTAP_RPT_SIZE 64
55
56/* Randomness device on UNIX-like platforms. */
57#ifndef FIDO_RANDOM_DEV
58#define FIDO_RANDOM_DEV "/dev/urandom"
59#endif
60
61/* CTAP capability bits. */
62#define FIDO_CAP_WINK 0x01 /* if set, device supports CTAP_CMD_WINK */
63#define FIDO_CAP_CBOR 0x04 /* if set, device supports CTAP_CMD_CBOR */
64#define FIDO_CAP_NMSG 0x08 /* if set, device doesn't support CTAP_CMD_MSG */
65
66/* Supported COSE algorithms. */
67#define COSE_ES256 -7
68#define COSE_EDDSA -8
69#define COSE_ECDH_ES256 -25
70#define COSE_RS256 -257
71
72/* Supported COSE types. */
73#define COSE_KTY_OKP 1
74#define COSE_KTY_EC2 2
75#define COSE_KTY_RSA 3
76
77/* Supported curves. */
78#define COSE_P256 1
79#define COSE_ED25519 6
80
81/* Supported extensions. */
82#define FIDO_EXT_HMAC_SECRET 0x01
83
84#endif /* !_FIDO_PARAM_H */
diff --git a/src/fido/rs256.h b/src/fido/rs256.h
new file mode 100644
index 0000000..d2fa162
--- /dev/null
+++ b/src/fido/rs256.h
@@ -0,0 +1,22 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_RS256_H
8#define _FIDO_RS256_H
9
10#include <openssl/rsa.h>
11
12#include <stdint.h>
13#include <stdlib.h>
14
15rs256_pk_t *rs256_pk_new(void);
16void rs256_pk_free(rs256_pk_t **);
17EVP_PKEY *rs256_pk_to_EVP_PKEY(const rs256_pk_t *);
18
19int rs256_pk_from_RSA(rs256_pk_t *, const RSA *);
20int rs256_pk_from_ptr(rs256_pk_t *, const void *, size_t);
21
22#endif /* !_FIDO_RS256_H */
diff --git a/src/hid.c b/src/hid.c
new file mode 100644
index 0000000..783d22c
--- /dev/null
+++ b/src/hid.c
@@ -0,0 +1,70 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8#include "fido.h"
9
10fido_dev_info_t *
11fido_dev_info_new(size_t n)
12{
13 return (calloc(n, sizeof(fido_dev_info_t)));
14}
15
16void
17fido_dev_info_free(fido_dev_info_t **devlist_p, size_t n)
18{
19 fido_dev_info_t *devlist;
20
21 if (devlist_p == NULL || (devlist = *devlist_p) == NULL)
22 return;
23
24 for (size_t i = 0; i < n; i++) {
25 const fido_dev_info_t *di = &devlist[i];
26 free(di->path);
27 free(di->manufacturer);
28 free(di->product);
29 }
30
31 free(devlist);
32
33 *devlist_p = NULL;
34}
35
36const fido_dev_info_t *
37fido_dev_info_ptr(const fido_dev_info_t *devlist, size_t i)
38{
39 return (&devlist[i]);
40}
41
42const char *
43fido_dev_info_path(const fido_dev_info_t *di)
44{
45 return (di->path);
46}
47
48int16_t
49fido_dev_info_vendor(const fido_dev_info_t *di)
50{
51 return (di->vendor_id);
52}
53
54int16_t
55fido_dev_info_product(const fido_dev_info_t *di)
56{
57 return (di->product_id);
58}
59
60const char *
61fido_dev_info_manufacturer_string(const fido_dev_info_t *di)
62{
63 return (di->manufacturer);
64}
65
66const char *
67fido_dev_info_product_string(const fido_dev_info_t *di)
68{
69 return (di->product);
70}
diff --git a/src/hid_linux.c b/src/hid_linux.c
new file mode 100644
index 0000000..c7cabc9
--- /dev/null
+++ b/src/hid_linux.c
@@ -0,0 +1,344 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <sys/types.h>
8
9#include <sys/ioctl.h>
10#include <linux/hidraw.h>
11
12#include <fcntl.h>
13#include <libudev.h>
14#include <string.h>
15#include <unistd.h>
16
17#include "fido.h"
18
19#define REPORT_LEN 65
20
21static int
22get_key_len(uint8_t tag, uint8_t *key, size_t *key_len)
23{
24 *key = tag & 0xfc;
25 if ((*key & 0xf0) == 0xf0) {
26 fido_log_debug("%s: *key=0x%02x", __func__, *key);
27 return (-1);
28 }
29
30 *key_len = tag & 0x3;
31 if (*key_len == 3) {
32 *key_len = 4;
33 }
34
35 return (0);
36}
37
38static int
39get_key_val(const void *body, size_t key_len, uint32_t *val)
40{
41 const uint8_t *ptr = body;
42
43 switch (key_len) {
44 case 0:
45 *val = 0;
46 break;
47 case 1:
48 *val = ptr[0];
49 break;
50 case 2:
51 *val = (uint32_t)((ptr[1] << 8) | ptr[0]);
52 break;
53 default:
54 fido_log_debug("%s: key_len=%zu", __func__, key_len);
55 return (-1);
56 }
57
58 return (0);
59}
60
61static int
62get_usage_info(const struct hidraw_report_descriptor *hrd, uint32_t *usage_page,
63 uint32_t *usage)
64{
65 const uint8_t *ptr;
66 size_t len;
67
68 ptr = hrd->value;
69 len = hrd->size;
70
71 while (len > 0) {
72 const uint8_t tag = ptr[0];
73 ptr++;
74 len--;
75
76 uint8_t key;
77 size_t key_len;
78 uint32_t key_val;
79
80 if (get_key_len(tag, &key, &key_len) < 0 || key_len > len ||
81 get_key_val(ptr, key_len, &key_val) < 0) {
82 return (-1);
83 }
84
85 if (key == 0x4) {
86 *usage_page = key_val;
87 } else if (key == 0x8) {
88 *usage = key_val;
89 }
90
91 ptr += key_len;
92 len -= key_len;
93 }
94
95 return (0);
96}
97
98static int
99get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd)
100{
101 int r;
102 int s = -1;
103 int fd;
104 int ok = -1;
105
106 if ((fd = open(path, O_RDONLY)) < 0) {
107 fido_log_debug("%s: open", __func__);
108 return (-1);
109 }
110
111 if ((r = ioctl(fd, HIDIOCGRDESCSIZE, &s)) < 0 || s < 0 ||
112 (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) {
113 fido_log_debug("%s: ioctl HIDIOCGRDESCSIZE", __func__);
114 goto fail;
115 }
116
117 hrd->size = s;
118
119 if ((r = ioctl(fd, HIDIOCGRDESC, hrd)) < 0) {
120 fido_log_debug("%s: ioctl HIDIOCGRDESC", __func__);
121 goto fail;
122 }
123
124 ok = 0;
125fail:
126 if (fd != -1)
127 close(fd);
128
129 return (ok);
130}
131
132static bool
133is_fido(const char *path)
134{
135 uint32_t usage = 0;
136 uint32_t usage_page = 0;
137 struct hidraw_report_descriptor hrd;
138
139 memset(&hrd, 0, sizeof(hrd));
140
141 if (get_report_descriptor(path, &hrd) < 0 ||
142 get_usage_info(&hrd, &usage_page, &usage) < 0) {
143 return (false);
144 }
145
146 return (usage_page == 0xf1d0);
147}
148
149static int
150parse_uevent(struct udev_device *dev, int16_t *vendor_id, int16_t *product_id)
151{
152 const char *uevent;
153 char *cp;
154 char *p;
155 char *s;
156 int ok = -1;
157 short unsigned int x;
158 short unsigned int y;
159
160 if ((uevent = udev_device_get_sysattr_value(dev, "uevent")) == NULL)
161 return (-1);
162
163 if ((s = cp = strdup(uevent)) == NULL)
164 return (-1);
165
166 for ((p = strsep(&cp, "\n")); p && *p != '\0'; (p = strsep(&cp, "\n"))) {
167 if (strncmp(p, "HID_ID=", 7) == 0) {
168 if (sscanf(p + 7, "%*x:%hx:%hx", &x, &y) == 2) {
169 *vendor_id = (int16_t)x;
170 *product_id = (int16_t)y;
171 ok = 0;
172 }
173 break;
174 }
175 }
176
177 free(s);
178
179 return (ok);
180}
181
182static int
183copy_info(fido_dev_info_t *di, struct udev *udev,
184 struct udev_list_entry *udev_entry)
185{
186 const char *name;
187 const char *path;
188 const char *manufacturer;
189 const char *product;
190 struct udev_device *dev = NULL;
191 struct udev_device *hid_parent;
192 struct udev_device *usb_parent;
193 int ok = -1;
194
195 memset(di, 0, sizeof(*di));
196
197 if ((name = udev_list_entry_get_name(udev_entry)) == NULL ||
198 (dev = udev_device_new_from_syspath(udev, name)) == NULL ||
199 (path = udev_device_get_devnode(dev)) == NULL ||
200 is_fido(path) == 0)
201 goto fail;
202
203 if ((hid_parent = udev_device_get_parent_with_subsystem_devtype(dev,
204 "hid", NULL)) == NULL)
205 goto fail;
206
207 if ((usb_parent = udev_device_get_parent_with_subsystem_devtype(dev,
208 "usb", "usb_device")) == NULL)
209 goto fail;
210
211 if (parse_uevent(hid_parent, &di->vendor_id, &di->product_id) < 0 ||
212 (manufacturer = udev_device_get_sysattr_value(usb_parent,
213 "manufacturer")) == NULL ||
214 (product = udev_device_get_sysattr_value(usb_parent,
215 "product")) == NULL)
216 goto fail;
217
218 di->path = strdup(path);
219 di->manufacturer = strdup(manufacturer);
220 di->product = strdup(product);
221
222 if (di->path == NULL ||
223 di->manufacturer == NULL ||
224 di->product == NULL)
225 goto fail;
226
227 ok = 0;
228fail:
229 if (dev != NULL)
230 udev_device_unref(dev);
231
232 if (ok < 0) {
233 free(di->path);
234 free(di->manufacturer);
235 free(di->product);
236 explicit_bzero(di, sizeof(*di));
237 }
238
239 return (ok);
240}
241
242int
243fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
244{
245 struct udev *udev = NULL;
246 struct udev_enumerate *udev_enum = NULL;
247 struct udev_list_entry *udev_list;
248 struct udev_list_entry *udev_entry;
249 int r = FIDO_ERR_INTERNAL;
250
251 *olen = 0;
252
253 if (ilen == 0)
254 return (FIDO_OK); /* nothing to do */
255
256 if (devlist == NULL)
257 return (FIDO_ERR_INVALID_ARGUMENT);
258
259 if ((udev = udev_new()) == NULL ||
260 (udev_enum = udev_enumerate_new(udev)) == NULL)
261 goto fail;
262
263 if (udev_enumerate_add_match_subsystem(udev_enum, "hidraw") < 0 ||
264 udev_enumerate_scan_devices(udev_enum) < 0 ||
265 (udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL)
266 goto fail;
267
268 udev_list_entry_foreach(udev_entry, udev_list) {
269 if (copy_info(&devlist[*olen], udev, udev_entry) == 0) {
270 if (++(*olen) == ilen)
271 break;
272 }
273 }
274
275 r = FIDO_OK;
276fail:
277 if (udev_enum != NULL)
278 udev_enumerate_unref(udev_enum);
279 if (udev != NULL)
280 udev_unref(udev);
281
282 return (r);
283}
284
285void *
286fido_hid_open(const char *path)
287{
288 int *fd;
289
290 if ((fd = malloc(sizeof(*fd))) == NULL ||
291 (*fd = open(path, O_RDWR)) < 0) {
292 free(fd);
293 return (NULL);
294 }
295
296 return (fd);
297}
298
299void
300fido_hid_close(void *handle)
301{
302 int *fd = handle;
303
304 close(*fd);
305 free(fd);
306}
307
308int
309fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
310{
311 int *fd = handle;
312 ssize_t r;
313
314 (void)ms; /* XXX */
315
316 if (len != REPORT_LEN - 1) {
317 fido_log_debug("%s: invalid len", __func__);
318 return (-1);
319 }
320
321 if ((r = read(*fd, buf, len)) < 0 || r != REPORT_LEN - 1)
322 return (-1);
323
324 return (REPORT_LEN - 1);
325}
326
327int
328fido_hid_write(void *handle, const unsigned char *buf, size_t len)
329{
330 int *fd = handle;
331 ssize_t r;
332
333 if (len != REPORT_LEN) {
334 fido_log_debug("%s: invalid len", __func__);
335 return (-1);
336 }
337
338 if ((r = write(*fd, buf, len)) < 0 || r != REPORT_LEN) {
339 fido_log_debug("%s: write", __func__);
340 return (-1);
341 }
342
343 return (REPORT_LEN);
344}
diff --git a/src/hid_openbsd.c b/src/hid_openbsd.c
new file mode 100644
index 0000000..8b92bd6
--- /dev/null
+++ b/src/hid_openbsd.c
@@ -0,0 +1,277 @@
1/*
2 * Copyright (c) 2019 Google LLC. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <sys/types.h>
8
9#include <sys/ioctl.h>
10#include <dev/usb/usb.h>
11#include <dev/usb/usbhid.h>
12
13#include <errno.h>
14#include <fcntl.h>
15#include <string.h>
16#include <unistd.h>
17#include <usbhid.h>
18#include <poll.h>
19
20#include "fido.h"
21
22#define MAX_UHID 64
23#define MAX_REPORT_LEN (sizeof(((struct usb_ctl_report *)(NULL))->ucr_data))
24
25struct hid_openbsd {
26 int fd;
27 size_t report_in_len;
28 size_t report_out_len;
29};
30
31int
32fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
33{
34 size_t i;
35 char path[64];
36 int is_fido, fd;
37 struct usb_device_info udi;
38 report_desc_t rdesc = NULL;
39 hid_data_t hdata = NULL;
40 hid_item_t hitem;
41 fido_dev_info_t *di;
42
43 if (ilen == 0)
44 return (FIDO_OK); /* nothing to do */
45
46 if (devlist == NULL || olen == NULL)
47 return (FIDO_ERR_INVALID_ARGUMENT);
48
49 for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) {
50 snprintf(path, sizeof(path), "/dev/uhid%zu", i);
51 if ((fd = open(path, O_RDWR)) == -1) {
52 if (errno != ENOENT && errno != ENXIO) {
53 fido_log_debug("%s: open %s: %s", __func__,
54 path, strerror(errno));
55 }
56 continue;
57 }
58 memset(&udi, 0, sizeof(udi));
59 if (ioctl(fd, USB_GET_DEVICEINFO, &udi) != 0) {
60 fido_log_debug("%s: get device info %s: %s", __func__,
61 path, strerror(errno));
62 close(fd);
63 continue;
64 }
65 if ((rdesc = hid_get_report_desc(fd)) == NULL) {
66 fido_log_debug("%s: failed to get report descriptor: %s",
67 __func__, path);
68 close(fd);
69 continue;
70 }
71 if ((hdata = hid_start_parse(rdesc,
72 1<<hid_collection, -1)) == NULL) {
73 fido_log_debug("%s: failed to parse report descriptor: %s",
74 __func__, path);
75 hid_dispose_report_desc(rdesc);
76 close(fd);
77 continue;
78 }
79 is_fido = 0;
80 for (is_fido = 0; !is_fido;) {
81 memset(&hitem, 0, sizeof(hitem));
82 if (hid_get_item(hdata, &hitem) <= 0)
83 break;
84 if ((hitem._usage_page & 0xFFFF0000) == 0xf1d00000)
85 is_fido = 1;
86 }
87 hid_end_parse(hdata);
88 hid_dispose_report_desc(rdesc);
89 close(fd);
90
91 if (!is_fido)
92 continue;
93
94 fido_log_debug("%s: %s: bus = 0x%02x, addr = 0x%02x",
95 __func__, path, udi.udi_bus, udi.udi_addr);
96 fido_log_debug("%s: %s: vendor = \"%s\", product = \"%s\"",
97 __func__, path, udi.udi_vendor, udi.udi_product);
98 fido_log_debug("%s: %s: productNo = 0x%04x, vendorNo = 0x%04x, "
99 "releaseNo = 0x%04x", __func__, path, udi.udi_productNo,
100 udi.udi_vendorNo, udi.udi_releaseNo);
101
102 di = &devlist[*olen];
103 memset(di, 0, sizeof(*di));
104 if ((di->path = strdup(path)) == NULL ||
105 (di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
106 (di->product = strdup(udi.udi_product)) == NULL) {
107 free(di->path);
108 free(di->manufacturer);
109 free(di->product);
110 explicit_bzero(di, sizeof(*di));
111 return FIDO_ERR_INTERNAL;
112 }
113 di->vendor_id = udi.udi_vendorNo;
114 di->product_id = udi.udi_productNo;
115 (*olen)++;
116 }
117
118 return FIDO_OK;
119}
120
121/*
122 * Workaround for OpenBSD <=6.6-current (as of 201910) bug that loses
123 * sync of DATA0/DATA1 sequence bit across uhid open/close.
124 * Send pings until we get a response - early pings with incorrect
125 * sequence bits will be ignored as duplicate packets by the device.
126 */
127static int
128terrible_ping_kludge(struct hid_openbsd *ctx)
129{
130 u_char data[256];
131 int i, n;
132 struct pollfd pfd;
133
134 if (sizeof(data) < ctx->report_out_len + 1)
135 return -1;
136 for (i = 0; i < 4; i++) {
137 memset(data, 0, sizeof(data));
138 /* broadcast channel ID */
139 data[1] = 0xff;
140 data[2] = 0xff;
141 data[3] = 0xff;
142 data[4] = 0xff;
143 /* Ping command */
144 data[5] = 0x81;
145 /* One byte ping only, Vasili */
146 data[6] = 0;
147 data[7] = 1;
148 fido_log_debug("%s: send ping %d", __func__, i);
149 if (fido_hid_write(ctx, data, ctx->report_out_len + 1) == -1)
150 return -1;
151 fido_log_debug("%s: wait reply", __func__);
152 memset(&pfd, 0, sizeof(pfd));
153 pfd.fd = ctx->fd;
154 pfd.events = POLLIN;
155 if ((n = poll(&pfd, 1, 100)) == -1) {
156 fido_log_debug("%s: poll: %s", __func__, strerror(errno));
157 return -1;
158 } else if (n == 0) {
159 fido_log_debug("%s: timed out", __func__);
160 continue;
161 }
162 if (fido_hid_read(ctx, data, ctx->report_out_len, 250) == -1)
163 return -1;
164 /*
165 * Ping isn't always supported on the broadcast channel,
166 * so we might get an error, but we don't care - we're
167 * synched now.
168 */
169 fido_log_debug("%s: got reply", __func__);
170 fido_log_xxd(data, ctx->report_out_len);
171 return 0;
172 }
173 fido_log_debug("%s: no response", __func__);
174 return -1;
175}
176
177void *
178fido_hid_open(const char *path)
179{
180 struct hid_openbsd *ret = NULL;
181 report_desc_t rdesc = NULL;
182 int len, usb_report_id = 0;
183
184 if ((ret = calloc(1, sizeof(*ret))) == NULL ||
185 (ret->fd = open(path, O_RDWR)) < 0) {
186 free(ret);
187 return (NULL);
188 }
189 if (ioctl(ret->fd, USB_GET_REPORT_ID, &usb_report_id) != 0) {
190 fido_log_debug("%s: failed to get report ID: %s", __func__,
191 strerror(errno));
192 goto fail;
193 }
194 if ((rdesc = hid_get_report_desc(ret->fd)) == NULL) {
195 fido_log_debug("%s: failed to get report descriptor", __func__);
196 goto fail;
197 }
198 if ((len = hid_report_size(rdesc, hid_input, usb_report_id)) <= 0 ||
199 (size_t)len > MAX_REPORT_LEN) {
200 fido_log_debug("%s: bad input report size %d", __func__, len);
201 goto fail;
202 }
203 ret->report_in_len = (size_t)len;
204 if ((len = hid_report_size(rdesc, hid_output, usb_report_id)) <= 0 ||
205 (size_t)len > MAX_REPORT_LEN) {
206 fido_log_debug("%s: bad output report size %d", __func__, len);
207 fail:
208 hid_dispose_report_desc(rdesc);
209 close(ret->fd);
210 free(ret);
211 return NULL;
212 }
213 ret->report_out_len = (size_t)len;
214 hid_dispose_report_desc(rdesc);
215 fido_log_debug("%s: USB report ID %d, inlen = %zu outlen = %zu",
216 __func__, usb_report_id, ret->report_in_len, ret->report_out_len);
217
218 /*
219 * OpenBSD (as of 201910) has a bug that causes it to lose
220 * track of the DATA0/DATA1 sequence toggle across uhid device
221 * open and close. This is a terrible hack to work around it.
222 */
223 if (terrible_ping_kludge(ret) != 0) {
224 fido_hid_close(ret);
225 return NULL;
226 }
227
228 return (ret);
229}
230
231void
232fido_hid_close(void *handle)
233{
234 struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
235
236 close(ctx->fd);
237 free(ctx);
238}
239
240int
241fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
242{
243 struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
244 ssize_t r;
245
246 (void)ms; /* XXX */
247
248 if (len != ctx->report_in_len) {
249 fido_log_debug("%s: invalid len: got %zu, want %zu", __func__,
250 len, ctx->report_in_len);
251 return (-1);
252 }
253 if ((r = read(ctx->fd, buf, len)) == -1 || (size_t)r != len) {
254 fido_log_debug("%s: read: %s", __func__, strerror(errno));
255 return (-1);
256 }
257 return ((int)len);
258}
259
260int
261fido_hid_write(void *handle, const unsigned char *buf, size_t len)
262{
263 struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
264 ssize_t r;
265
266 if (len != ctx->report_out_len + 1) {
267 fido_log_debug("%s: invalid len: got %zu, want %zu", __func__,
268 len, ctx->report_out_len);
269 return (-1);
270 }
271 if ((r = write(ctx->fd, buf + 1, len - 1)) == -1 ||
272 (size_t)r != len - 1) {
273 fido_log_debug("%s: write: %s", __func__, strerror(errno));
274 return (-1);
275 }
276 return ((int)len);
277}
diff --git a/src/hid_osx.c b/src/hid_osx.c
new file mode 100644
index 0000000..b705b43
--- /dev/null
+++ b/src/hid_osx.c
@@ -0,0 +1,410 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <sys/types.h>
8
9#include <fcntl.h>
10#include <string.h>
11#ifdef HAVE_UNISTD_H
12#include <unistd.h>
13#endif
14
15#include <CoreFoundation/CoreFoundation.h>
16#include <IOKit/IOKitLib.h>
17#include <IOKit/hid/IOHIDKeys.h>
18#include <IOKit/hid/IOHIDManager.h>
19
20#include "fido.h"
21
22#define REPORT_LEN 65
23
24struct dev {
25 IOHIDDeviceRef ref;
26 CFStringRef loop_id;
27};
28
29static int
30get_int32(IOHIDDeviceRef dev, CFStringRef key, int32_t *v)
31{
32 CFTypeRef ref;
33
34 if ((ref = IOHIDDeviceGetProperty(dev, key)) == NULL ||
35 CFGetTypeID(ref) != CFNumberGetTypeID()) {
36 fido_log_debug("%s: IOHIDDeviceGetProperty", __func__);
37 return (-1);
38 }
39
40 if (CFNumberGetType(ref) != kCFNumberSInt32Type &&
41 CFNumberGetType(ref) != kCFNumberSInt64Type) {
42 fido_log_debug("%s: CFNumberGetType", __func__);
43 return (-1);
44 }
45
46 if (CFNumberGetValue(ref, kCFNumberSInt32Type, v) == false) {
47 fido_log_debug("%s: CFNumberGetValue", __func__);
48 return (-1);
49 }
50
51 return (0);
52}
53
54static int
55get_utf8(IOHIDDeviceRef dev, CFStringRef key, void *buf, size_t len)
56{
57 CFTypeRef ref;
58
59 memset(buf, 0, len);
60
61 if ((ref = IOHIDDeviceGetProperty(dev, key)) == NULL ||
62 CFGetTypeID(ref) != CFStringGetTypeID()) {
63 fido_log_debug("%s: IOHIDDeviceGetProperty", __func__);
64 return (-1);
65 }
66
67 if (CFStringGetCString(ref, buf, len, kCFStringEncodingUTF8) == false) {
68 fido_log_debug("%s: CFStringGetCString", __func__);
69 return (-1);
70 }
71
72 return (0);
73}
74
75static bool
76is_fido(IOHIDDeviceRef dev)
77{
78 uint32_t usage_page;
79 int32_t report_len;
80
81 if (get_int32(dev, CFSTR(kIOHIDPrimaryUsagePageKey),
82 (int32_t *)&usage_page) != 0 || usage_page != 0xf1d0)
83 return (false);
84
85 if (get_int32(dev, CFSTR(kIOHIDMaxInputReportSizeKey),
86 &report_len) < 0 || report_len != REPORT_LEN - 1) {
87 fido_log_debug("%s: unsupported report len", __func__);
88 return (false);
89 }
90
91 return (true);
92}
93
94static int
95get_id(IOHIDDeviceRef dev, int16_t *vendor_id, int16_t *product_id)
96{
97 int32_t vendor;
98 int32_t product;
99
100 if (get_int32(dev, CFSTR(kIOHIDVendorIDKey), &vendor) < 0 ||
101 vendor > UINT16_MAX) {
102 fido_log_debug("%s: get_int32 vendor", __func__);
103 return (-1);
104 }
105
106 if (get_int32(dev, CFSTR(kIOHIDProductIDKey), &product) < 0 ||
107 product > UINT16_MAX) {
108 fido_log_debug("%s: get_int32 product", __func__);
109 return (-1);
110 }
111
112 *vendor_id = (int16_t)vendor;
113 *product_id = (int16_t)product;
114
115 return (0);
116}
117
118static int
119get_str(IOHIDDeviceRef dev, char **manufacturer, char **product)
120{
121 char buf[512];
122 int ok = -1;
123
124 *manufacturer = NULL;
125 *product = NULL;
126
127 if (get_utf8(dev, CFSTR(kIOHIDManufacturerKey), buf, sizeof(buf)) < 0) {
128 fido_log_debug("%s: get_utf8 manufacturer", __func__);
129 goto fail;
130 }
131
132 if ((*manufacturer = strdup(buf)) == NULL) {
133 fido_log_debug("%s: strdup manufacturer", __func__);
134 goto fail;
135 }
136
137 if (get_utf8(dev, CFSTR(kIOHIDProductKey), buf, sizeof(buf)) < 0) {
138 fido_log_debug("%s: get_utf8 product", __func__);
139 goto fail;
140 }
141
142 if ((*product = strdup(buf)) == NULL) {
143 fido_log_debug("%s: strdup product", __func__);
144 goto fail;
145 }
146
147 ok = 0;
148fail:
149 if (ok < 0) {
150 free(*manufacturer);
151 free(*product);
152 *manufacturer = NULL;
153 *product = NULL;
154 }
155
156 return (ok);
157}
158
159static char *
160get_path(IOHIDDeviceRef dev)
161{
162 io_service_t s;
163 io_string_t path;
164
165 if ((s = IOHIDDeviceGetService(dev)) == MACH_PORT_NULL) {
166 fido_log_debug("%s: IOHIDDeviceGetService", __func__);
167 return (NULL);
168 }
169
170 if (IORegistryEntryGetPath(s, kIOServicePlane, path) != KERN_SUCCESS) {
171 fido_log_debug("%s: IORegistryEntryGetPath", __func__);
172 return (NULL);
173 }
174
175 return (strdup(path));
176}
177
178static int
179copy_info(fido_dev_info_t *di, IOHIDDeviceRef dev)
180{
181 memset(di, 0, sizeof(*di));
182
183 if (is_fido(dev) == false)
184 return (-1);
185
186 if (get_id(dev, &di->vendor_id, &di->product_id) < 0 ||
187 get_str(dev, &di->manufacturer, &di->product) < 0 ||
188 (di->path = get_path(dev)) == NULL) {
189 free(di->path);
190 free(di->manufacturer);
191 free(di->product);
192 explicit_bzero(di, sizeof(*di));
193 return (-1);
194 }
195
196 return (0);
197}
198
199int
200fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
201{
202 IOHIDManagerRef manager = NULL;
203 CFSetRef devset = NULL;
204 CFIndex devcnt;
205 IOHIDDeviceRef *devs = NULL;
206 int r = FIDO_ERR_INTERNAL;
207
208 *olen = 0;
209
210 if (ilen == 0)
211 return (FIDO_OK); /* nothing to do */
212
213 if (devlist == NULL)
214 return (FIDO_ERR_INVALID_ARGUMENT);
215
216 if ((manager = IOHIDManagerCreate(kCFAllocatorDefault,
217 kIOHIDManagerOptionNone)) == NULL) {
218 fido_log_debug("%s: IOHIDManagerCreate", __func__);
219 goto fail;
220 }
221
222 IOHIDManagerSetDeviceMatching(manager, NULL);
223
224 if ((devset = IOHIDManagerCopyDevices(manager)) == NULL) {
225 fido_log_debug("%s: IOHIDManagerCopyDevices", __func__);
226 goto fail;
227 }
228
229 if ((devcnt = CFSetGetCount(devset)) < 0) {
230 fido_log_debug("%s: CFSetGetCount", __func__);
231 goto fail;
232 }
233
234 if ((devs = calloc(devcnt, sizeof(*devs))) == NULL) {
235 fido_log_debug("%s: calloc", __func__);
236 goto fail;
237 }
238
239 CFSetGetValues(devset, (void *)devs);
240
241 for (CFIndex i = 0; i < devcnt; i++) {
242 if (copy_info(&devlist[*olen], devs[i]) == 0) {
243 if (++(*olen) == ilen)
244 break;
245 }
246 }
247
248 r = FIDO_OK;
249fail:
250 if (manager != NULL)
251 CFRelease(manager);
252 if (devset != NULL)
253 CFRelease(devset);
254
255 free(devs);
256
257 return (r);
258}
259
260void *
261fido_hid_open(const char *path)
262{
263 io_registry_entry_t entry = MACH_PORT_NULL;
264 struct dev *dev = NULL;
265 int ok = -1;
266 int r;
267 char loop_id[32];
268
269 if ((dev = calloc(1, sizeof(*dev))) == NULL) {
270 fido_log_debug("%s: calloc", __func__);
271 goto fail;
272 }
273
274 if ((entry = IORegistryEntryFromPath(kIOMasterPortDefault,
275 path)) == MACH_PORT_NULL) {
276 fido_log_debug("%s: IORegistryEntryFromPath", __func__);
277 goto fail;
278 }
279
280 if ((dev->ref = IOHIDDeviceCreate(kCFAllocatorDefault,
281 entry)) == NULL) {
282 fido_log_debug("%s: IOHIDDeviceCreate", __func__);
283 goto fail;
284 }
285
286 if (IOHIDDeviceOpen(dev->ref,
287 kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) {
288 fido_log_debug("%s: IOHIDDeviceOpen", __func__);
289 goto fail;
290 }
291
292 if ((r = snprintf(loop_id, sizeof(loop_id), "fido2-%p",
293 (void *)dev->ref)) < 0 || (size_t)r >= sizeof(loop_id)) {
294 fido_log_debug("%s: snprintf", __func__);
295 goto fail;
296 }
297
298 if ((dev->loop_id = CFStringCreateWithCString(NULL, loop_id,
299 kCFStringEncodingASCII)) == NULL) {
300 fido_log_debug("%s: CFStringCreateWithCString", __func__);
301 goto fail;
302 }
303
304 ok = 0;
305fail:
306 if (entry != MACH_PORT_NULL)
307 IOObjectRelease(entry);
308
309 if (ok < 0 && dev != NULL) {
310 if (dev->ref != NULL)
311 CFRelease(dev->ref);
312 if (dev->loop_id != NULL)
313 CFRelease(dev->loop_id);
314 free(dev);
315 dev = NULL;
316 }
317
318 return (dev);
319}
320
321void
322fido_hid_close(void *handle)
323{
324 struct dev *dev = handle;
325
326 if (IOHIDDeviceClose(dev->ref,
327 kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess)
328 fido_log_debug("%s: IOHIDDeviceClose", __func__);
329
330 CFRelease(dev->ref);
331 CFRelease(dev->loop_id);
332
333 free(dev);
334}
335
336static void
337read_callback(void *context, IOReturn result, void *dev, IOHIDReportType type,
338 uint32_t report_id, uint8_t *report, CFIndex report_len)
339{
340 (void)context;
341 (void)dev;
342 (void)report;
343
344 if (result != kIOReturnSuccess || type != kIOHIDReportTypeInput ||
345 report_id != 0 || report_len != REPORT_LEN - 1) {
346 fido_log_debug("%s: io error", __func__);
347 }
348}
349
350static void
351removal_callback(void *context, IOReturn result, void *sender)
352{
353 (void)context;
354 (void)result;
355 (void)sender;
356
357 CFRunLoopStop(CFRunLoopGetCurrent());
358}
359
360int
361fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
362{
363 struct dev *dev = handle;
364 CFRunLoopRunResult r;
365
366 (void)ms; /* XXX */
367
368 if (len != REPORT_LEN - 1) {
369 fido_log_debug("%s: invalid len", __func__);
370 return (-1);
371 }
372
373 explicit_bzero(buf, len);
374
375 IOHIDDeviceRegisterInputReportCallback(dev->ref, buf, len,
376 &read_callback, NULL);
377 IOHIDDeviceRegisterRemovalCallback(dev->ref, &removal_callback, dev);
378 IOHIDDeviceScheduleWithRunLoop(dev->ref, CFRunLoopGetCurrent(),
379 dev->loop_id);
380
381 do
382 r = CFRunLoopRunInMode(dev->loop_id, 0.003, true);
383 while (r != kCFRunLoopRunHandledSource);
384
385 IOHIDDeviceRegisterInputReportCallback(dev->ref, buf, len, NULL, NULL);
386 IOHIDDeviceRegisterRemovalCallback(dev->ref, NULL, NULL);
387 IOHIDDeviceUnscheduleFromRunLoop(dev->ref, CFRunLoopGetCurrent(),
388 dev->loop_id);
389
390 return (REPORT_LEN - 1);
391}
392
393int
394fido_hid_write(void *handle, const unsigned char *buf, size_t len)
395{
396 struct dev *dev = handle;
397
398 if (len != REPORT_LEN) {
399 fido_log_debug("%s: invalid len", __func__);
400 return (-1);
401 }
402
403 if (IOHIDDeviceSetReport(dev->ref, kIOHIDReportTypeOutput, 0, buf + 1,
404 len - 1) != kIOReturnSuccess) {
405 fido_log_debug("%s: IOHIDDeviceSetReport", __func__);
406 return (-1);
407 }
408
409 return (REPORT_LEN);
410}
diff --git a/src/hid_win.c b/src/hid_win.c
new file mode 100644
index 0000000..6d93778
--- /dev/null
+++ b/src/hid_win.c
@@ -0,0 +1,324 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <sys/types.h>
8
9#include <fcntl.h>
10#include <string.h>
11#ifdef HAVE_UNISTD_H
12#include <unistd.h>
13#endif
14#include <windows.h>
15#include <setupapi.h>
16#include <initguid.h>
17#include <hidclass.h>
18#include <hidsdi.h>
19
20#include "fido.h"
21
22#define REPORT_LEN 65
23
24static bool
25is_fido(HANDLE dev)
26{
27 PHIDP_PREPARSED_DATA data = NULL;
28 HIDP_CAPS caps;
29 uint16_t usage_page = 0;
30
31 if (HidD_GetPreparsedData(dev, &data) == false) {
32 fido_log_debug("%s: HidD_GetPreparsedData", __func__);
33 goto fail;
34 }
35
36 if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) {
37 fido_log_debug("%s: HidP_GetCaps", __func__);
38 goto fail;
39 }
40
41 if (caps.OutputReportByteLength != REPORT_LEN ||
42 caps.InputReportByteLength != REPORT_LEN) {
43 fido_log_debug("%s: unsupported report len", __func__);
44 goto fail;
45 }
46
47 usage_page = caps.UsagePage;
48fail:
49 if (data != NULL)
50 HidD_FreePreparsedData(data);
51
52 return (usage_page == 0xf1d0);
53}
54
55static int
56get_int(HANDLE dev, int16_t *vendor_id, int16_t *product_id)
57{
58 HIDD_ATTRIBUTES attr;
59
60 attr.Size = sizeof(attr);
61
62 if (HidD_GetAttributes(dev, &attr) == false) {
63 fido_log_debug("%s: HidD_GetAttributes", __func__);
64 return (-1);
65 }
66
67 *vendor_id = attr.VendorID;
68 *product_id = attr.ProductID;
69
70 return (0);
71}
72
73static int
74get_str(HANDLE dev, char **manufacturer, char **product)
75{
76 wchar_t buf[512];
77 int utf8_len;
78 int ok = -1;
79
80 *manufacturer = NULL;
81 *product = NULL;
82
83 if (HidD_GetManufacturerString(dev, &buf, sizeof(buf)) == false) {
84 fido_log_debug("%s: HidD_GetManufacturerString", __func__);
85 goto fail;
86 }
87
88 if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
89 -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
90 fido_log_debug("%s: WideCharToMultiByte", __func__);
91 goto fail;
92 }
93
94 if ((*manufacturer = malloc(utf8_len)) == NULL) {
95 fido_log_debug("%s: malloc", __func__);
96 goto fail;
97 }
98
99 if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
100 *manufacturer, utf8_len, NULL, NULL) != utf8_len) {
101 fido_log_debug("%s: WideCharToMultiByte", __func__);
102 goto fail;
103 }
104
105 if (HidD_GetProductString(dev, &buf, sizeof(buf)) == false) {
106 fido_log_debug("%s: HidD_GetProductString", __func__);
107 goto fail;
108 }
109
110 if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
111 -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
112 fido_log_debug("%s: WideCharToMultiByte", __func__);
113 goto fail;
114 }
115
116 if ((*product = malloc(utf8_len)) == NULL) {
117 fido_log_debug("%s: malloc", __func__);
118 goto fail;
119 }
120
121 if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
122 *product, utf8_len, NULL, NULL) != utf8_len) {
123 fido_log_debug("%s: WideCharToMultiByte", __func__);
124 goto fail;
125 }
126
127 ok = 0;
128fail:
129 if (ok < 0) {
130 free(*manufacturer);
131 free(*product);
132 *manufacturer = NULL;
133 *product = NULL;
134 }
135
136 return (ok);
137}
138
139static int
140copy_info(fido_dev_info_t *di, const char *path)
141{
142 HANDLE dev = INVALID_HANDLE_VALUE;
143 int ok = -1;
144
145 memset(di, 0, sizeof(*di));
146
147 dev = CreateFileA(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
148 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
149 if (dev == INVALID_HANDLE_VALUE || is_fido(dev) == 0)
150 goto fail;
151
152 if (get_int(dev, &di->vendor_id, &di->product_id) < 0 ||
153 get_str(dev, &di->manufacturer, &di->product) < 0)
154 goto fail;
155
156 if ((di->path = strdup(path)) == NULL)
157 goto fail;
158
159 ok = 0;
160fail:
161 if (dev != INVALID_HANDLE_VALUE)
162 CloseHandle(dev);
163
164 if (ok < 0) {
165 free(di->path);
166 free(di->manufacturer);
167 free(di->product);
168 explicit_bzero(di, sizeof(*di));
169 }
170
171 return (ok);
172}
173
174int
175fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
176{
177 GUID hid_guid = GUID_DEVINTERFACE_HID;
178 HDEVINFO devinfo = INVALID_HANDLE_VALUE;
179 SP_DEVICE_INTERFACE_DATA ifdata;
180 SP_DEVICE_INTERFACE_DETAIL_DATA_A *ifdetail = NULL;
181 DWORD len = 0;
182 DWORD idx = 0;
183 int r = FIDO_ERR_INTERNAL;
184
185 *olen = 0;
186
187 if (ilen == 0)
188 return (FIDO_OK); /* nothing to do */
189
190 if (devlist == NULL)
191 return (FIDO_ERR_INVALID_ARGUMENT);
192
193 devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL,
194 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
195 if (devinfo == INVALID_HANDLE_VALUE) {
196 fido_log_debug("%s: SetupDiGetClassDevsA", __func__);
197 goto fail;
198 }
199
200 ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
201
202 while (SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid, idx++,
203 &ifdata) == true) {
204 /*
205 * "Get the required buffer size. Call
206 * SetupDiGetDeviceInterfaceDetail with a NULL
207 * DeviceInterfaceDetailData pointer, a
208 * DeviceInterfaceDetailDataSize of zero, and a valid
209 * RequiredSize variable. In response to such a call, this
210 * function returns the required buffer size at RequiredSize
211 * and fails with GetLastError returning
212 * ERROR_INSUFFICIENT_BUFFER."
213 */
214 if (SetupDiGetDeviceInterfaceDetailA(devinfo, &ifdata, NULL, 0,
215 &len, NULL) != false ||
216 GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
217 fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 1",
218 __func__);
219 goto fail;
220 }
221
222 if ((ifdetail = malloc(len)) == NULL) {
223 fido_log_debug("%s: malloc", __func__);
224 goto fail;
225 }
226
227 ifdetail->cbSize = sizeof(*ifdetail);
228
229 if (SetupDiGetDeviceInterfaceDetailA(devinfo, &ifdata, ifdetail,
230 len, NULL, NULL) == false) {
231 fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 2",
232 __func__);
233 goto fail;
234 }
235
236 if (copy_info(&devlist[*olen], ifdetail->DevicePath) == 0) {
237 if (++(*olen) == ilen)
238 break;
239 }
240
241 free(ifdetail);
242 ifdetail = NULL;
243 }
244
245 r = FIDO_OK;
246fail:
247 if (devinfo != INVALID_HANDLE_VALUE)
248 SetupDiDestroyDeviceInfoList(devinfo);
249
250 free(ifdetail);
251
252 return (r);
253}
254
255void *
256fido_hid_open(const char *path)
257{
258 HANDLE dev;
259
260 dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
261 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
262 FILE_ATTRIBUTE_NORMAL, NULL);
263
264 if (dev == INVALID_HANDLE_VALUE)
265 return (NULL);
266
267 return (dev);
268}
269
270void
271fido_hid_close(void *handle)
272{
273 CloseHandle(handle);
274}
275
276int
277fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
278{
279 DWORD n;
280 int r = -1;
281 uint8_t report[REPORT_LEN];
282
283 (void)ms; /* XXX */
284
285 memset(report, 0, sizeof(report));
286
287 if (len != sizeof(report) - 1) {
288 fido_log_debug("%s: invalid len", __func__);
289 return (-1);
290 }
291
292 if (ReadFile(handle, report, sizeof(report), &n, NULL) == false ||
293 n != sizeof(report)) {
294 fido_log_debug("%s: ReadFile", __func__);
295 goto fail;
296 }
297
298 r = sizeof(report) - 1;
299 memcpy(buf, report + 1, len);
300
301fail:
302 explicit_bzero(report, sizeof(report));
303
304 return (r);
305}
306
307int
308fido_hid_write(void *handle, const unsigned char *buf, size_t len)
309{
310 DWORD n;
311
312 if (len != REPORT_LEN) {
313 fido_log_debug("%s: invalid len", __func__);
314 return (-1);
315 }
316
317 if (WriteFile(handle, buf, (DWORD)len, &n, NULL) == false ||
318 n != REPORT_LEN) {
319 fido_log_debug("%s: WriteFile", __func__);
320 return (-1);
321 }
322
323 return (REPORT_LEN);
324}
diff --git a/src/info.c b/src/info.c
new file mode 100644
index 0000000..e896503
--- /dev/null
+++ b/src/info.c
@@ -0,0 +1,410 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8#include "fido.h"
9
10static int
11decode_version(const cbor_item_t *item, void *arg)
12{
13 fido_str_array_t *v = arg;
14 const size_t i = v->len;
15
16 /* keep ptr[x] and len consistent */
17 if (cbor_string_copy(item, &v->ptr[i]) < 0) {
18 fido_log_debug("%s: cbor_string_copy", __func__);
19 return (-1);
20 }
21
22 v->len++;
23
24 return (0);
25}
26
27static int
28decode_versions(const cbor_item_t *item, fido_str_array_t *v)
29{
30 v->ptr = NULL;
31 v->len = 0;
32
33 if (cbor_isa_array(item) == false ||
34 cbor_array_is_definite(item) == false) {
35 fido_log_debug("%s: cbor type", __func__);
36 return (-1);
37 }
38
39 v->ptr = calloc(cbor_array_size(item), sizeof(char *));
40 if (v->ptr == NULL)
41 return (-1);
42
43 if (cbor_array_iter(item, v, decode_version) < 0) {
44 fido_log_debug("%s: decode_version", __func__);
45 return (-1);
46 }
47
48 return (0);
49}
50
51static int
52decode_extension(const cbor_item_t *item, void *arg)
53{
54 fido_str_array_t *e = arg;
55 const size_t i = e->len;
56
57 /* keep ptr[x] and len consistent */
58 if (cbor_string_copy(item, &e->ptr[i]) < 0) {
59 fido_log_debug("%s: cbor_string_copy", __func__);
60 return (-1);
61 }
62
63 e->len++;
64
65 return (0);
66}
67
68static int
69decode_extensions(const cbor_item_t *item, fido_str_array_t *e)
70{
71 e->ptr = NULL;
72 e->len = 0;
73
74 if (cbor_isa_array(item) == false ||
75 cbor_array_is_definite(item) == false) {
76 fido_log_debug("%s: cbor type", __func__);
77 return (-1);
78 }
79
80 e->ptr = calloc(cbor_array_size(item), sizeof(char *));
81 if (e->ptr == NULL)
82 return (-1);
83
84 if (cbor_array_iter(item, e, decode_extension) < 0) {
85 fido_log_debug("%s: decode_extension", __func__);
86 return (-1);
87 }
88
89 return (0);
90}
91
92static int
93decode_aaguid(const cbor_item_t *item, unsigned char *aaguid, size_t aaguid_len)
94{
95 if (cbor_isa_bytestring(item) == false ||
96 cbor_bytestring_is_definite(item) == false ||
97 cbor_bytestring_length(item) != aaguid_len) {
98 fido_log_debug("%s: cbor type", __func__);
99 return (-1);
100 }
101
102 memcpy(aaguid, cbor_bytestring_handle(item), aaguid_len);
103
104 return (0);
105}
106
107static int
108decode_option(const cbor_item_t *key, const cbor_item_t *val, void *arg)
109{
110 fido_opt_array_t *o = arg;
111 const size_t i = o->len;
112
113 if (cbor_isa_float_ctrl(val) == false ||
114 cbor_float_get_width(val) != CBOR_FLOAT_0 ||
115 cbor_is_bool(val) == false) {
116 fido_log_debug("%s: cbor type", __func__);
117 return (0); /* ignore */
118 }
119
120 if (cbor_string_copy(key, &o->name[i]) < 0) {
121 fido_log_debug("%s: cbor_string_copy", __func__);
122 return (0); /* ignore */
123 }
124
125 /* keep name/value and len consistent */
126 o->value[i] = cbor_ctrl_value(val) == CBOR_CTRL_TRUE;
127 o->len++;
128
129 return (0);
130}
131
132static int
133decode_options(const cbor_item_t *item, fido_opt_array_t *o)
134{
135 o->name = NULL;
136 o->value = NULL;
137 o->len = 0;
138
139 if (cbor_isa_map(item) == false ||
140 cbor_map_is_definite(item) == false) {
141 fido_log_debug("%s: cbor type", __func__);
142 return (-1);
143 }
144
145 o->name = calloc(cbor_map_size(item), sizeof(char *));
146 o->value = calloc(cbor_map_size(item), sizeof(bool));
147 if (o->name == NULL || o->value == NULL)
148 return (-1);
149
150 return (cbor_map_iter(item, o, decode_option));
151}
152
153static int
154decode_protocol(const cbor_item_t *item, void *arg)
155{
156 fido_byte_array_t *p = arg;
157 const size_t i = p->len;
158
159 if (cbor_isa_uint(item) == false ||
160 cbor_int_get_width(item) != CBOR_INT_8) {
161 fido_log_debug("%s: cbor type", __func__);
162 return (-1);
163 }
164
165 /* keep ptr[x] and len consistent */
166 p->ptr[i] = cbor_get_uint8(item);
167 p->len++;
168
169 return (0);
170}
171
172static int
173decode_protocols(const cbor_item_t *item, fido_byte_array_t *p)
174{
175 p->ptr = NULL;
176 p->len = 0;
177
178 if (cbor_isa_array(item) == false ||
179 cbor_array_is_definite(item) == false) {
180 fido_log_debug("%s: cbor type", __func__);
181 return (-1);
182 }
183
184 p->ptr = calloc(cbor_array_size(item), sizeof(uint8_t));
185 if (p->ptr == NULL)
186 return (-1);
187
188 if (cbor_array_iter(item, p, decode_protocol) < 0) {
189 fido_log_debug("%s: decode_protocol", __func__);
190 return (-1);
191 }
192
193 return (0);
194}
195
196static int
197parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg)
198{
199 fido_cbor_info_t *ci = arg;
200
201 if (cbor_isa_uint(key) == false ||
202 cbor_int_get_width(key) != CBOR_INT_8) {
203 fido_log_debug("%s: cbor type", __func__);
204 return (0); /* ignore */
205 }
206
207 switch (cbor_get_uint8(key)) {
208 case 1: /* versions */
209 return (decode_versions(val, &ci->versions));
210 case 2: /* extensions */
211 return (decode_extensions(val, &ci->extensions));
212 case 3: /* aaguid */
213 return (decode_aaguid(val, ci->aaguid, sizeof(ci->aaguid)));
214 case 4: /* options */
215 return (decode_options(val, &ci->options));
216 case 5: /* maxMsgSize */
217 return (cbor_decode_uint64(val, &ci->maxmsgsiz));
218 case 6: /* pinProtocols */
219 return (decode_protocols(val, &ci->protocols));
220 default: /* ignore */
221 fido_log_debug("%s: cbor type", __func__);
222 return (0);
223 }
224}
225
226static int
227fido_dev_get_cbor_info_tx(fido_dev_t *dev)
228{
229 const unsigned char cbor[] = { CTAP_CBOR_GETINFO };
230 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
231
232 fido_log_debug("%s: dev=%p", __func__, (void *)dev);
233
234 if (fido_tx(dev, cmd, cbor, sizeof(cbor)) < 0) {
235 fido_log_debug("%s: fido_tx", __func__);
236 return (FIDO_ERR_TX);
237 }
238
239 return (FIDO_OK);
240}
241
242static int
243fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int ms)
244{
245 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
246 unsigned char reply[512];
247 int reply_len;
248
249 fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev,
250 (void *)ci, ms);
251
252 memset(ci, 0, sizeof(*ci));
253
254 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
255 fido_log_debug("%s: fido_rx", __func__);
256 return (FIDO_ERR_RX);
257 }
258
259 return (cbor_parse_reply(reply, (size_t)reply_len, ci,
260 parse_reply_element));
261}
262
263static int
264fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int ms)
265{
266 int r;
267
268 if ((r = fido_dev_get_cbor_info_tx(dev)) != FIDO_OK ||
269 (r = fido_dev_get_cbor_info_rx(dev, ci, ms)) != FIDO_OK)
270 return (r);
271
272 return (FIDO_OK);
273}
274
275int
276fido_dev_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci)
277{
278 return (fido_dev_get_cbor_info_wait(dev, ci, -1));
279}
280
281/*
282 * get/set functions for fido_cbor_info_t; always at the end of the file
283 */
284
285fido_cbor_info_t *
286fido_cbor_info_new(void)
287{
288 return (calloc(1, sizeof(fido_cbor_info_t)));
289}
290
291static void
292free_str_array(fido_str_array_t *sa)
293{
294 for (size_t i = 0; i < sa->len; i++)
295 free(sa->ptr[i]);
296
297 free(sa->ptr);
298 sa->ptr = NULL;
299 sa->len = 0;
300}
301
302static void
303free_opt_array(fido_opt_array_t *oa)
304{
305 for (size_t i = 0; i < oa->len; i++)
306 free(oa->name[i]);
307
308 free(oa->name);
309 free(oa->value);
310 oa->name = NULL;
311 oa->value = NULL;
312}
313
314static void
315free_byte_array(fido_byte_array_t *ba)
316{
317 free(ba->ptr);
318
319 ba->ptr = NULL;
320 ba->len = 0;
321}
322
323void
324fido_cbor_info_free(fido_cbor_info_t **ci_p)
325{
326 fido_cbor_info_t *ci;
327
328 if (ci_p == NULL || (ci = *ci_p) == NULL)
329 return;
330
331 free_str_array(&ci->versions);
332 free_str_array(&ci->extensions);
333 free_opt_array(&ci->options);
334 free_byte_array(&ci->protocols);
335 free(ci);
336
337 *ci_p = NULL;
338}
339
340char **
341fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci)
342{
343 return (ci->versions.ptr);
344}
345
346size_t
347fido_cbor_info_versions_len(const fido_cbor_info_t *ci)
348{
349 return (ci->versions.len);
350}
351
352char **
353fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci)
354{
355 return (ci->extensions.ptr);
356}
357
358size_t
359fido_cbor_info_extensions_len(const fido_cbor_info_t *ci)
360{
361 return (ci->extensions.len);
362}
363
364const unsigned char *
365fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci)
366{
367 return (ci->aaguid);
368}
369
370size_t
371fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci)
372{
373 return (sizeof(ci->aaguid));
374}
375
376char **
377fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci)
378{
379 return (ci->options.name);
380}
381
382const bool *
383fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci)
384{
385 return (ci->options.value);
386}
387
388size_t
389fido_cbor_info_options_len(const fido_cbor_info_t *ci)
390{
391 return (ci->options.len);
392}
393
394uint64_t
395fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci)
396{
397 return (ci->maxmsgsiz);
398}
399
400const uint8_t *
401fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci)
402{
403 return (ci->protocols.ptr);
404}
405
406size_t
407fido_cbor_info_protocols_len(const fido_cbor_info_t *ci)
408{
409 return (ci->protocols.len);
410}
diff --git a/src/io.c b/src/io.c
new file mode 100644
index 0000000..aa88720
--- /dev/null
+++ b/src/io.c
@@ -0,0 +1,256 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <stdint.h>
8#include <stdio.h>
9#include <string.h>
10
11#include "fido.h"
12#include "packed.h"
13
14PACKED_TYPE(frame_t,
15struct frame {
16 uint32_t cid; /* channel id */
17 union {
18 uint8_t type;
19 struct {
20 uint8_t cmd;
21 uint8_t bcnth;
22 uint8_t bcntl;
23 uint8_t data[CTAP_RPT_SIZE - 7];
24 } init;
25 struct {
26 uint8_t seq;
27 uint8_t data[CTAP_RPT_SIZE - 5];
28 } cont;
29 } body;
30})
31
32#ifndef MIN
33#define MIN(x, y) ((x) > (y) ? (y) : (x))
34#endif
35
36static size_t
37tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
38{
39 struct frame *fp;
40 unsigned char pkt[sizeof(*fp) + 1];
41 int n;
42
43 if (d->io.write == NULL || (cmd & 0x80) == 0)
44 return (0);
45
46 memset(&pkt, 0, sizeof(pkt));
47 fp = (struct frame *)(pkt + 1);
48 fp->cid = d->cid;
49 fp->body.init.cmd = 0x80 | cmd;
50 fp->body.init.bcnth = (count >> 8) & 0xff;
51 fp->body.init.bcntl = count & 0xff;
52 count = MIN(count, sizeof(fp->body.init.data));
53 if (count)
54 memcpy(&fp->body.init.data, buf, count);
55
56 n = d->io.write(d->io_handle, pkt, sizeof(pkt));
57 if (n < 0 || (size_t)n != sizeof(pkt))
58 return (0);
59
60 return (count);
61}
62
63static size_t
64tx_frame(fido_dev_t *d, int seq, const void *buf, size_t count)
65{
66 struct frame *fp;
67 unsigned char pkt[sizeof(*fp) + 1];
68 int n;
69
70 if (d->io.write == NULL || seq < 0 || seq > UINT8_MAX)
71 return (0);
72
73 memset(&pkt, 0, sizeof(pkt));
74 fp = (struct frame *)(pkt + 1);
75 fp->cid = d->cid;
76 fp->body.cont.seq = (uint8_t)seq;
77 count = MIN(count, sizeof(fp->body.cont.data));
78 memcpy(&fp->body.cont.data, buf, count);
79
80 n = d->io.write(d->io_handle, pkt, sizeof(pkt));
81 if (n < 0 || (size_t)n != sizeof(pkt))
82 return (0);
83
84 return (count);
85}
86
87int
88fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
89{
90 int seq = 0;
91 size_t sent;
92
93 fido_log_debug("%s: d=%p, cmd=0x%02x, buf=%p, count=%zu", __func__,
94 (void *)d, cmd, buf, count);
95 fido_log_xxd(buf, count);
96
97 if (d->io_handle == NULL || count > UINT16_MAX) {
98 fido_log_debug("%s: invalid argument (%p, %zu)", __func__,
99 d->io_handle, count);
100 return (-1);
101 }
102
103 if ((sent = tx_preamble(d, cmd, buf, count)) == 0) {
104 fido_log_debug("%s: tx_preamble", __func__);
105 return (-1);
106 }
107
108 while (sent < count) {
109 if (seq & 0x80) {
110 fido_log_debug("%s: seq & 0x80", __func__);
111 return (-1);
112 }
113 const uint8_t *p = (const uint8_t *)buf + sent;
114 size_t n = tx_frame(d, seq++, p, count - sent);
115 if (n == 0) {
116 fido_log_debug("%s: tx_frame", __func__);
117 return (-1);
118 }
119 sent += n;
120 }
121
122 return (0);
123}
124
125static int
126rx_frame(fido_dev_t *d, struct frame *fp, int ms)
127{
128 int n;
129
130 if (d->io.read == NULL)
131 return (-1);
132
133 n = d->io.read(d->io_handle, (unsigned char *)fp, sizeof(*fp), ms);
134 if (n < 0 || (size_t)n != sizeof(*fp))
135 return (-1);
136
137 return (0);
138}
139
140static int
141rx_preamble(fido_dev_t *d, struct frame *fp, int ms)
142{
143 do {
144 if (rx_frame(d, fp, ms) < 0)
145 return (-1);
146#ifdef FIDO_FUZZ
147 fp->cid = d->cid;
148#endif
149 } while (fp->cid == d->cid &&
150 fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE));
151
152 return (0);
153}
154
155int
156fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms)
157{
158 struct frame f;
159 uint16_t r;
160 uint16_t flen;
161 int seq;
162
163 if (d->io_handle == NULL || (cmd & 0x80) == 0) {
164 fido_log_debug("%s: invalid argument (%p, 0x%02x)", __func__,
165 d->io_handle, cmd);
166 return (-1);
167 }
168
169 if (rx_preamble(d, &f, ms) < 0) {
170 fido_log_debug("%s: rx_preamble", __func__);
171 return (-1);
172 }
173
174 fido_log_debug("%s: initiation frame at %p, len %zu", __func__,
175 (void *)&f, sizeof(f));
176 fido_log_xxd(&f, sizeof(f));
177
178#ifdef FIDO_FUZZ
179 f.cid = d->cid;
180 f.body.init.cmd = cmd;
181#endif
182
183 if (f.cid != d->cid || f.body.init.cmd != cmd) {
184 fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)",
185 __func__, f.cid, d->cid, f.body.init.cmd, cmd);
186 return (-1);
187 }
188
189 flen = (f.body.init.bcnth << 8) | f.body.init.bcntl;
190 if (count < (size_t)flen) {
191 fido_log_debug("%s: count < flen (%zu, %zu)", __func__, count,
192 (size_t)flen);
193 return (-1);
194 }
195 if (flen < sizeof(f.body.init.data)) {
196 memcpy(buf, f.body.init.data, flen);
197 return (flen);
198 }
199
200 memcpy(buf, f.body.init.data, sizeof(f.body.init.data));
201 r = sizeof(f.body.init.data);
202 seq = 0;
203
204 while ((size_t)r < flen) {
205 if (rx_frame(d, &f, ms) < 0) {
206 fido_log_debug("%s: rx_frame", __func__);
207 return (-1);
208 }
209
210 fido_log_debug("%s: continuation frame at %p, len %zu",
211 __func__, (void *)&f, sizeof(f));
212 fido_log_xxd(&f, sizeof(f));
213
214#ifdef FIDO_FUZZ
215 f.cid = d->cid;
216 f.body.cont.seq = seq;
217#endif
218
219 if (f.cid != d->cid || f.body.cont.seq != seq++) {
220 fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
221 __func__, f.cid, d->cid, f.body.cont.seq, seq);
222 return (-1);
223 }
224
225 uint8_t *p = (uint8_t *)buf + r;
226
227 if ((size_t)(flen - r) > sizeof(f.body.cont.data)) {
228 memcpy(p, f.body.cont.data, sizeof(f.body.cont.data));
229 r += sizeof(f.body.cont.data);
230 } else {
231 memcpy(p, f.body.cont.data, flen - r);
232 r += (flen - r); /* break */
233 }
234 }
235
236 fido_log_debug("%s: payload at %p, len %zu", __func__, buf, (size_t)r);
237 fido_log_xxd(buf, r);
238
239 return (r);
240}
241
242int
243fido_rx_cbor_status(fido_dev_t *d, int ms)
244{
245 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
246 unsigned char reply[2048];
247 int reply_len;
248
249 if ((reply_len = fido_rx(d, cmd, &reply, sizeof(reply), ms)) < 0 ||
250 (size_t)reply_len < 1) {
251 fido_log_debug("%s: fido_rx", __func__);
252 return (FIDO_ERR_RX);
253 }
254
255 return (reply[0]);
256}
diff --git a/src/iso7816.c b/src/iso7816.c
new file mode 100644
index 0000000..e2ea281
--- /dev/null
+++ b/src/iso7816.c
@@ -0,0 +1,70 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8#include "fido.h"
9
10iso7816_apdu_t *
11iso7816_new(uint8_t ins, uint8_t p1, uint16_t payload_len)
12{
13 iso7816_apdu_t *apdu;
14 size_t alloc_len;
15
16 alloc_len = sizeof(iso7816_apdu_t) + payload_len;
17
18 if ((apdu = calloc(1, alloc_len)) == NULL)
19 return (NULL);
20
21 apdu->alloc_len = alloc_len;
22 apdu->payload_len = payload_len;
23 apdu->payload_ptr = apdu->payload;
24 apdu->header.ins = ins;
25 apdu->header.p1 = p1;
26 apdu->header.lc2 = (payload_len >> 8) & 0xff;
27 apdu->header.lc3 = payload_len & 0xff;
28
29 return (apdu);
30}
31
32void
33iso7816_free(iso7816_apdu_t **apdu_p)
34{
35 iso7816_apdu_t *apdu;
36
37 if (apdu_p == NULL || (apdu = *apdu_p) == NULL)
38 return;
39
40 explicit_bzero(apdu, apdu->alloc_len);
41 free(apdu);
42
43 *apdu_p = NULL;
44}
45
46int
47iso7816_add(iso7816_apdu_t *apdu, const void *buf, size_t cnt)
48{
49 if (cnt > apdu->payload_len || cnt > UINT16_MAX)
50 return (-1);
51
52 memcpy(apdu->payload_ptr, buf, cnt);
53 apdu->payload_ptr += cnt;
54 apdu->payload_len -= (uint16_t)cnt;
55
56 return (0);
57}
58
59const unsigned char *
60iso7816_ptr(const iso7816_apdu_t *apdu)
61{
62 return ((const unsigned char *)&apdu->header);
63}
64
65size_t
66iso7816_len(const iso7816_apdu_t *apdu)
67{
68 return (apdu->alloc_len - sizeof(apdu->alloc_len) -
69 sizeof(apdu->payload_len) - sizeof(apdu->payload_ptr));
70}
diff --git a/src/iso7816.h b/src/iso7816.h
new file mode 100644
index 0000000..426cd97
--- /dev/null
+++ b/src/iso7816.h
@@ -0,0 +1,38 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _ISO7816_H
8#define _ISO7816_H
9
10#include "packed.h"
11
12PACKED_TYPE(iso7816_header_t,
13struct iso7816_header {
14 uint8_t cla;
15 uint8_t ins;
16 uint8_t p1;
17 uint8_t p2;
18 uint8_t lc1;
19 uint8_t lc2;
20 uint8_t lc3;
21})
22
23PACKED_TYPE(iso7816_apdu_t,
24struct iso7816_apdu {
25 size_t alloc_len;
26 uint16_t payload_len;
27 uint8_t *payload_ptr;
28 iso7816_header_t header;
29 uint8_t payload[];
30})
31
32const unsigned char *iso7816_ptr(const iso7816_apdu_t *);
33int iso7816_add(iso7816_apdu_t *, const void *, size_t);
34iso7816_apdu_t *iso7816_new(uint8_t, uint8_t, uint16_t);
35size_t iso7816_len(const iso7816_apdu_t *);
36void iso7816_free(iso7816_apdu_t **);
37
38#endif /* !_ISO7816_H */
diff --git a/src/libfido2.pc.in b/src/libfido2.pc.in
new file mode 100644
index 0000000..03d0606
--- /dev/null
+++ b/src/libfido2.pc.in
@@ -0,0 +1,12 @@
1prefix=@CMAKE_INSTALL_PREFIX@
2exec_prefix=${prefix}
3libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
4includedir=${prefix}/include
5
6Name: @PROJECT_NAME@
7Description: A FIDO2 library
8URL: https://github.com/yubico/libfido2
9Version: @FIDO_VERSION@
10Requires: libcrypto
11Libs: -L${libdir} -lfido2
12Cflags: -I${includedir}
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..982bdb7
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,63 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <stdarg.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include "fido.h"
11
12#ifndef FIDO_NO_DIAGNOSTIC
13
14#ifndef TLS
15#define TLS
16#endif
17
18static TLS int logging;
19
20void
21fido_log_init(void)
22{
23 logging = 1;
24}
25
26void
27fido_log_xxd(const void *buf, size_t count)
28{
29 const uint8_t *ptr = buf;
30 size_t i;
31
32 if (!logging)
33 return;
34
35 fprintf(stderr, " ");
36
37 for (i = 0; i < count; i++) {
38 fprintf(stderr, "%02x ", *ptr++);
39 if ((i + 1) % 16 == 0 && i + 1 < count)
40 fprintf(stderr, "\n ");
41 }
42
43 fprintf(stderr, "\n");
44 fflush(stderr);
45}
46
47void
48fido_log_debug(const char *fmt, ...)
49{
50 va_list ap;
51
52 if (!logging)
53 return;
54
55 va_start(ap, fmt);
56 vfprintf(stderr, fmt, ap);
57 va_end(ap);
58
59 fprintf(stderr, "\n");
60 fflush(stderr);
61}
62
63#endif /* !FIDO_NO_DIAGNOSTIC */
diff --git a/src/packed.h b/src/packed.h
new file mode 100644
index 0000000..3857c22
--- /dev/null
+++ b/src/packed.h
@@ -0,0 +1,22 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _PACKED_H
8#define _PACKED_H
9
10#if defined(__GNUC__)
11#define PACKED_TYPE(type, def) \
12 typedef def __attribute__ ((__packed__)) type;
13#elif defined(_MSC_VER)
14#define PACKED_TYPE(type, def) \
15 __pragma(pack(push, 1)) \
16 typedef def type; \
17 __pragma(pack(pop))
18#else
19#error "please provide a way to define packed types on your platform"
20#endif
21
22#endif /* !_PACKED_H */
diff --git a/src/pin.c b/src/pin.c
new file mode 100644
index 0000000..1ed555c
--- /dev/null
+++ b/src/pin.c
@@ -0,0 +1,428 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8#include "fido.h"
9#include "fido/es256.h"
10
11static int
12parse_pintoken(const cbor_item_t *key, const cbor_item_t *val, void *arg)
13{
14 fido_blob_t *token = arg;
15
16 if (cbor_isa_uint(key) == false ||
17 cbor_int_get_width(key) != CBOR_INT_8 ||
18 cbor_get_uint8(key) != 2) {
19 fido_log_debug("%s: cbor type", __func__);
20 return (0); /* ignore */
21 }
22
23 return (fido_blob_decode(val, token));
24}
25
26static int
27fido_dev_get_pin_token_tx(fido_dev_t *dev, const char *pin,
28 const fido_blob_t *ecdh, const es256_pk_t *pk)
29{
30 fido_blob_t f;
31 fido_blob_t *p = NULL;
32 cbor_item_t *argv[6];
33 int r;
34
35 memset(&f, 0, sizeof(f));
36 memset(argv, 0, sizeof(argv));
37
38 if ((p = fido_blob_new()) == NULL || fido_blob_set(p,
39 (const unsigned char *)pin, strlen(pin)) < 0) {
40 fido_log_debug("%s: fido_blob_set", __func__);
41 r = FIDO_ERR_INVALID_ARGUMENT;
42 goto fail;
43 }
44
45 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
46 (argv[1] = cbor_build_uint8(5)) == NULL ||
47 (argv[2] = es256_pk_encode(pk, 0)) == NULL ||
48 (argv[5] = cbor_encode_pin_hash_enc(ecdh, p)) == NULL) {
49 fido_log_debug("%s: cbor encode", __func__);
50 r = FIDO_ERR_INTERNAL;
51 goto fail;
52 }
53
54 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 6, &f) < 0 ||
55 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
56 fido_log_debug("%s: fido_tx", __func__);
57 r = FIDO_ERR_TX;
58 goto fail;
59 }
60
61 r = FIDO_OK;
62fail:
63 cbor_vector_free(argv, nitems(argv));
64 fido_blob_free(&p);
65 free(f.ptr);
66
67 return (r);
68}
69
70static int
71fido_dev_get_pin_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh,
72 fido_blob_t *token, int ms)
73{
74 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
75 fido_blob_t *aes_token = NULL;
76 unsigned char reply[2048];
77 int reply_len;
78 int r;
79
80 if ((aes_token = fido_blob_new()) == NULL) {
81 r = FIDO_ERR_INTERNAL;
82 goto fail;
83 }
84
85 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
86 fido_log_debug("%s: fido_rx", __func__);
87 r = FIDO_ERR_RX;
88 goto fail;
89 }
90
91 if ((r = cbor_parse_reply(reply, (size_t)reply_len, aes_token,
92 parse_pintoken)) != FIDO_OK) {
93 fido_log_debug("%s: parse_pintoken", __func__);
94 goto fail;
95 }
96
97 if (aes256_cbc_dec(ecdh, aes_token, token) < 0) {
98 fido_log_debug("%s: aes256_cbc_dec", __func__);
99 r = FIDO_ERR_RX;
100 goto fail;
101 }
102
103 r = FIDO_OK;
104fail:
105 fido_blob_free(&aes_token);
106
107 return (r);
108}
109
110static int
111fido_dev_get_pin_token_wait(fido_dev_t *dev, const char *pin,
112 const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token, int ms)
113{
114 int r;
115
116 if ((r = fido_dev_get_pin_token_tx(dev, pin, ecdh, pk)) != FIDO_OK ||
117 (r = fido_dev_get_pin_token_rx(dev, ecdh, token, ms)) != FIDO_OK)
118 return (r);
119
120 return (FIDO_OK);
121}
122
123int
124fido_dev_get_pin_token(fido_dev_t *dev, const char *pin,
125 const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token)
126{
127 return (fido_dev_get_pin_token_wait(dev, pin, ecdh, pk, token, -1));
128}
129
130static int
131pad64(const char *pin, fido_blob_t **ppin)
132{
133 size_t pin_len;
134 size_t ppin_len;
135
136 pin_len = strlen(pin);
137 if (pin_len < 4 || pin_len > 255) {
138 fido_log_debug("%s: invalid pin length", __func__);
139 return (FIDO_ERR_PIN_POLICY_VIOLATION);
140 }
141
142 if ((*ppin = fido_blob_new()) == NULL)
143 return (FIDO_ERR_INTERNAL);
144
145 ppin_len = (pin_len + 63) & ~63;
146 if (ppin_len < pin_len || ((*ppin)->ptr = calloc(1, ppin_len)) == NULL) {
147 fido_blob_free(ppin);
148 return (FIDO_ERR_INTERNAL);
149 }
150
151 memcpy((*ppin)->ptr, pin, pin_len);
152 (*ppin)->len = ppin_len;
153
154 return (FIDO_OK);
155}
156
157static int
158fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin)
159{
160 fido_blob_t f;
161 fido_blob_t *ppin = NULL;
162 fido_blob_t *ecdh = NULL;
163 fido_blob_t *opin = NULL;
164 cbor_item_t *argv[6];
165 es256_pk_t *pk = NULL;
166 int r;
167
168 memset(&f, 0, sizeof(f));
169 memset(argv, 0, sizeof(argv));
170
171 if ((opin = fido_blob_new()) == NULL || fido_blob_set(opin,
172 (const unsigned char *)oldpin, strlen(oldpin)) < 0) {
173 fido_log_debug("%s: fido_blob_set", __func__);
174 r = FIDO_ERR_INVALID_ARGUMENT;
175 goto fail;
176 }
177
178 if ((r = pad64(pin, &ppin)) != FIDO_OK) {
179 fido_log_debug("%s: pad64", __func__);
180 goto fail;
181 }
182
183 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
184 fido_log_debug("%s: fido_do_ecdh", __func__);
185 goto fail;
186 }
187
188 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
189 (argv[1] = cbor_build_uint8(4)) == NULL ||
190 (argv[2] = es256_pk_encode(pk, 0)) == NULL ||
191 (argv[3] = cbor_encode_change_pin_auth(ecdh, ppin, opin)) == NULL ||
192 (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL ||
193 (argv[5] = cbor_encode_pin_hash_enc(ecdh, opin)) == NULL) {
194 fido_log_debug("%s: cbor encode", __func__);
195 r = FIDO_ERR_INTERNAL;
196 goto fail;
197 }
198
199 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 6, &f) < 0 ||
200 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
201 fido_log_debug("%s: fido_tx", __func__);
202 r = FIDO_ERR_TX;
203 goto fail;
204 }
205
206 r = FIDO_OK;
207fail:
208 cbor_vector_free(argv, nitems(argv));
209 es256_pk_free(&pk);
210 fido_blob_free(&ppin);
211 fido_blob_free(&ecdh);
212 fido_blob_free(&opin);
213 free(f.ptr);
214
215 return (r);
216
217}
218
219static int
220fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin)
221{
222 fido_blob_t f;
223 fido_blob_t *ppin = NULL;
224 fido_blob_t *ecdh = NULL;
225 cbor_item_t *argv[5];
226 es256_pk_t *pk = NULL;
227 int r;
228
229 memset(&f, 0, sizeof(f));
230 memset(argv, 0, sizeof(argv));
231
232 if ((r = pad64(pin, &ppin)) != FIDO_OK) {
233 fido_log_debug("%s: pad64", __func__);
234 goto fail;
235 }
236
237 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
238 fido_log_debug("%s: fido_do_ecdh", __func__);
239 goto fail;
240 }
241
242 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
243 (argv[1] = cbor_build_uint8(3)) == NULL ||
244 (argv[2] = es256_pk_encode(pk, 0)) == NULL ||
245 (argv[3] = cbor_encode_set_pin_auth(ecdh, ppin)) == NULL ||
246 (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL) {
247 fido_log_debug("%s: cbor encode", __func__);
248 r = FIDO_ERR_INTERNAL;
249 goto fail;
250 }
251
252 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 5, &f) < 0 ||
253 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
254 fido_log_debug("%s: fido_tx", __func__);
255 r = FIDO_ERR_TX;
256 goto fail;
257 }
258
259 r = FIDO_OK;
260fail:
261 cbor_vector_free(argv, nitems(argv));
262 es256_pk_free(&pk);
263 fido_blob_free(&ppin);
264 fido_blob_free(&ecdh);
265 free(f.ptr);
266
267 return (r);
268}
269
270static int
271fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin,
272 int ms)
273{
274 int r;
275
276 if (oldpin != NULL) {
277 if ((r = fido_dev_change_pin_tx(dev, pin, oldpin)) != FIDO_OK) {
278 fido_log_debug("%s: fido_dev_change_pin_tx", __func__);
279 return (r);
280 }
281 } else {
282 if ((r = fido_dev_set_pin_tx(dev, pin)) != FIDO_OK) {
283 fido_log_debug("%s: fido_dev_set_pin_tx", __func__);
284 return (r);
285 }
286 }
287
288 if ((r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
289 fido_log_debug("%s: fido_rx_cbor_status", __func__);
290 return (r);
291 }
292
293 return (FIDO_OK);
294}
295
296int
297fido_dev_set_pin(fido_dev_t *dev, const char *pin, const char *oldpin)
298{
299 return (fido_dev_set_pin_wait(dev, pin, oldpin, -1));
300}
301
302static int
303parse_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
304{
305 int *retries = arg;
306 uint64_t n;
307
308 if (cbor_isa_uint(key) == false ||
309 cbor_int_get_width(key) != CBOR_INT_8 ||
310 cbor_get_uint8(key) != 3) {
311 fido_log_debug("%s: cbor type", __func__);
312 return (0); /* ignore */
313 }
314
315 if (cbor_decode_uint64(val, &n) < 0 || n > INT_MAX) {
316 fido_log_debug("%s: cbor_decode_uint64", __func__);
317 return (-1);
318 }
319
320 *retries = (int)n;
321
322 return (0);
323}
324
325static int
326fido_dev_get_retry_count_tx(fido_dev_t *dev)
327{
328 fido_blob_t f;
329 cbor_item_t *argv[2];
330 int r;
331
332 memset(&f, 0, sizeof(f));
333 memset(argv, 0, sizeof(argv));
334
335 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
336 (argv[1] = cbor_build_uint8(1)) == NULL) {
337 r = FIDO_ERR_INTERNAL;
338 goto fail;
339 }
340
341 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 2, &f) < 0 ||
342 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
343 fido_log_debug("%s: fido_tx", __func__);
344 r = FIDO_ERR_TX;
345 goto fail;
346 }
347
348 r = FIDO_OK;
349fail:
350 cbor_vector_free(argv, nitems(argv));
351 free(f.ptr);
352
353 return (r);
354}
355
356static int
357fido_dev_get_retry_count_rx(fido_dev_t *dev, int *retries, int ms)
358{
359 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
360 unsigned char reply[512];
361 int reply_len;
362 int r;
363
364 *retries = 0;
365
366 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
367 fido_log_debug("%s: fido_rx", __func__);
368 return (FIDO_ERR_RX);
369 }
370
371 if ((r = cbor_parse_reply(reply, (size_t)reply_len, retries,
372 parse_retry_count)) != FIDO_OK) {
373 fido_log_debug("%s: parse_retry_count", __func__);
374 return (r);
375 }
376
377 return (FIDO_OK);
378}
379
380static int
381fido_dev_get_retry_count_wait(fido_dev_t *dev, int *retries, int ms)
382{
383 int r;
384
385 if ((r = fido_dev_get_retry_count_tx(dev)) != FIDO_OK ||
386 (r = fido_dev_get_retry_count_rx(dev, retries, ms)) != FIDO_OK)
387 return (r);
388
389 return (FIDO_OK);
390}
391
392int
393fido_dev_get_retry_count(fido_dev_t *dev, int *retries)
394{
395 return (fido_dev_get_retry_count_wait(dev, retries, -1));
396}
397
398int
399cbor_add_pin_params(fido_dev_t *dev, const fido_blob_t *hmac_data,
400 const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin,
401 cbor_item_t **auth, cbor_item_t **opt)
402{
403 fido_blob_t *token = NULL;
404 int r;
405
406 if ((token = fido_blob_new()) == NULL) {
407 r = FIDO_ERR_INTERNAL;
408 goto fail;
409 }
410
411 if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) {
412 fido_log_debug("%s: fido_dev_get_pin_token", __func__);
413 goto fail;
414 }
415
416 if ((*auth = cbor_encode_pin_auth(token, hmac_data)) == NULL ||
417 (*opt = cbor_encode_pin_opt()) == NULL) {
418 fido_log_debug("%s: cbor encode", __func__);
419 r = FIDO_ERR_INTERNAL;
420 goto fail;
421 }
422
423 r = FIDO_OK;
424fail:
425 fido_blob_free(&token);
426
427 return (r);
428}
diff --git a/src/reset.c b/src/reset.c
new file mode 100644
index 0000000..4b2c88a
--- /dev/null
+++ b/src/reset.c
@@ -0,0 +1,40 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <stdlib.h>
8#include "fido.h"
9
10static int
11fido_dev_reset_tx(fido_dev_t *dev)
12{
13 const unsigned char cbor[] = { CTAP_CBOR_RESET };
14 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
15
16 if (fido_tx(dev, cmd, cbor, sizeof(cbor)) < 0) {
17 fido_log_debug("%s: fido_tx", __func__);
18 return (FIDO_ERR_TX);
19 }
20
21 return (FIDO_OK);
22}
23
24static int
25fido_dev_reset_wait(fido_dev_t *dev, int ms)
26{
27 int r;
28
29 if ((r = fido_dev_reset_tx(dev)) != FIDO_OK ||
30 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
31 return (r);
32
33 return (FIDO_OK);
34}
35
36int
37fido_dev_reset(fido_dev_t *dev)
38{
39 return (fido_dev_reset_wait(dev, -1));
40}
diff --git a/src/rs256.c b/src/rs256.c
new file mode 100644
index 0000000..9f30163
--- /dev/null
+++ b/src/rs256.c
@@ -0,0 +1,204 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/bn.h>
8#include <openssl/rsa.h>
9#include <openssl/evp.h>
10#include <openssl/obj_mac.h>
11
12#include <string.h>
13#include "fido.h"
14#include "fido/rs256.h"
15
16#if OPENSSL_VERSION_NUMBER < 0x10100000L
17static int
18RSA_bits(const RSA *r)
19{
20 return (BN_num_bits(r->n));
21}
22
23static int
24RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
25{
26 r->n = n;
27 r->e = e;
28 r->d = d;
29
30 return (1);
31}
32
33static void
34RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
35{
36 *n = r->n;
37 *e = r->e;
38 *d = r->d;
39}
40#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
41
42static int
43decode_bignum(const cbor_item_t *item, void *ptr, size_t len)
44{
45 if (cbor_isa_bytestring(item) == false ||
46 cbor_bytestring_is_definite(item) == false ||
47 cbor_bytestring_length(item) != len) {
48 fido_log_debug("%s: cbor type", __func__);
49 return (-1);
50 }
51
52 memcpy(ptr, cbor_bytestring_handle(item), len);
53
54 return (0);
55}
56
57static int
58decode_rsa_pubkey(const cbor_item_t *key, const cbor_item_t *val, void *arg)
59{
60 rs256_pk_t *k = arg;
61
62 if (cbor_isa_negint(key) == false ||
63 cbor_int_get_width(key) != CBOR_INT_8)
64 return (0); /* ignore */
65
66 switch (cbor_get_uint8(key)) {
67 case 0: /* modulus */
68 return (decode_bignum(val, &k->n, sizeof(k->n)));
69 case 1: /* public exponent */
70 return (decode_bignum(val, &k->e, sizeof(k->e)));
71 }
72
73 return (0); /* ignore */
74}
75
76int
77rs256_pk_decode(const cbor_item_t *item, rs256_pk_t *k)
78{
79 if (cbor_isa_map(item) == false ||
80 cbor_map_is_definite(item) == false ||
81 cbor_map_iter(item, k, decode_rsa_pubkey) < 0) {
82 fido_log_debug("%s: cbor type", __func__);
83 return (-1);
84 }
85
86 return (0);
87}
88
89rs256_pk_t *
90rs256_pk_new(void)
91{
92 return (calloc(1, sizeof(rs256_pk_t)));
93}
94
95void
96rs256_pk_free(rs256_pk_t **pkp)
97{
98 rs256_pk_t *pk;
99
100 if (pkp == NULL || (pk = *pkp) == NULL)
101 return;
102
103 explicit_bzero(pk, sizeof(*pk));
104 free(pk);
105
106 *pkp = NULL;
107}
108
109int
110rs256_pk_from_ptr(rs256_pk_t *pk, const void *ptr, size_t len)
111{
112 if (len < sizeof(*pk))
113 return (FIDO_ERR_INVALID_ARGUMENT);
114
115 memcpy(pk, ptr, sizeof(*pk));
116
117 return (FIDO_OK);
118}
119
120EVP_PKEY *
121rs256_pk_to_EVP_PKEY(const rs256_pk_t *k)
122{
123 RSA *rsa = NULL;
124 EVP_PKEY *pkey = NULL;
125 BIGNUM *n = NULL;
126 BIGNUM *e = NULL;
127 int ok = -1;
128
129 if ((n = BN_new()) == NULL || (e = BN_new()) == NULL)
130 goto fail;
131
132 if (BN_bin2bn(k->n, sizeof(k->n), n) == NULL ||
133 BN_bin2bn(k->e, sizeof(k->e), e) == NULL) {
134 fido_log_debug("%s: BN_bin2bn", __func__);
135 goto fail;
136 }
137
138 if ((rsa = RSA_new()) == NULL || RSA_set0_key(rsa, n, e, NULL) == 0) {
139 fido_log_debug("%s: RSA_set0_key", __func__);
140 goto fail;
141 }
142
143 /* at this point, n and e belong to rsa */
144 n = NULL;
145 e = NULL;
146
147 if ((pkey = EVP_PKEY_new()) == NULL ||
148 EVP_PKEY_assign_RSA(pkey, rsa) == 0) {
149 fido_log_debug("%s: EVP_PKEY_assign_RSA", __func__);
150 goto fail;
151 }
152
153 rsa = NULL; /* at this point, rsa belongs to evp */
154
155 ok = 0;
156fail:
157 if (n != NULL)
158 BN_free(n);
159 if (e != NULL)
160 BN_free(e);
161 if (rsa != NULL)
162 RSA_free(rsa);
163 if (ok < 0 && pkey != NULL) {
164 EVP_PKEY_free(pkey);
165 pkey = NULL;
166 }
167
168 return (pkey);
169}
170
171int
172rs256_pk_from_RSA(rs256_pk_t *pk, const RSA *rsa)
173{
174 const BIGNUM *n = NULL;
175 const BIGNUM *e = NULL;
176 const BIGNUM *d = NULL;
177 int k;
178
179 if (RSA_bits(rsa) != 2048) {
180 fido_log_debug("%s: invalid key length", __func__);
181 return (FIDO_ERR_INVALID_ARGUMENT);
182 }
183
184 RSA_get0_key(rsa, &n, &e, &d);
185
186 if (n == NULL || e == NULL) {
187 fido_log_debug("%s: RSA_get0_key", __func__);
188 return (FIDO_ERR_INTERNAL);
189 }
190
191 if ((k = BN_num_bytes(n)) < 0 || (size_t)k > sizeof(pk->n) ||
192 (k = BN_num_bytes(e)) < 0 || (size_t)k > sizeof(pk->e)) {
193 fido_log_debug("%s: invalid key", __func__);
194 return (FIDO_ERR_INTERNAL);
195 }
196
197 if ((k = BN_bn2bin(n, pk->n)) < 0 || (size_t)k > sizeof(pk->n) ||
198 (k = BN_bn2bin(e, pk->e)) < 0 || (size_t)k > sizeof(pk->e)) {
199 fido_log_debug("%s: BN_bn2bin", __func__);
200 return (FIDO_ERR_INTERNAL);
201 }
202
203 return (FIDO_OK);
204}
diff --git a/src/types.h b/src/types.h
new file mode 100644
index 0000000..42ed1b7
--- /dev/null
+++ b/src/types.h
@@ -0,0 +1,171 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _TYPES_H
8#define _TYPES_H
9
10#include "packed.h"
11
12/* COSE ES256 (ECDSA over P-256 with SHA-256) public key */
13typedef struct es256_pk {
14 unsigned char x[32];
15 unsigned char y[32];
16} es256_pk_t;
17
18/* COSE ES256 (ECDSA over P-256 with SHA-256) (secret) key */
19typedef struct es256_sk {
20 unsigned char d[32];
21} es256_sk_t;
22
23/* COSE RS256 (2048-bit RSA with PKCS1 padding and SHA-256) public key */
24typedef struct rs256_pk {
25 unsigned char n[256];
26 unsigned char e[3];
27} rs256_pk_t;
28
29/* COSE EDDSA (ED25519) */
30typedef struct eddsa_pk {
31 unsigned char x[32];
32} eddsa_pk_t;
33
34PACKED_TYPE(fido_authdata_t,
35struct fido_authdata {
36 unsigned char rp_id_hash[32]; /* sha256 of fido_rp.id */
37 uint8_t flags; /* user present/verified */
38 uint32_t sigcount; /* signature counter */
39 /* actually longer */
40})
41
42PACKED_TYPE(fido_attcred_raw_t,
43struct fido_attcred_raw {
44 unsigned char aaguid[16]; /* credential's aaguid */
45 uint16_t id_len; /* credential id length */
46 uint8_t body[]; /* credential id + pubkey */
47})
48
49typedef struct fido_attcred {
50 unsigned char aaguid[16]; /* credential's aaguid */
51 fido_blob_t id; /* credential id */
52 int type; /* credential's cose algorithm */
53 union { /* credential's public key */
54 es256_pk_t es256;
55 rs256_pk_t rs256;
56 eddsa_pk_t eddsa;
57 } pubkey;
58} fido_attcred_t;
59
60typedef struct fido_attstmt {
61 fido_blob_t x5c; /* attestation certificate */
62 fido_blob_t sig; /* attestation signature */
63} fido_attstmt_t;
64
65typedef struct fido_rp {
66 char *id; /* relying party id */
67 char *name; /* relying party name */
68} fido_rp_t;
69
70typedef struct fido_user {
71 fido_blob_t id; /* required */
72 char *icon; /* optional */
73 char *name; /* optional */
74 char *display_name; /* required */
75} fido_user_t;
76
77typedef struct fido_cred {
78 fido_blob_t cdh; /* client data hash */
79 fido_rp_t rp; /* relying party */
80 fido_user_t user; /* user entity */
81 fido_blob_array_t excl; /* list of credential ids to exclude */
82 fido_opt_t rk; /* resident key */
83 fido_opt_t uv; /* user verification */
84 int ext; /* enabled extensions */
85 int type; /* cose algorithm */
86 char *fmt; /* credential format */
87 int authdata_ext; /* decoded extensions */
88 fido_blob_t authdata_cbor; /* raw cbor payload */
89 fido_authdata_t authdata; /* decoded authdata payload */
90 fido_attcred_t attcred; /* returned credential (key + id) */
91 fido_attstmt_t attstmt; /* attestation statement (x509 + sig) */
92} fido_cred_t;
93
94typedef struct _fido_assert_stmt {
95 fido_blob_t id; /* credential id */
96 fido_user_t user; /* user attributes */
97 fido_blob_t hmac_secret_enc; /* hmac secret, encrypted */
98 fido_blob_t hmac_secret; /* hmac secret */
99 int authdata_ext; /* decoded extensions */
100 fido_blob_t authdata_cbor; /* raw cbor payload */
101 fido_authdata_t authdata; /* decoded authdata payload */
102 fido_blob_t sig; /* signature of cdh + authdata */
103} fido_assert_stmt;
104
105typedef struct fido_assert {
106 char *rp_id; /* relying party id */
107 fido_blob_t cdh; /* client data hash */
108 fido_blob_t hmac_salt; /* optional hmac-secret salt */
109 fido_blob_array_t allow_list; /* list of allowed credentials */
110 fido_opt_t up; /* user presence */
111 fido_opt_t uv; /* user verification */
112 int ext; /* enabled extensions */
113 fido_assert_stmt *stmt; /* array of expected assertions */
114 size_t stmt_cnt; /* number of allocated assertions */
115 size_t stmt_len; /* number of received assertions */
116} fido_assert_t;
117
118typedef struct fido_opt_array {
119 char **name;
120 bool *value;
121 size_t len;
122} fido_opt_array_t;
123
124typedef struct fido_str_array {
125 char **ptr;
126 size_t len;
127} fido_str_array_t;
128
129typedef struct fido_byte_array {
130 uint8_t *ptr;
131 size_t len;
132} fido_byte_array_t;
133
134typedef struct fido_cbor_info {
135 fido_str_array_t versions; /* supported versions: fido2|u2f */
136 fido_str_array_t extensions; /* list of supported extensions */
137 unsigned char aaguid[16]; /* aaguid */
138 fido_opt_array_t options; /* list of supported options */
139 uint64_t maxmsgsiz; /* maximum message size */
140 fido_byte_array_t protocols; /* supported pin protocols */
141} fido_cbor_info_t;
142
143typedef struct fido_dev_info {
144 char *path; /* device path */
145 int16_t vendor_id; /* 2-byte vendor id */
146 int16_t product_id; /* 2-byte product id */
147 char *manufacturer; /* manufacturer string */
148 char *product; /* product string */
149} fido_dev_info_t;
150
151PACKED_TYPE(fido_ctap_info_t,
152/* defined in section 8.1.9.1.3 (CTAPHID_INIT) of the fido2 ctap spec */
153struct fido_ctap_info {
154 uint64_t nonce; /* echoed nonce */
155 uint32_t cid; /* channel id */
156 uint8_t protocol; /* ctaphid protocol id */
157 uint8_t major; /* major version number */
158 uint8_t minor; /* minor version number */
159 uint8_t build; /* build version number */
160 uint8_t flags; /* capabilities flags; see FIDO_CAP_* */
161})
162
163typedef struct fido_dev {
164 uint64_t nonce; /* issued nonce */
165 fido_ctap_info_t attr; /* device attributes */
166 uint32_t cid; /* assigned channel id */
167 void *io_handle; /* abstract i/o handle */
168 fido_dev_io_t io; /* i/o functions & data */
169} fido_dev_t;
170
171#endif /* !_TYPES_H */
diff --git a/src/u2f.c b/src/u2f.c
new file mode 100644
index 0000000..3f2d9aa
--- /dev/null
+++ b/src/u2f.c
@@ -0,0 +1,758 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/sha.h>
8#include <openssl/x509.h>
9
10#include <string.h>
11#ifdef HAVE_UNISTD_H
12#include <unistd.h>
13#endif
14
15#include "fido.h"
16#include "fido/es256.h"
17
18#if defined(_MSC_VER)
19static int
20usleep(unsigned int usec)
21{
22 Sleep(usec / 1000);
23
24 return (0);
25}
26#endif
27
28static int
29sig_get(fido_blob_t *sig, const unsigned char **buf, size_t *len)
30{
31 sig->len = *len; /* consume the whole buffer */
32 if ((sig->ptr = calloc(1, sig->len)) == NULL ||
33 fido_buf_read(buf, len, sig->ptr, sig->len) < 0) {
34 fido_log_debug("%s: fido_buf_read", __func__);
35 if (sig->ptr != NULL) {
36 explicit_bzero(sig->ptr, sig->len);
37 free(sig->ptr);
38 sig->ptr = NULL;
39 sig->len = 0;
40 return (-1);
41 }
42 }
43
44 return (0);
45}
46
47static int
48x5c_get(fido_blob_t *x5c, const unsigned char **buf, size_t *len)
49{
50 X509 *cert = NULL;
51 int ok = -1;
52
53 if (*len > LONG_MAX) {
54 fido_log_debug("%s: invalid len %zu", __func__, *len);
55 goto fail;
56 }
57
58 /* find out the certificate's length */
59 const unsigned char *end = *buf;
60 if ((cert = d2i_X509(NULL, &end, (long)*len)) == NULL || end <= *buf ||
61 (x5c->len = (size_t)(end - *buf)) >= *len) {
62 fido_log_debug("%s: d2i_X509", __func__);
63 goto fail;
64 }
65
66 /* read accordingly */
67 if ((x5c->ptr = calloc(1, x5c->len)) == NULL ||
68 fido_buf_read(buf, len, x5c->ptr, x5c->len) < 0) {
69 fido_log_debug("%s: fido_buf_read", __func__);
70 goto fail;
71 }
72
73 ok = 0;
74fail:
75 if (cert != NULL)
76 X509_free(cert);
77
78 if (ok < 0) {
79 free(x5c->ptr);
80 x5c->ptr = NULL;
81 x5c->len = 0;
82 }
83
84 return (ok);
85}
86
87static int
88authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount,
89 fido_blob_t *fake_cbor_ad)
90{
91 fido_authdata_t ad;
92 cbor_item_t *item = NULL;
93 size_t alloc_len;
94
95 memset(&ad, 0, sizeof(ad));
96
97 if (SHA256((const void *)rp_id, strlen(rp_id),
98 ad.rp_id_hash) != ad.rp_id_hash) {
99 fido_log_debug("%s: sha256", __func__);
100 return (-1);
101 }
102
103 ad.flags = flags; /* XXX translate? */
104 ad.sigcount = sigcount;
105
106 if ((item = cbor_build_bytestring((const unsigned char *)&ad,
107 sizeof(ad))) == NULL) {
108 fido_log_debug("%s: cbor_build_bytestring", __func__);
109 return (-1);
110 }
111
112 if (fake_cbor_ad->ptr != NULL ||
113 (fake_cbor_ad->len = cbor_serialize_alloc(item, &fake_cbor_ad->ptr,
114 &alloc_len)) == 0) {
115 fido_log_debug("%s: cbor_serialize_alloc", __func__);
116 cbor_decref(&item);
117 return (-1);
118 }
119
120 cbor_decref(&item);
121
122 return (0);
123}
124
125static int
126send_dummy_register(fido_dev_t *dev, int ms)
127{
128 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG;
129 iso7816_apdu_t *apdu = NULL;
130 unsigned char challenge[SHA256_DIGEST_LENGTH];
131 unsigned char application[SHA256_DIGEST_LENGTH];
132 unsigned char reply[2048];
133 int r;
134
135#ifdef FIDO_FUZZ
136 ms = 0; /* XXX */
137#endif
138
139 /* dummy challenge & application */
140 memset(&challenge, 0xff, sizeof(challenge));
141 memset(&application, 0xff, sizeof(application));
142
143 if ((apdu = iso7816_new(U2F_CMD_REGISTER, 0, 2 *
144 SHA256_DIGEST_LENGTH)) == NULL ||
145 iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
146 iso7816_add(apdu, &application, sizeof(application)) < 0) {
147 fido_log_debug("%s: iso7816", __func__);
148 r = FIDO_ERR_INTERNAL;
149 goto fail;
150 }
151
152 do {
153 if (fido_tx(dev, cmd, iso7816_ptr(apdu),
154 iso7816_len(apdu)) < 0) {
155 fido_log_debug("%s: fido_tx", __func__);
156 r = FIDO_ERR_TX;
157 goto fail;
158 }
159 if (fido_rx(dev, cmd, &reply, sizeof(reply), ms) < 2) {
160 fido_log_debug("%s: fido_rx", __func__);
161 r = FIDO_ERR_RX;
162 goto fail;
163 }
164 if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) {
165 fido_log_debug("%s: usleep", __func__);
166 r = FIDO_ERR_RX;
167 goto fail;
168 }
169 } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
170
171 r = FIDO_OK;
172fail:
173 iso7816_free(&apdu);
174
175 return (r);
176}
177
178static int
179key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id,
180 int *found, int ms)
181{
182 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG;
183 iso7816_apdu_t *apdu = NULL;
184 unsigned char challenge[SHA256_DIGEST_LENGTH];
185 unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
186 unsigned char reply[8];
187 uint8_t key_id_len;
188 int r;
189
190 if (key_id->len > UINT8_MAX || rp_id == NULL) {
191 fido_log_debug("%s: key_id->len=%zu, rp_id=%p", __func__,
192 key_id->len, (const void *)rp_id);
193 r = FIDO_ERR_INVALID_ARGUMENT;
194 goto fail;
195 }
196
197 memset(&challenge, 0xff, sizeof(challenge));
198 memset(&rp_id_hash, 0, sizeof(rp_id_hash));
199
200 if (SHA256((const void *)rp_id, strlen(rp_id),
201 rp_id_hash) != rp_id_hash) {
202 fido_log_debug("%s: sha256", __func__);
203 r = FIDO_ERR_INTERNAL;
204 goto fail;
205 }
206
207 key_id_len = (uint8_t)key_id->len;
208
209 if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_CHECK, 2 *
210 SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len)) == NULL ||
211 iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
212 iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
213 iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
214 iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
215 fido_log_debug("%s: iso7816", __func__);
216 r = FIDO_ERR_INTERNAL;
217 goto fail;
218 }
219
220 if (fido_tx(dev, cmd, iso7816_ptr(apdu), iso7816_len(apdu)) < 0) {
221 fido_log_debug("%s: fido_tx", __func__);
222 r = FIDO_ERR_TX;
223 goto fail;
224 }
225 if (fido_rx(dev, cmd, &reply, sizeof(reply), ms) != 2) {
226 fido_log_debug("%s: fido_rx", __func__);
227 r = FIDO_ERR_RX;
228 goto fail;
229 }
230
231 switch ((reply[0] << 8) | reply[1]) {
232 case SW_CONDITIONS_NOT_SATISFIED:
233 *found = 1; /* key exists */
234 break;
235 case SW_WRONG_DATA:
236 *found = 0; /* key does not exist */
237 break;
238 default:
239 /* unexpected sw */
240 r = FIDO_ERR_INTERNAL;
241 goto fail;
242 }
243
244 r = FIDO_OK;
245fail:
246 iso7816_free(&apdu);
247
248 return (r);
249}
250
251static int
252parse_auth_reply(fido_blob_t *sig, fido_blob_t *ad, const char *rp_id,
253 const unsigned char *reply, size_t len)
254{
255 uint8_t flags;
256 uint32_t sigcount;
257
258 if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
259 fido_log_debug("%s: unexpected sw", __func__);
260 return (FIDO_ERR_RX);
261 }
262
263 len -= 2;
264
265 if (fido_buf_read(&reply, &len, &flags, sizeof(flags)) < 0 ||
266 fido_buf_read(&reply, &len, &sigcount, sizeof(sigcount)) < 0) {
267 fido_log_debug("%s: fido_buf_read", __func__);
268 return (FIDO_ERR_RX);
269 }
270
271 if (sig_get(sig, &reply, &len) < 0) {
272 fido_log_debug("%s: sig_get", __func__);
273 return (FIDO_ERR_RX);
274 }
275
276 if (authdata_fake(rp_id, flags, sigcount, ad) < 0) {
277 fido_log_debug("%s; authdata_fake", __func__);
278 return (FIDO_ERR_RX);
279 }
280
281 return (FIDO_OK);
282}
283
284static int
285do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
286 const fido_blob_t *key_id, fido_blob_t *sig, fido_blob_t *ad, int ms)
287{
288 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG;
289 iso7816_apdu_t *apdu = NULL;
290 unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
291 unsigned char reply[128];
292 int reply_len;
293 uint8_t key_id_len;
294 int r;
295
296#ifdef FIDO_FUZZ
297 ms = 0; /* XXX */
298#endif
299
300 if (cdh->len != SHA256_DIGEST_LENGTH || key_id->len > UINT8_MAX ||
301 rp_id == NULL) {
302 r = FIDO_ERR_INVALID_ARGUMENT;
303 goto fail;
304 }
305
306 memset(&rp_id_hash, 0, sizeof(rp_id_hash));
307
308 if (SHA256((const void *)rp_id, strlen(rp_id),
309 rp_id_hash) != rp_id_hash) {
310 fido_log_debug("%s: sha256", __func__);
311 r = FIDO_ERR_INTERNAL;
312 goto fail;
313 }
314
315 key_id_len = (uint8_t)key_id->len;
316
317 if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_SIGN, 2 *
318 SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len)) == NULL ||
319 iso7816_add(apdu, cdh->ptr, cdh->len) < 0 ||
320 iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
321 iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
322 iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
323 fido_log_debug("%s: iso7816", __func__);
324 r = FIDO_ERR_INTERNAL;
325 goto fail;
326 }
327
328 do {
329 if (fido_tx(dev, cmd, iso7816_ptr(apdu),
330 iso7816_len(apdu)) < 0) {
331 fido_log_debug("%s: fido_tx", __func__);
332 r = FIDO_ERR_TX;
333 goto fail;
334 }
335 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply),
336 ms)) < 2) {
337 fido_log_debug("%s: fido_rx", __func__);
338 r = FIDO_ERR_RX;
339 goto fail;
340 }
341 if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) {
342 fido_log_debug("%s: usleep", __func__);
343 r = FIDO_ERR_RX;
344 goto fail;
345 }
346 } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
347
348 if ((r = parse_auth_reply(sig, ad, rp_id, reply,
349 (size_t)reply_len)) != FIDO_OK) {
350 fido_log_debug("%s: parse_auth_reply", __func__);
351 goto fail;
352 }
353
354fail:
355 iso7816_free(&apdu);
356
357 return (r);
358}
359
360static int
361cbor_blob_from_ec_point(const uint8_t *ec_point, size_t ec_point_len,
362 fido_blob_t *cbor_blob)
363{
364 es256_pk_t *pk = NULL;
365 cbor_item_t *pk_cbor = NULL;
366 size_t alloc_len;
367 int ok = -1;
368
369 /* only handle uncompressed points */
370 if (ec_point_len != 65 || ec_point[0] != 0x04) {
371 fido_log_debug("%s: unexpected format", __func__);
372 goto fail;
373 }
374
375 if ((pk = es256_pk_new()) == NULL ||
376 es256_pk_set_x(pk, &ec_point[1]) < 0 ||
377 es256_pk_set_y(pk, &ec_point[33]) < 0) {
378 fido_log_debug("%s: es256_pk_set", __func__);
379 goto fail;
380 }
381
382 if ((pk_cbor = es256_pk_encode(pk, 0)) == NULL) {
383 fido_log_debug("%s: es256_pk_encode", __func__);
384 goto fail;
385 }
386
387 if ((cbor_blob->len = cbor_serialize_alloc(pk_cbor, &cbor_blob->ptr,
388 &alloc_len)) != 77) {
389 fido_log_debug("%s: cbor_serialize_alloc", __func__);
390 goto fail;
391 }
392
393 ok = 0;
394fail:
395 es256_pk_free(&pk);
396
397 if (pk_cbor)
398 cbor_decref(&pk_cbor);
399
400 return (ok);
401}
402
403static int
404encode_cred_authdata(const char *rp_id, const uint8_t *kh, uint8_t kh_len,
405 const uint8_t *pubkey, size_t pubkey_len, fido_blob_t *out)
406{
407 fido_authdata_t authdata;
408 fido_attcred_raw_t attcred_raw;
409 fido_blob_t pk_blob;
410 fido_blob_t authdata_blob;
411 cbor_item_t *authdata_cbor = NULL;
412 unsigned char *ptr;
413 size_t len;
414 size_t alloc_len;
415 int ok = -1;
416
417 memset(&pk_blob, 0, sizeof(pk_blob));
418 memset(&authdata, 0, sizeof(authdata));
419 memset(&authdata_blob, 0, sizeof(authdata_blob));
420 memset(out, 0, sizeof(*out));
421
422 if (rp_id == NULL) {
423 fido_log_debug("%s: NULL rp_id", __func__);
424 goto fail;
425 }
426
427 if (cbor_blob_from_ec_point(pubkey, pubkey_len, &pk_blob) < 0) {
428 fido_log_debug("%s: cbor_blob_from_ec_point", __func__);
429 goto fail;
430 }
431
432 if (SHA256((const void *)rp_id, strlen(rp_id),
433 authdata.rp_id_hash) != authdata.rp_id_hash) {
434 fido_log_debug("%s: sha256", __func__);
435 goto fail;
436 }
437
438 authdata.flags = (CTAP_AUTHDATA_ATT_CRED | CTAP_AUTHDATA_USER_PRESENT);
439 authdata.sigcount = 0;
440
441 memset(&attcred_raw.aaguid, 0, sizeof(attcred_raw.aaguid));
442 attcred_raw.id_len = (uint16_t)(kh_len << 8); /* XXX */
443
444 len = authdata_blob.len = sizeof(authdata) + sizeof(attcred_raw) +
445 kh_len + pk_blob.len;
446 ptr = authdata_blob.ptr = calloc(1, authdata_blob.len);
447
448 fido_log_debug("%s: ptr=%p, len=%zu", __func__, (void *)ptr, len);
449
450 if (authdata_blob.ptr == NULL)
451 goto fail;
452
453 if (fido_buf_write(&ptr, &len, &authdata, sizeof(authdata)) < 0 ||
454 fido_buf_write(&ptr, &len, &attcred_raw, sizeof(attcred_raw)) < 0 ||
455 fido_buf_write(&ptr, &len, kh, kh_len) < 0 ||
456 fido_buf_write(&ptr, &len, pk_blob.ptr, pk_blob.len) < 0) {
457 fido_log_debug("%s: fido_buf_write", __func__);
458 goto fail;
459 }
460
461 if ((authdata_cbor = fido_blob_encode(&authdata_blob)) == NULL) {
462 fido_log_debug("%s: fido_blob_encode", __func__);
463 goto fail;
464 }
465
466 if ((out->len = cbor_serialize_alloc(authdata_cbor, &out->ptr,
467 &alloc_len)) == 0) {
468 fido_log_debug("%s: cbor_serialize_alloc", __func__);
469 goto fail;
470 }
471
472 ok = 0;
473fail:
474 if (authdata_cbor)
475 cbor_decref(&authdata_cbor);
476
477 if (pk_blob.ptr) {
478 explicit_bzero(pk_blob.ptr, pk_blob.len);
479 free(pk_blob.ptr);
480 }
481 if (authdata_blob.ptr) {
482 explicit_bzero(authdata_blob.ptr, authdata_blob.len);
483 free(authdata_blob.ptr);
484 }
485
486 return (ok);
487}
488
489static int
490parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len)
491{
492 fido_blob_t x5c;
493 fido_blob_t sig;
494 fido_blob_t ad;
495 uint8_t dummy;
496 uint8_t pubkey[65];
497 uint8_t kh_len = 0;
498 uint8_t *kh = NULL;
499 int r;
500
501 memset(&x5c, 0, sizeof(x5c));
502 memset(&sig, 0, sizeof(sig));
503 memset(&ad, 0, sizeof(ad));
504 r = FIDO_ERR_RX;
505
506 /* status word */
507 if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
508 fido_log_debug("%s: unexpected sw", __func__);
509 goto fail;
510 }
511
512 len -= 2;
513
514 /* reserved byte */
515 if (fido_buf_read(&reply, &len, &dummy, sizeof(dummy)) < 0 ||
516 dummy != 0x05) {
517 fido_log_debug("%s: reserved byte", __func__);
518 goto fail;
519 }
520
521 /* pubkey + key handle */
522 if (fido_buf_read(&reply, &len, &pubkey, sizeof(pubkey)) < 0 ||
523 fido_buf_read(&reply, &len, &kh_len, sizeof(kh_len)) < 0 ||
524 (kh = calloc(1, kh_len)) == NULL ||
525 fido_buf_read(&reply, &len, kh, kh_len) < 0) {
526 fido_log_debug("%s: fido_buf_read", __func__);
527 goto fail;
528 }
529
530 /* x5c + sig */
531 if (x5c_get(&x5c, &reply, &len) < 0 ||
532 sig_get(&sig, &reply, &len) < 0) {
533 fido_log_debug("%s: x5c || sig", __func__);
534 goto fail;
535 }
536
537 /* authdata */
538 if (encode_cred_authdata(cred->rp.id, kh, kh_len, pubkey,
539 sizeof(pubkey), &ad) < 0) {
540 fido_log_debug("%s: encode_cred_authdata", __func__);
541 goto fail;
542 }
543
544 if (fido_cred_set_fmt(cred, "fido-u2f") != FIDO_OK ||
545 fido_cred_set_authdata(cred, ad.ptr, ad.len) != FIDO_OK ||
546 fido_cred_set_x509(cred, x5c.ptr, x5c.len) != FIDO_OK ||
547 fido_cred_set_sig(cred, sig.ptr, sig.len) != FIDO_OK) {
548 fido_log_debug("%s: fido_cred_set", __func__);
549 r = FIDO_ERR_INTERNAL;
550 goto fail;
551 }
552
553 r = FIDO_OK;
554fail:
555 if (kh) {
556 explicit_bzero(kh, kh_len);
557 free(kh);
558 }
559 if (x5c.ptr) {
560 explicit_bzero(x5c.ptr, x5c.len);
561 free(x5c.ptr);
562 }
563 if (sig.ptr) {
564 explicit_bzero(sig.ptr, sig.len);
565 free(sig.ptr);
566 }
567 if (ad.ptr) {
568 explicit_bzero(ad.ptr, ad.len);
569 free(ad.ptr);
570 }
571
572 return (r);
573}
574
575int
576u2f_register(fido_dev_t *dev, fido_cred_t *cred, int ms)
577{
578 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG;
579 iso7816_apdu_t *apdu = NULL;
580 unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
581 unsigned char reply[2048];
582 int reply_len;
583 int found;
584 int r;
585
586#ifdef FIDO_FUZZ
587 ms = 0; /* XXX */
588#endif
589
590 if (cred->rk == FIDO_OPT_TRUE || cred->uv == FIDO_OPT_TRUE) {
591 fido_log_debug("%s: rk=%d, uv=%d", __func__, cred->rk,
592 cred->uv);
593 return (FIDO_ERR_UNSUPPORTED_OPTION);
594 }
595
596 if (cred->type != COSE_ES256 || cred->cdh.ptr == NULL ||
597 cred->rp.id == NULL || cred->cdh.len != SHA256_DIGEST_LENGTH) {
598 fido_log_debug("%s: type=%d, cdh=(%p,%zu)" , __func__,
599 cred->type, (void *)cred->cdh.ptr, cred->cdh.len);
600 return (FIDO_ERR_INVALID_ARGUMENT);
601 }
602
603 for (size_t i = 0; i < cred->excl.len; i++) {
604 if ((r = key_lookup(dev, cred->rp.id, &cred->excl.ptr[i],
605 &found, ms)) != FIDO_OK) {
606 fido_log_debug("%s: key_lookup", __func__);
607 return (r);
608 }
609 if (found) {
610 if ((r = send_dummy_register(dev, ms)) != FIDO_OK) {
611 fido_log_debug("%s: send_dummy_register",
612 __func__);
613 return (r);
614 }
615 return (FIDO_ERR_CREDENTIAL_EXCLUDED);
616 }
617 }
618
619 memset(&rp_id_hash, 0, sizeof(rp_id_hash));
620
621 if (SHA256((const void *)cred->rp.id, strlen(cred->rp.id),
622 rp_id_hash) != rp_id_hash) {
623 fido_log_debug("%s: sha256", __func__);
624 return (FIDO_ERR_INTERNAL);
625 }
626
627 if ((apdu = iso7816_new(U2F_CMD_REGISTER, 0, 2 *
628 SHA256_DIGEST_LENGTH)) == NULL ||
629 iso7816_add(apdu, cred->cdh.ptr, cred->cdh.len) < 0 ||
630 iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
631 fido_log_debug("%s: iso7816", __func__);
632 r = FIDO_ERR_INTERNAL;
633 goto fail;
634 }
635
636 do {
637 if (fido_tx(dev, cmd, iso7816_ptr(apdu),
638 iso7816_len(apdu)) < 0) {
639 fido_log_debug("%s: fido_tx", __func__);
640 r = FIDO_ERR_TX;
641 goto fail;
642 }
643 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply),
644 ms)) < 2) {
645 fido_log_debug("%s: fido_rx", __func__);
646 r = FIDO_ERR_RX;
647 goto fail;
648 }
649 if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) {
650 fido_log_debug("%s: usleep", __func__);
651 r = FIDO_ERR_RX;
652 goto fail;
653 }
654 } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
655
656 if ((r = parse_register_reply(cred, reply,
657 (size_t)reply_len)) != FIDO_OK) {
658 fido_log_debug("%s: parse_register_reply", __func__);
659 goto fail;
660 }
661fail:
662 iso7816_free(&apdu);
663
664 return (r);
665}
666
667static int
668u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id,
669 fido_assert_t *fa, size_t idx, int ms)
670{
671 fido_blob_t sig;
672 fido_blob_t ad;
673 int found;
674 int r;
675
676 memset(&sig, 0, sizeof(sig));
677 memset(&ad, 0, sizeof(ad));
678
679 if ((r = key_lookup(dev, fa->rp_id, key_id, &found, ms)) != FIDO_OK) {
680 fido_log_debug("%s: key_lookup", __func__);
681 goto fail;
682 }
683
684 if (!found) {
685 fido_log_debug("%s: not found", __func__);
686 r = FIDO_ERR_CREDENTIAL_EXCLUDED;
687 goto fail;
688 }
689
690 if (fa->up == FIDO_OPT_FALSE) {
691 fido_log_debug("%s: checking for key existence only", __func__);
692 r = FIDO_ERR_USER_PRESENCE_REQUIRED;
693 goto fail;
694 }
695
696 if ((r = do_auth(dev, &fa->cdh, fa->rp_id, key_id, &sig, &ad,
697 ms)) != FIDO_OK) {
698 fido_log_debug("%s: do_auth", __func__);
699 goto fail;
700 }
701
702 if (fido_blob_set(&fa->stmt[idx].id, key_id->ptr, key_id->len) < 0 ||
703 fido_assert_set_authdata(fa, idx, ad.ptr, ad.len) != FIDO_OK ||
704 fido_assert_set_sig(fa, idx, sig.ptr, sig.len) != FIDO_OK) {
705 fido_log_debug("%s: fido_assert_set", __func__);
706 r = FIDO_ERR_INTERNAL;
707 goto fail;
708 }
709
710 r = FIDO_OK;
711fail:
712 if (sig.ptr) {
713 explicit_bzero(sig.ptr, sig.len);
714 free(sig.ptr);
715 }
716 if (ad.ptr) {
717 explicit_bzero(ad.ptr, ad.len);
718 free(ad.ptr);
719 }
720
721 return (r);
722}
723
724int
725u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int ms)
726{
727 int nauth_ok = 0;
728 int r;
729
730 if (fa->uv == FIDO_OPT_TRUE || fa->allow_list.ptr == NULL) {
731 fido_log_debug("%s: uv=%d, allow_list=%p", __func__, fa->uv,
732 (void *)fa->allow_list.ptr);
733 return (FIDO_ERR_UNSUPPORTED_OPTION);
734 }
735
736 if ((r = fido_assert_set_count(fa, fa->allow_list.len)) != FIDO_OK) {
737 fido_log_debug("%s: fido_assert_set_count", __func__);
738 return (r);
739 }
740
741 for (size_t i = 0; i < fa->allow_list.len; i++) {
742 if ((r = u2f_authenticate_single(dev, &fa->allow_list.ptr[i],
743 fa, nauth_ok, ms)) == FIDO_OK) {
744 nauth_ok++;
745 } else if (r != FIDO_ERR_CREDENTIAL_EXCLUDED) {
746 fido_log_debug("%s: u2f_authenticate_single", __func__);
747 return (r);
748 }
749 /* ignore credentials that don't exist */
750 }
751
752 fa->stmt_len = nauth_ok;
753
754 if (nauth_ok == 0)
755 return (FIDO_ERR_NO_CREDENTIALS);
756
757 return (FIDO_OK);
758}