diff options
author | nicoo <nicoo@debian.org> | 2020-02-12 13:42:22 +0100 |
---|---|---|
committer | Nicolas Braud-Santoni <nicolas@braud-santoni.eu> | 2020-02-12 13:42:22 +0100 |
commit | c79050aa44b8836d836c5dd22a383a073c28b74b (patch) | |
tree | 7bcca9fabd7718bf87ca600a6594f57b76d8de7d /src |
Import upstream release 1.3.0
Closes: #951184
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 104 | ||||
-rw-r--r-- | src/aes256.c | 98 | ||||
-rw-r--r-- | src/assert.c | 1090 | ||||
-rw-r--r-- | src/authkey.c | 98 | ||||
-rw-r--r-- | src/bio.c | 844 | ||||
-rw-r--r-- | src/blob.c | 102 | ||||
-rw-r--r-- | src/blob.h | 28 | ||||
-rw-r--r-- | src/buf.c | 34 | ||||
-rw-r--r-- | src/cbor.c | 1520 | ||||
-rw-r--r-- | src/cred.c | 1034 | ||||
-rw-r--r-- | src/credman.c | 736 | ||||
-rw-r--r-- | src/dev.c | 284 | ||||
-rwxr-xr-x | src/diff_exports.sh | 23 | ||||
-rw-r--r-- | src/ecdh.c | 121 | ||||
-rw-r--r-- | src/eddsa.c | 169 | ||||
-rw-r--r-- | src/err.c | 122 | ||||
-rw-r--r-- | src/es256.c | 434 | ||||
-rw-r--r-- | src/export.gnu | 183 | ||||
-rw-r--r-- | src/export.llvm | 178 | ||||
-rw-r--r-- | src/export.msvc | 179 | ||||
-rw-r--r-- | src/extern.h | 132 | ||||
-rw-r--r-- | src/fido.h | 194 | ||||
-rw-r--r-- | src/fido/bio.h | 95 | ||||
-rw-r--r-- | src/fido/credman.h | 74 | ||||
-rw-r--r-- | src/fido/eddsa.h | 40 | ||||
-rw-r--r-- | src/fido/err.h | 69 | ||||
-rw-r--r-- | src/fido/es256.h | 34 | ||||
-rw-r--r-- | src/fido/param.h | 84 | ||||
-rw-r--r-- | src/fido/rs256.h | 22 | ||||
-rw-r--r-- | src/hid.c | 70 | ||||
-rw-r--r-- | src/hid_linux.c | 344 | ||||
-rw-r--r-- | src/hid_openbsd.c | 277 | ||||
-rw-r--r-- | src/hid_osx.c | 410 | ||||
-rw-r--r-- | src/hid_win.c | 324 | ||||
-rw-r--r-- | src/info.c | 410 | ||||
-rw-r--r-- | src/io.c | 256 | ||||
-rw-r--r-- | src/iso7816.c | 70 | ||||
-rw-r--r-- | src/iso7816.h | 38 | ||||
-rw-r--r-- | src/libfido2.pc.in | 12 | ||||
-rw-r--r-- | src/log.c | 63 | ||||
-rw-r--r-- | src/packed.h | 22 | ||||
-rw-r--r-- | src/pin.c | 428 | ||||
-rw-r--r-- | src/reset.c | 40 | ||||
-rw-r--r-- | src/rs256.c | 204 | ||||
-rw-r--r-- | src/types.h | 171 | ||||
-rw-r--r-- | src/u2f.c | 758 |
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 | |||
5 | add_definitions(-D_FIDO_INTERNAL) | ||
6 | |||
7 | list(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 | |||
33 | if(FUZZ) | ||
34 | list(APPEND FIDO_SOURCES ../fuzz/uniform_random.c) | ||
35 | list(APPEND FIDO_SOURCES ../fuzz/wrap.c) | ||
36 | endif() | ||
37 | |||
38 | if(WIN32) | ||
39 | list(APPEND COMPAT_SOURCES hid_win.c) | ||
40 | elseif(APPLE) | ||
41 | list(APPEND COMPAT_SOURCES hid_osx.c) | ||
42 | elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") | ||
43 | list(APPEND COMPAT_SOURCES hid_linux.c) | ||
44 | elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") | ||
45 | list(APPEND COMPAT_SOURCES hid_openbsd.c) | ||
46 | endif() | ||
47 | |||
48 | list(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 | ||
57 | add_library(fido2 STATIC ${FIDO_SOURCES} ${COMPAT_SOURCES}) | ||
58 | target_link_libraries(fido2 ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES} | ||
59 | ${UDEV_LIBRARIES} ${BASE_LIBRARIES}) | ||
60 | if(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() | ||
67 | elseif(APPLE) | ||
68 | target_link_libraries(fido2 "-framework CoreFoundation" | ||
69 | "-framework IOKit") | ||
70 | endif() | ||
71 | install(TARGETS fido2 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} | ||
72 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) | ||
73 | |||
74 | # dynamic library | ||
75 | add_library(fido2_shared SHARED ${FIDO_SOURCES} ${COMPAT_SOURCES}) | ||
76 | target_link_libraries(fido2_shared ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES} | ||
77 | ${UDEV_LIBRARIES} ${BASE_LIBRARIES}) | ||
78 | if(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() | ||
86 | elseif(APPLE) | ||
87 | target_link_libraries(fido2_shared "-framework CoreFoundation" | ||
88 | "-framework IOKit") | ||
89 | endif() | ||
90 | set_target_properties(fido2_shared PROPERTIES OUTPUT_NAME fido2 | ||
91 | VERSION ${LIB_VERSION} SOVERSION ${LIB_SOVERSION}) | ||
92 | install(TARGETS fido2_shared | ||
93 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} | ||
94 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} | ||
95 | RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}) | ||
96 | |||
97 | install(FILES fido.h DESTINATION include) | ||
98 | install(DIRECTORY fido DESTINATION include) | ||
99 | |||
100 | if(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") | ||
104 | endif() | ||
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 | |||
12 | int | ||
13 | aes256_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; | ||
43 | fail: | ||
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 | |||
56 | int | ||
57 | aes256_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; | ||
87 | fail: | ||
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 | |||
18 | static int | ||
19 | adjust_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 | |||
54 | static int | ||
55 | parse_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 | |||
82 | static int | ||
83 | fido_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; | ||
163 | fail: | ||
164 | cbor_vector_free(argv, nitems(argv)); | ||
165 | free(f.ptr); | ||
166 | |||
167 | return (r); | ||
168 | } | ||
169 | |||
170 | static int | ||
171 | fido_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 | |||
211 | static int | ||
212 | fido_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 | |||
225 | static int | ||
226 | fido_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 | |||
254 | static int | ||
255 | fido_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 | |||
274 | static int | ||
275 | decrypt_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 | |||
292 | int | ||
293 | fido_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 | |||
326 | fail: | ||
327 | es256_pk_free(&pk); | ||
328 | fido_blob_free(&ecdh); | ||
329 | |||
330 | return (r); | ||
331 | } | ||
332 | |||
333 | int | ||
334 | fido_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 | |||
354 | static int | ||
355 | check_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 | |||
366 | static int | ||
367 | get_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; | ||
409 | fail: | ||
410 | if (item != NULL) | ||
411 | cbor_decref(&item); | ||
412 | |||
413 | return (ok); | ||
414 | } | ||
415 | |||
416 | int | ||
417 | fido_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; | ||
444 | fail: | ||
445 | if (pkey != NULL) | ||
446 | EVP_PKEY_free(pkey); | ||
447 | |||
448 | return (ok); | ||
449 | } | ||
450 | |||
451 | int | ||
452 | fido_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; | ||
479 | fail: | ||
480 | if (pkey != NULL) | ||
481 | EVP_PKEY_free(pkey); | ||
482 | |||
483 | return (ok); | ||
484 | } | ||
485 | |||
486 | int | ||
487 | fido_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; | ||
523 | fail: | ||
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 | |||
533 | int | ||
534 | fido_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; | ||
610 | out: | ||
611 | explicit_bzero(buf, sizeof(buf)); | ||
612 | |||
613 | return (r); | ||
614 | } | ||
615 | |||
616 | int | ||
617 | fido_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 | |||
626 | int | ||
627 | fido_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 | |||
637 | int | ||
638 | fido_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 | |||
654 | int | ||
655 | fido_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); | ||
680 | fail: | ||
681 | free(id.ptr); | ||
682 | |||
683 | return (r); | ||
684 | |||
685 | } | ||
686 | |||
687 | int | ||
688 | fido_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 | |||
698 | int | ||
699 | fido_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 | |||
707 | int | ||
708 | fido_assert_set_up(fido_assert_t *assert, fido_opt_t up) | ||
709 | { | ||
710 | assert->up = up; | ||
711 | |||
712 | return (FIDO_OK); | ||
713 | } | ||
714 | |||
715 | int | ||
716 | fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv) | ||
717 | { | ||
718 | assert->uv = uv; | ||
719 | |||
720 | return (FIDO_OK); | ||
721 | } | ||
722 | |||
723 | const unsigned char * | ||
724 | fido_assert_clientdata_hash_ptr(const fido_assert_t *assert) | ||
725 | { | ||
726 | return (assert->cdh.ptr); | ||
727 | } | ||
728 | |||
729 | size_t | ||
730 | fido_assert_clientdata_hash_len(const fido_assert_t *assert) | ||
731 | { | ||
732 | return (assert->cdh.len); | ||
733 | } | ||
734 | |||
735 | fido_assert_t * | ||
736 | fido_assert_new(void) | ||
737 | { | ||
738 | return (calloc(1, sizeof(fido_assert_t))); | ||
739 | } | ||
740 | |||
741 | void | ||
742 | fido_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 | |||
759 | void | ||
760 | fido_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 | |||
786 | void | ||
787 | fido_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 | |||
802 | size_t | ||
803 | fido_assert_count(const fido_assert_t *assert) | ||
804 | { | ||
805 | return (assert->stmt_len); | ||
806 | } | ||
807 | |||
808 | const char * | ||
809 | fido_assert_rp_id(const fido_assert_t *assert) | ||
810 | { | ||
811 | return (assert->rp_id); | ||
812 | } | ||
813 | |||
814 | uint8_t | ||
815 | fido_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 | |||
823 | uint32_t | ||
824 | fido_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 | |||
832 | const unsigned char * | ||
833 | fido_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 | |||
841 | size_t | ||
842 | fido_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 | |||
850 | const unsigned char * | ||
851 | fido_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 | |||
859 | size_t | ||
860 | fido_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 | |||
868 | const unsigned char * | ||
869 | fido_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 | |||
877 | size_t | ||
878 | fido_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 | |||
886 | const unsigned char * | ||
887 | fido_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 | |||
895 | size_t | ||
896 | fido_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 | |||
904 | const char * | ||
905 | fido_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 | |||
913 | const char * | ||
914 | fido_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 | |||
922 | const char * | ||
923 | fido_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 | |||
931 | const unsigned char * | ||
932 | fido_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 | |||
940 | size_t | ||
941 | fido_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 | |||
949 | static void | ||
950 | fido_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 | |||
961 | int | ||
962 | fido_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; | ||
990 | fail: | ||
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 | |||
1000 | int | ||
1001 | fido_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; | ||
1028 | fail: | ||
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 | |||
1038 | static void | ||
1039 | fido_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 | |||
1046 | int | ||
1047 | fido_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 */ | ||
1068 | int | ||
1069 | fido_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 | |||
10 | static int | ||
11 | parse_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 | |||
25 | static int | ||
26 | fido_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; | ||
54 | fail: | ||
55 | cbor_vector_free(argv, nitems(argv)); | ||
56 | free(f.ptr); | ||
57 | |||
58 | return (r); | ||
59 | } | ||
60 | |||
61 | static int | ||
62 | fido_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 | |||
82 | static int | ||
83 | fido_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 | |||
94 | int | ||
95 | fido_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 | |||
21 | static int | ||
22 | bio_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; | ||
55 | fail: | ||
56 | free(cbor); | ||
57 | |||
58 | return (ok); | ||
59 | } | ||
60 | |||
61 | static int | ||
62 | bio_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; | ||
120 | fail: | ||
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 | |||
130 | static void | ||
131 | bio_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 | |||
139 | static void | ||
140 | bio_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 | |||
150 | static int | ||
151 | decode_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 | |||
171 | static int | ||
172 | decode_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 | |||
197 | static int | ||
198 | bio_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 | |||
235 | static int | ||
236 | bio_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 | |||
259 | static int | ||
260 | bio_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 | |||
272 | int | ||
273 | fido_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 | |||
282 | static int | ||
283 | bio_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; | ||
304 | fail: | ||
305 | cbor_vector_free(argv, nitems(argv)); | ||
306 | |||
307 | return (r); | ||
308 | } | ||
309 | |||
310 | int | ||
311 | fido_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 | |||
320 | static void | ||
321 | bio_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 | |||
330 | static int | ||
331 | bio_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 | |||
365 | static int | ||
366 | bio_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 | |||
381 | static int | ||
382 | bio_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 | |||
414 | static int | ||
415 | bio_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; | ||
436 | fail: | ||
437 | cbor_vector_free(argv, nitems(argv)); | ||
438 | |||
439 | return (r); | ||
440 | } | ||
441 | |||
442 | int | ||
443 | fido_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; | ||
471 | fail: | ||
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 | |||
482 | static int | ||
483 | bio_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 | |||
507 | static int | ||
508 | bio_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; | ||
530 | fail: | ||
531 | cbor_vector_free(argv, nitems(argv)); | ||
532 | |||
533 | return (r); | ||
534 | } | ||
535 | |||
536 | int | ||
537 | fido_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 | |||
546 | static int | ||
547 | bio_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 | |||
561 | int | ||
562 | fido_bio_dev_enroll_cancel(fido_dev_t *dev) | ||
563 | { | ||
564 | return (bio_enroll_cancel_wait(dev, -1)); | ||
565 | } | ||
566 | |||
567 | static int | ||
568 | bio_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; | ||
589 | fail: | ||
590 | cbor_vector_free(argv, nitems(argv)); | ||
591 | |||
592 | return (r); | ||
593 | } | ||
594 | |||
595 | int | ||
596 | fido_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 | |||
602 | static void | ||
603 | bio_reset_info(fido_bio_info_t *i) | ||
604 | { | ||
605 | i->type = 0; | ||
606 | i->max_samples = 0; | ||
607 | } | ||
608 | |||
609 | static int | ||
610 | bio_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 | |||
643 | static int | ||
644 | bio_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 | |||
667 | static int | ||
668 | bio_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 | |||
681 | int | ||
682 | fido_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 | |||
687 | const char * | ||
688 | fido_bio_template_name(const fido_bio_template_t *t) | ||
689 | { | ||
690 | return (t->name); | ||
691 | } | ||
692 | |||
693 | const unsigned char * | ||
694 | fido_bio_template_id_ptr(const fido_bio_template_t *t) | ||
695 | { | ||
696 | return (t->id.ptr); | ||
697 | } | ||
698 | |||
699 | size_t | ||
700 | fido_bio_template_id_len(const fido_bio_template_t *t) | ||
701 | { | ||
702 | return (t->id.len); | ||
703 | } | ||
704 | |||
705 | size_t | ||
706 | fido_bio_template_array_count(const fido_bio_template_array_t *ta) | ||
707 | { | ||
708 | return (ta->n_rx); | ||
709 | } | ||
710 | |||
711 | fido_bio_template_array_t * | ||
712 | fido_bio_template_array_new(void) | ||
713 | { | ||
714 | return (calloc(1, sizeof(fido_bio_template_array_t))); | ||
715 | } | ||
716 | |||
717 | fido_bio_template_t * | ||
718 | fido_bio_template_new(void) | ||
719 | { | ||
720 | return (calloc(1, sizeof(fido_bio_template_t))); | ||
721 | } | ||
722 | |||
723 | void | ||
724 | fido_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 | |||
736 | void | ||
737 | fido_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 | |||
749 | int | ||
750 | fido_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 | |||
761 | int | ||
762 | fido_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 | |||
775 | const fido_bio_template_t * | ||
776 | fido_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 | |||
784 | fido_bio_enroll_t * | ||
785 | fido_bio_enroll_new(void) | ||
786 | { | ||
787 | return (calloc(1, sizeof(fido_bio_enroll_t))); | ||
788 | } | ||
789 | |||
790 | fido_bio_info_t * | ||
791 | fido_bio_info_new(void) | ||
792 | { | ||
793 | return (calloc(1, sizeof(fido_bio_info_t))); | ||
794 | } | ||
795 | |||
796 | uint8_t | ||
797 | fido_bio_info_type(const fido_bio_info_t *i) | ||
798 | { | ||
799 | return (i->type); | ||
800 | } | ||
801 | |||
802 | uint8_t | ||
803 | fido_bio_info_max_samples(const fido_bio_info_t *i) | ||
804 | { | ||
805 | return (i->max_samples); | ||
806 | } | ||
807 | |||
808 | void | ||
809 | fido_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 | |||
822 | void | ||
823 | fido_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 | |||
834 | uint8_t | ||
835 | fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *e) | ||
836 | { | ||
837 | return (e->remaining_samples); | ||
838 | } | ||
839 | |||
840 | uint8_t | ||
841 | fido_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 | |||
10 | fido_blob_t * | ||
11 | fido_blob_new(void) | ||
12 | { | ||
13 | return (calloc(1, sizeof(fido_blob_t))); | ||
14 | } | ||
15 | |||
16 | int | ||
17 | fido_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 | |||
44 | void | ||
45 | fido_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 | |||
63 | void | ||
64 | fido_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 | |||
83 | cbor_item_t * | ||
84 | fido_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 | |||
92 | int | ||
93 | fido_blob_decode(const cbor_item_t *item, fido_blob_t *b) | ||
94 | { | ||
95 | return (cbor_bytestring_copy(item, &b->ptr, &b->len)); | ||
96 | } | ||
97 | |||
98 | int | ||
99 | fido_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 | |||
10 | typedef struct fido_blob { | ||
11 | unsigned char *ptr; | ||
12 | size_t len; | ||
13 | } fido_blob_t; | ||
14 | |||
15 | typedef struct fido_blob_array { | ||
16 | fido_blob_t *ptr; | ||
17 | size_t len; | ||
18 | } fido_blob_array_t; | ||
19 | |||
20 | cbor_item_t *fido_blob_encode(const fido_blob_t *); | ||
21 | fido_blob_t *fido_blob_new(void); | ||
22 | int fido_blob_decode(const cbor_item_t *, fido_blob_t *); | ||
23 | int fido_blob_is_empty(const fido_blob_t *); | ||
24 | int fido_blob_set(fido_blob_t *, const unsigned char *, size_t); | ||
25 | void fido_blob_free(fido_blob_t **); | ||
26 | void 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 | |||
10 | int | ||
11 | fido_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 | |||
23 | int | ||
24 | fido_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 | |||
14 | static int | ||
15 | check_key_type(cbor_item_t *item) | ||
16 | { | ||
17 | if (item->type == CBOR_TYPE_UINT || item->type == CBOR_TYPE_NEGINT || | ||
18 | item->type == CBOR_TYPE_STRING) | ||
19 | return (0); | ||
20 | |||
21 | fido_log_debug("%s: invalid type: %d", __func__, item->type); | ||
22 | |||
23 | return (-1); | ||
24 | } | ||
25 | |||
26 | /* | ||
27 | * Validate CTAP2 canonical CBOR encoding rules for maps. | ||
28 | */ | ||
29 | static int | ||
30 | ctap_check_cbor(cbor_item_t *prev, cbor_item_t *curr) | ||
31 | { | ||
32 | size_t curr_len; | ||
33 | size_t prev_len; | ||
34 | |||
35 | if (check_key_type(prev) < 0 || check_key_type(curr) < 0) | ||
36 | return (-1); | ||
37 | |||
38 | if (prev->type != curr->type) { | ||
39 | if (prev->type < curr->type) | ||
40 | return (0); | ||
41 | fido_log_debug("%s: unsorted types", __func__); | ||
42 | return (-1); | ||
43 | } | ||
44 | |||
45 | if (curr->type == CBOR_TYPE_UINT || curr->type == CBOR_TYPE_NEGINT) { | ||
46 | if (cbor_int_get_width(curr) >= cbor_int_get_width(prev) && | ||
47 | cbor_get_int(curr) > cbor_get_int(prev)) | ||
48 | return (0); | ||
49 | } else { | ||
50 | curr_len = cbor_string_length(curr); | ||
51 | prev_len = cbor_string_length(prev); | ||
52 | |||
53 | if (curr_len > prev_len || (curr_len == prev_len && | ||
54 | memcmp(cbor_string_handle(prev), cbor_string_handle(curr), | ||
55 | curr_len) < 0)) | ||
56 | return (0); | ||
57 | } | ||
58 | |||
59 | fido_log_debug("%s: invalid cbor", __func__); | ||
60 | |||
61 | return (-1); | ||
62 | } | ||
63 | |||
64 | int | ||
65 | cbor_map_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *, | ||
66 | const cbor_item_t *, void *)) | ||
67 | { | ||
68 | struct cbor_pair *v; | ||
69 | size_t n; | ||
70 | |||
71 | if ((v = cbor_map_handle(item)) == NULL) { | ||
72 | fido_log_debug("%s: cbor_map_handle", __func__); | ||
73 | return (-1); | ||
74 | } | ||
75 | |||
76 | n = cbor_map_size(item); | ||
77 | |||
78 | for (size_t i = 0; i < n; i++) { | ||
79 | if (v[i].key == NULL || v[i].value == NULL) { | ||
80 | fido_log_debug("%s: key=%p, value=%p for i=%zu", | ||
81 | __func__, (void *)v[i].key, (void *)v[i].value, i); | ||
82 | return (-1); | ||
83 | } | ||
84 | if (i && ctap_check_cbor(v[i - 1].key, v[i].key) < 0) { | ||
85 | fido_log_debug("%s: ctap_check_cbor", __func__); | ||
86 | return (-1); | ||
87 | } | ||
88 | if (f(v[i].key, v[i].value, arg) < 0) { | ||
89 | fido_log_debug("%s: iterator < 0 on i=%zu", __func__, | ||
90 | i); | ||
91 | return (-1); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | return (0); | ||
96 | } | ||
97 | |||
98 | int | ||
99 | cbor_array_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *, | ||
100 | void *)) | ||
101 | { | ||
102 | cbor_item_t **v; | ||
103 | size_t n; | ||
104 | |||
105 | if ((v = cbor_array_handle(item)) == NULL) { | ||
106 | fido_log_debug("%s: cbor_array_handle", __func__); | ||
107 | return (-1); | ||
108 | } | ||
109 | |||
110 | n = cbor_array_size(item); | ||
111 | |||
112 | for (size_t i = 0; i < n; i++) | ||
113 | if (v[i] == NULL || f(v[i], arg) < 0) { | ||
114 | fido_log_debug("%s: iterator < 0 on i=%zu,%p", | ||
115 | __func__, i, (void *)v[i]); | ||
116 | return (-1); | ||
117 | } | ||
118 | |||
119 | return (0); | ||
120 | } | ||
121 | |||
122 | int | ||
123 | cbor_parse_reply(const unsigned char *blob, size_t blob_len, void *arg, | ||
124 | int(*parser)(const cbor_item_t *, const cbor_item_t *, void *)) | ||
125 | { | ||
126 | cbor_item_t *item = NULL; | ||
127 | struct cbor_load_result cbor; | ||
128 | int r; | ||
129 | |||
130 | if (blob_len < 1) { | ||
131 | fido_log_debug("%s: blob_len=%zu", __func__, blob_len); | ||
132 | r = FIDO_ERR_RX; | ||
133 | goto fail; | ||
134 | } | ||
135 | |||
136 | if (blob[0] != FIDO_OK) { | ||
137 | fido_log_debug("%s: blob[0]=0x%02x", __func__, blob[0]); | ||
138 | r = blob[0]; | ||
139 | goto fail; | ||
140 | } | ||
141 | |||
142 | if ((item = cbor_load(blob + 1, blob_len - 1, &cbor)) == NULL) { | ||
143 | fido_log_debug("%s: cbor_load", __func__); | ||
144 | r = FIDO_ERR_RX_NOT_CBOR; | ||
145 | goto fail; | ||
146 | } | ||
147 | |||
148 | if (cbor_isa_map(item) == false || | ||
149 | cbor_map_is_definite(item) == false) { | ||
150 | fido_log_debug("%s: cbor type", __func__); | ||
151 | r = FIDO_ERR_RX_INVALID_CBOR; | ||
152 | goto fail; | ||
153 | } | ||
154 | |||
155 | if (cbor_map_iter(item, arg, parser) < 0) { | ||
156 | fido_log_debug("%s: cbor_map_iter", __func__); | ||
157 | r = FIDO_ERR_RX_INVALID_CBOR; | ||
158 | goto fail; | ||
159 | } | ||
160 | |||
161 | r = FIDO_OK; | ||
162 | fail: | ||
163 | if (item != NULL) | ||
164 | cbor_decref(&item); | ||
165 | |||
166 | return (r); | ||
167 | } | ||
168 | |||
169 | void | ||
170 | cbor_vector_free(cbor_item_t **item, size_t len) | ||
171 | { | ||
172 | for (size_t i = 0; i < len; i++) | ||
173 | if (item[i] != NULL) | ||
174 | cbor_decref(&item[i]); | ||
175 | } | ||
176 | |||
177 | int | ||
178 | cbor_bytestring_copy(const cbor_item_t *item, unsigned char **buf, size_t *len) | ||
179 | { | ||
180 | if (*buf != NULL || *len != 0) { | ||
181 | fido_log_debug("%s: dup", __func__); | ||
182 | return (-1); | ||
183 | } | ||
184 | |||
185 | if (cbor_isa_bytestring(item) == false || | ||
186 | cbor_bytestring_is_definite(item) == false) { | ||
187 | fido_log_debug("%s: cbor type", __func__); | ||
188 | return (-1); | ||
189 | } | ||
190 | |||
191 | *len = cbor_bytestring_length(item); | ||
192 | if ((*buf = malloc(*len)) == NULL) { | ||
193 | *len = 0; | ||
194 | return (-1); | ||
195 | } | ||
196 | |||
197 | memcpy(*buf, cbor_bytestring_handle(item), *len); | ||
198 | |||
199 | return (0); | ||
200 | } | ||
201 | |||
202 | int | ||
203 | cbor_string_copy(const cbor_item_t *item, char **str) | ||
204 | { | ||
205 | size_t len; | ||
206 | |||
207 | if (*str != NULL) { | ||
208 | fido_log_debug("%s: dup", __func__); | ||
209 | return (-1); | ||
210 | } | ||
211 | |||
212 | if (cbor_isa_string(item) == false || | ||
213 | cbor_string_is_definite(item) == false) { | ||
214 | fido_log_debug("%s: cbor type", __func__); | ||
215 | return (-1); | ||
216 | } | ||
217 | |||
218 | if ((len = cbor_string_length(item)) == SIZE_MAX || | ||
219 | (*str = malloc(len + 1)) == NULL) | ||
220 | return (-1); | ||
221 | |||
222 | memcpy(*str, cbor_string_handle(item), len); | ||
223 | (*str)[len] = '\0'; | ||
224 | |||
225 | return (0); | ||
226 | } | ||
227 | |||
228 | int | ||
229 | cbor_add_bytestring(cbor_item_t *item, const char *key, | ||
230 | const unsigned char *value, size_t value_len) | ||
231 | { | ||
232 | struct cbor_pair pair; | ||
233 | int ok = -1; | ||
234 | |||
235 | memset(&pair, 0, sizeof(pair)); | ||
236 | |||
237 | if ((pair.key = cbor_build_string(key)) == NULL || | ||
238 | (pair.value = cbor_build_bytestring(value, value_len)) == NULL) { | ||
239 | fido_log_debug("%s: cbor_build", __func__); | ||
240 | goto fail; | ||
241 | } | ||
242 | |||
243 | if (!cbor_map_add(item, pair)) { | ||
244 | fido_log_debug("%s: cbor_map_add", __func__); | ||
245 | goto fail; | ||
246 | } | ||
247 | |||
248 | ok = 0; | ||
249 | fail: | ||
250 | if (pair.key) | ||
251 | cbor_decref(&pair.key); | ||
252 | if (pair.value) | ||
253 | cbor_decref(&pair.value); | ||
254 | |||
255 | return (ok); | ||
256 | } | ||
257 | |||
258 | int | ||
259 | cbor_add_string(cbor_item_t *item, const char *key, const char *value) | ||
260 | { | ||
261 | struct cbor_pair pair; | ||
262 | int ok = -1; | ||
263 | |||
264 | memset(&pair, 0, sizeof(pair)); | ||
265 | |||
266 | if ((pair.key = cbor_build_string(key)) == NULL || | ||
267 | (pair.value = cbor_build_string(value)) == NULL) { | ||
268 | fido_log_debug("%s: cbor_build", __func__); | ||
269 | goto fail; | ||
270 | } | ||
271 | |||
272 | if (!cbor_map_add(item, pair)) { | ||
273 | fido_log_debug("%s: cbor_map_add", __func__); | ||
274 | goto fail; | ||
275 | } | ||
276 | |||
277 | ok = 0; | ||
278 | fail: | ||
279 | if (pair.key) | ||
280 | cbor_decref(&pair.key); | ||
281 | if (pair.value) | ||
282 | cbor_decref(&pair.value); | ||
283 | |||
284 | return (ok); | ||
285 | } | ||
286 | |||
287 | int | ||
288 | cbor_add_bool(cbor_item_t *item, const char *key, fido_opt_t value) | ||
289 | { | ||
290 | struct cbor_pair pair; | ||
291 | int ok = -1; | ||
292 | |||
293 | memset(&pair, 0, sizeof(pair)); | ||
294 | |||
295 | if ((pair.key = cbor_build_string(key)) == NULL || | ||
296 | (pair.value = cbor_build_bool(value == FIDO_OPT_TRUE)) == NULL) { | ||
297 | fido_log_debug("%s: cbor_build", __func__); | ||
298 | goto fail; | ||
299 | } | ||
300 | |||
301 | if (!cbor_map_add(item, pair)) { | ||
302 | fido_log_debug("%s: cbor_map_add", __func__); | ||
303 | goto fail; | ||
304 | } | ||
305 | |||
306 | ok = 0; | ||
307 | fail: | ||
308 | if (pair.key) | ||
309 | cbor_decref(&pair.key); | ||
310 | if (pair.value) | ||
311 | cbor_decref(&pair.value); | ||
312 | |||
313 | return (ok); | ||
314 | } | ||
315 | |||
316 | static int | ||
317 | cbor_add_arg(cbor_item_t *item, uint8_t n, cbor_item_t *arg) | ||
318 | { | ||
319 | struct cbor_pair pair; | ||
320 | int ok = -1; | ||
321 | |||
322 | memset(&pair, 0, sizeof(pair)); | ||
323 | |||
324 | if (arg == NULL) | ||
325 | return (0); /* empty argument */ | ||
326 | |||
327 | if ((pair.key = cbor_build_uint8(n)) == NULL) { | ||
328 | fido_log_debug("%s: cbor_build", __func__); | ||
329 | goto fail; | ||
330 | } | ||
331 | |||
332 | pair.value = arg; | ||
333 | |||
334 | if (!cbor_map_add(item, pair)) { | ||
335 | fido_log_debug("%s: cbor_map_add", __func__); | ||
336 | goto fail; | ||
337 | } | ||
338 | |||
339 | ok = 0; | ||
340 | fail: | ||
341 | if (pair.key) | ||
342 | cbor_decref(&pair.key); | ||
343 | |||
344 | return (ok); | ||
345 | } | ||
346 | |||
347 | cbor_item_t * | ||
348 | cbor_flatten_vector(cbor_item_t *argv[], size_t argc) | ||
349 | { | ||
350 | cbor_item_t *map; | ||
351 | uint8_t i; | ||
352 | |||
353 | if (argc > UINT8_MAX - 1) | ||
354 | return (NULL); | ||
355 | |||
356 | if ((map = cbor_new_definite_map(argc)) == NULL) | ||
357 | return (NULL); | ||
358 | |||
359 | for (i = 0; i < argc; i++) | ||
360 | if (cbor_add_arg(map, i + 1, argv[i]) < 0) | ||
361 | break; | ||
362 | |||
363 | if (i != argc) { | ||
364 | cbor_decref(&map); | ||
365 | map = NULL; | ||
366 | } | ||
367 | |||
368 | return (map); | ||
369 | } | ||
370 | |||
371 | int | ||
372 | cbor_build_frame(uint8_t cmd, cbor_item_t *argv[], size_t argc, fido_blob_t *f) | ||
373 | { | ||
374 | cbor_item_t *flat = NULL; | ||
375 | unsigned char *cbor = NULL; | ||
376 | size_t cbor_len; | ||
377 | size_t cbor_alloc_len; | ||
378 | int ok = -1; | ||
379 | |||
380 | if ((flat = cbor_flatten_vector(argv, argc)) == NULL) | ||
381 | goto fail; | ||
382 | |||
383 | cbor_len = cbor_serialize_alloc(flat, &cbor, &cbor_alloc_len); | ||
384 | if (cbor_len == 0 || cbor_len == SIZE_MAX) { | ||
385 | fido_log_debug("%s: cbor_len=%zu", __func__, cbor_len); | ||
386 | goto fail; | ||
387 | } | ||
388 | |||
389 | if ((f->ptr = malloc(cbor_len + 1)) == NULL) | ||
390 | goto fail; | ||
391 | |||
392 | f->len = cbor_len + 1; | ||
393 | f->ptr[0] = cmd; | ||
394 | memcpy(f->ptr + 1, cbor, f->len - 1); | ||
395 | |||
396 | ok = 0; | ||
397 | fail: | ||
398 | if (flat != NULL) | ||
399 | cbor_decref(&flat); | ||
400 | |||
401 | free(cbor); | ||
402 | |||
403 | return (ok); | ||
404 | } | ||
405 | |||
406 | cbor_item_t * | ||
407 | cbor_encode_rp_entity(const fido_rp_t *rp) | ||
408 | { | ||
409 | cbor_item_t *item = NULL; | ||
410 | |||
411 | if ((item = cbor_new_definite_map(2)) == NULL) | ||
412 | return (NULL); | ||
413 | |||
414 | if ((rp->id && cbor_add_string(item, "id", rp->id) < 0) || | ||
415 | (rp->name && cbor_add_string(item, "name", rp->name) < 0)) { | ||
416 | cbor_decref(&item); | ||
417 | return (NULL); | ||
418 | } | ||
419 | |||
420 | return (item); | ||
421 | } | ||
422 | |||
423 | cbor_item_t * | ||
424 | cbor_encode_user_entity(const fido_user_t *user) | ||
425 | { | ||
426 | cbor_item_t *item = NULL; | ||
427 | const fido_blob_t *id = &user->id; | ||
428 | const char *display = user->display_name; | ||
429 | |||
430 | if ((item = cbor_new_definite_map(4)) == NULL) | ||
431 | return (NULL); | ||
432 | |||
433 | if ((id->ptr && cbor_add_bytestring(item, "id", id->ptr, id->len) < 0) || | ||
434 | (user->icon && cbor_add_string(item, "icon", user->icon) < 0) || | ||
435 | (user->name && cbor_add_string(item, "name", user->name) < 0) || | ||
436 | (display && cbor_add_string(item, "displayName", display) < 0)) { | ||
437 | cbor_decref(&item); | ||
438 | return (NULL); | ||
439 | } | ||
440 | |||
441 | return (item); | ||
442 | } | ||
443 | |||
444 | cbor_item_t * | ||
445 | cbor_encode_pubkey_param(int cose_alg) | ||
446 | { | ||
447 | cbor_item_t *item = NULL; | ||
448 | cbor_item_t *body = NULL; | ||
449 | struct cbor_pair alg; | ||
450 | int ok = -1; | ||
451 | |||
452 | memset(&alg, 0, sizeof(alg)); | ||
453 | |||
454 | if ((item = cbor_new_definite_array(1)) == NULL || | ||
455 | (body = cbor_new_definite_map(2)) == NULL || | ||
456 | cose_alg > -1 || cose_alg < INT16_MIN) | ||
457 | goto fail; | ||
458 | |||
459 | alg.key = cbor_build_string("alg"); | ||
460 | |||
461 | if (-cose_alg - 1 > UINT8_MAX) | ||
462 | alg.value = cbor_build_negint16((uint16_t)(-cose_alg - 1)); | ||
463 | else | ||
464 | alg.value = cbor_build_negint8((uint8_t)(-cose_alg - 1)); | ||
465 | |||
466 | if (alg.key == NULL || alg.value == NULL) { | ||
467 | fido_log_debug("%s: cbor_build", __func__); | ||
468 | goto fail; | ||
469 | } | ||
470 | |||
471 | if (cbor_map_add(body, alg) == false || | ||
472 | cbor_add_string(body, "type", "public-key") < 0 || | ||
473 | cbor_array_push(item, body) == false) | ||
474 | goto fail; | ||
475 | |||
476 | ok = 0; | ||
477 | fail: | ||
478 | if (ok < 0) { | ||
479 | if (item != NULL) { | ||
480 | cbor_decref(&item); | ||
481 | item = NULL; | ||
482 | } | ||
483 | } | ||
484 | |||
485 | if (body != NULL) | ||
486 | cbor_decref(&body); | ||
487 | if (alg.key != NULL) | ||
488 | cbor_decref(&alg.key); | ||
489 | if (alg.value != NULL) | ||
490 | cbor_decref(&alg.value); | ||
491 | |||
492 | return (item); | ||
493 | } | ||
494 | |||
495 | cbor_item_t * | ||
496 | cbor_encode_pubkey(const fido_blob_t *pubkey) | ||
497 | { | ||
498 | cbor_item_t *cbor_key = NULL; | ||
499 | |||
500 | if ((cbor_key = cbor_new_definite_map(2)) == NULL || | ||
501 | cbor_add_bytestring(cbor_key, "id", pubkey->ptr, pubkey->len) < 0 || | ||
502 | cbor_add_string(cbor_key, "type", "public-key") < 0) { | ||
503 | if (cbor_key) | ||
504 | cbor_decref(&cbor_key); | ||
505 | return (NULL); | ||
506 | } | ||
507 | |||
508 | return (cbor_key); | ||
509 | } | ||
510 | |||
511 | cbor_item_t * | ||
512 | cbor_encode_pubkey_list(const fido_blob_array_t *list) | ||
513 | { | ||
514 | cbor_item_t *array = NULL; | ||
515 | cbor_item_t *key = NULL; | ||
516 | |||
517 | if ((array = cbor_new_definite_array(list->len)) == NULL) | ||
518 | goto fail; | ||
519 | |||
520 | for (size_t i = 0; i < list->len; i++) { | ||
521 | if ((key = cbor_encode_pubkey(&list->ptr[i])) == NULL || | ||
522 | cbor_array_push(array, key) == false) | ||
523 | goto fail; | ||
524 | cbor_decref(&key); | ||
525 | } | ||
526 | |||
527 | return (array); | ||
528 | fail: | ||
529 | if (key != NULL) | ||
530 | cbor_decref(&key); | ||
531 | if (array != NULL) | ||
532 | cbor_decref(&array); | ||
533 | |||
534 | return (NULL); | ||
535 | } | ||
536 | |||
537 | cbor_item_t * | ||
538 | cbor_encode_extensions(int ext) | ||
539 | { | ||
540 | cbor_item_t *item = NULL; | ||
541 | |||
542 | if (ext == 0 || ext != FIDO_EXT_HMAC_SECRET) | ||
543 | return (NULL); | ||
544 | |||
545 | if ((item = cbor_new_definite_map(1)) == NULL) | ||
546 | return (NULL); | ||
547 | |||
548 | if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) { | ||
549 | cbor_decref(&item); | ||
550 | return (NULL); | ||
551 | } | ||
552 | |||
553 | return (item); | ||
554 | } | ||
555 | |||
556 | cbor_item_t * | ||
557 | cbor_encode_options(fido_opt_t rk, fido_opt_t uv) | ||
558 | { | ||
559 | cbor_item_t *item = NULL; | ||
560 | |||
561 | if ((item = cbor_new_definite_map(2)) == NULL) | ||
562 | return (NULL); | ||
563 | |||
564 | if ((rk != FIDO_OPT_OMIT && cbor_add_bool(item, "rk", rk) < 0) || | ||
565 | (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) { | ||
566 | cbor_decref(&item); | ||
567 | return (NULL); | ||
568 | } | ||
569 | |||
570 | return (item); | ||
571 | } | ||
572 | |||
573 | cbor_item_t * | ||
574 | cbor_encode_assert_options(fido_opt_t up, fido_opt_t uv) | ||
575 | { | ||
576 | cbor_item_t *item = NULL; | ||
577 | |||
578 | if ((item = cbor_new_definite_map(2)) == NULL) | ||
579 | return (NULL); | ||
580 | |||
581 | if ((up != FIDO_OPT_OMIT && cbor_add_bool(item, "up", up) < 0) || | ||
582 | (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) { | ||
583 | cbor_decref(&item); | ||
584 | return (NULL); | ||
585 | } | ||
586 | |||
587 | return (item); | ||
588 | } | ||
589 | |||
590 | cbor_item_t * | ||
591 | cbor_encode_pin_auth(const fido_blob_t *hmac_key, const fido_blob_t *data) | ||
592 | { | ||
593 | const EVP_MD *md = NULL; | ||
594 | unsigned char dgst[SHA256_DIGEST_LENGTH]; | ||
595 | unsigned int dgst_len; | ||
596 | |||
597 | if ((md = EVP_sha256()) == NULL || HMAC(md, hmac_key->ptr, | ||
598 | (int)hmac_key->len, data->ptr, (int)data->len, dgst, | ||
599 | &dgst_len) == NULL || dgst_len != SHA256_DIGEST_LENGTH) | ||
600 | return (NULL); | ||
601 | |||
602 | return (cbor_build_bytestring(dgst, 16)); | ||
603 | } | ||
604 | |||
605 | cbor_item_t * | ||
606 | cbor_encode_pin_opt(void) | ||
607 | { | ||
608 | return (cbor_build_uint8(1)); | ||
609 | } | ||
610 | |||
611 | cbor_item_t * | ||
612 | cbor_encode_pin_enc(const fido_blob_t *key, const fido_blob_t *pin) | ||
613 | { | ||
614 | fido_blob_t pe; | ||
615 | cbor_item_t *item = NULL; | ||
616 | |||
617 | if (aes256_cbc_enc(key, pin, &pe) < 0) | ||
618 | return (NULL); | ||
619 | |||
620 | item = cbor_build_bytestring(pe.ptr, pe.len); | ||
621 | free(pe.ptr); | ||
622 | |||
623 | return (item); | ||
624 | } | ||
625 | |||
626 | static int | ||
627 | sha256(const unsigned char *data, size_t data_len, fido_blob_t *digest) | ||
628 | { | ||
629 | if ((digest->ptr = calloc(1, SHA256_DIGEST_LENGTH)) == NULL) | ||
630 | return (-1); | ||
631 | |||
632 | digest->len = SHA256_DIGEST_LENGTH; | ||
633 | |||
634 | if (SHA256(data, data_len, digest->ptr) != digest->ptr) { | ||
635 | free(digest->ptr); | ||
636 | digest->ptr = NULL; | ||
637 | digest->len = 0; | ||
638 | return (-1); | ||
639 | } | ||
640 | |||
641 | return (0); | ||
642 | } | ||
643 | |||
644 | cbor_item_t * | ||
645 | cbor_encode_change_pin_auth(const fido_blob_t *key, const fido_blob_t *new_pin, | ||
646 | const fido_blob_t *pin) | ||
647 | { | ||
648 | unsigned char dgst[SHA256_DIGEST_LENGTH]; | ||
649 | unsigned int dgst_len; | ||
650 | cbor_item_t *item = NULL; | ||
651 | const EVP_MD *md = NULL; | ||
652 | #if OPENSSL_VERSION_NUMBER < 0x10100000L | ||
653 | HMAC_CTX ctx; | ||
654 | #else | ||
655 | HMAC_CTX *ctx = NULL; | ||
656 | #endif | ||
657 | fido_blob_t *npe = NULL; /* new pin, encrypted */ | ||
658 | fido_blob_t *ph = NULL; /* pin hash */ | ||
659 | fido_blob_t *phe = NULL; /* pin hash, encrypted */ | ||
660 | int ok = -1; | ||
661 | |||
662 | if ((npe = fido_blob_new()) == NULL || | ||
663 | (ph = fido_blob_new()) == NULL || | ||
664 | (phe = fido_blob_new()) == NULL) | ||
665 | goto fail; | ||
666 | |||
667 | if (aes256_cbc_enc(key, new_pin, npe) < 0) { | ||
668 | fido_log_debug("%s: aes256_cbc_enc 1", __func__); | ||
669 | goto fail; | ||
670 | } | ||
671 | |||
672 | if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) { | ||
673 | fido_log_debug("%s: sha256", __func__); | ||
674 | goto fail; | ||
675 | } | ||
676 | |||
677 | ph->len = 16; /* first 16 bytes */ | ||
678 | |||
679 | if (aes256_cbc_enc(key, ph, phe) < 0) { | ||
680 | fido_log_debug("%s: aes256_cbc_enc 2", __func__); | ||
681 | goto fail; | ||
682 | } | ||
683 | |||
684 | #if OPENSSL_VERSION_NUMBER < 0x10100000L | ||
685 | HMAC_CTX_init(&ctx); | ||
686 | |||
687 | if ((md = EVP_sha256()) == NULL || | ||
688 | HMAC_Init_ex(&ctx, key->ptr, (int)key->len, md, NULL) == 0 || | ||
689 | HMAC_Update(&ctx, npe->ptr, (int)npe->len) == 0 || | ||
690 | HMAC_Update(&ctx, phe->ptr, (int)phe->len) == 0 || | ||
691 | HMAC_Final(&ctx, dgst, &dgst_len) == 0 || dgst_len != 32) { | ||
692 | fido_log_debug("%s: HMAC", __func__); | ||
693 | goto fail; | ||
694 | } | ||
695 | #else | ||
696 | if ((ctx = HMAC_CTX_new()) == NULL || | ||
697 | (md = EVP_sha256()) == NULL || | ||
698 | HMAC_Init_ex(ctx, key->ptr, (int)key->len, md, NULL) == 0 || | ||
699 | HMAC_Update(ctx, npe->ptr, (int)npe->len) == 0 || | ||
700 | HMAC_Update(ctx, phe->ptr, (int)phe->len) == 0 || | ||
701 | HMAC_Final(ctx, dgst, &dgst_len) == 0 || dgst_len != 32) { | ||
702 | fido_log_debug("%s: HMAC", __func__); | ||
703 | goto fail; | ||
704 | } | ||
705 | #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ | ||
706 | |||
707 | if ((item = cbor_build_bytestring(dgst, 16)) == NULL) { | ||
708 | fido_log_debug("%s: cbor_build_bytestring", __func__); | ||
709 | goto fail; | ||
710 | } | ||
711 | |||
712 | ok = 0; | ||
713 | fail: | ||
714 | fido_blob_free(&npe); | ||
715 | fido_blob_free(&ph); | ||
716 | fido_blob_free(&phe); | ||
717 | |||
718 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L | ||
719 | if (ctx != NULL) | ||
720 | HMAC_CTX_free(ctx); | ||
721 | #endif | ||
722 | |||
723 | if (ok < 0) { | ||
724 | if (item != NULL) { | ||
725 | cbor_decref(&item); | ||
726 | item = NULL; | ||
727 | } | ||
728 | } | ||
729 | |||
730 | return (item); | ||
731 | } | ||
732 | |||
733 | cbor_item_t * | ||
734 | cbor_encode_set_pin_auth(const fido_blob_t *key, const fido_blob_t *pin) | ||
735 | { | ||
736 | const EVP_MD *md = NULL; | ||
737 | unsigned char dgst[SHA256_DIGEST_LENGTH]; | ||
738 | unsigned int dgst_len; | ||
739 | cbor_item_t *item = NULL; | ||
740 | fido_blob_t *pe = NULL; | ||
741 | |||
742 | if ((pe = fido_blob_new()) == NULL) | ||
743 | goto fail; | ||
744 | |||
745 | if (aes256_cbc_enc(key, pin, pe) < 0) { | ||
746 | fido_log_debug("%s: aes256_cbc_enc", __func__); | ||
747 | goto fail; | ||
748 | } | ||
749 | |||
750 | if ((md = EVP_sha256()) == NULL || key->len != 32 || HMAC(md, key->ptr, | ||
751 | (int)key->len, pe->ptr, (int)pe->len, dgst, &dgst_len) == NULL || | ||
752 | dgst_len != SHA256_DIGEST_LENGTH) { | ||
753 | fido_log_debug("%s: HMAC", __func__); | ||
754 | goto fail; | ||
755 | } | ||
756 | |||
757 | item = cbor_build_bytestring(dgst, 16); | ||
758 | fail: | ||
759 | fido_blob_free(&pe); | ||
760 | |||
761 | return (item); | ||
762 | } | ||
763 | |||
764 | cbor_item_t * | ||
765 | cbor_encode_pin_hash_enc(const fido_blob_t *shared, const fido_blob_t *pin) | ||
766 | { | ||
767 | cbor_item_t *item = NULL; | ||
768 | fido_blob_t *ph = NULL; | ||
769 | fido_blob_t *phe = NULL; | ||
770 | |||
771 | if ((ph = fido_blob_new()) == NULL || (phe = fido_blob_new()) == NULL) | ||
772 | goto fail; | ||
773 | |||
774 | if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) { | ||
775 | fido_log_debug("%s: SHA256", __func__); | ||
776 | goto fail; | ||
777 | } | ||
778 | |||
779 | ph->len = 16; /* first 16 bytes */ | ||
780 | |||
781 | if (aes256_cbc_enc(shared, ph, phe) < 0) { | ||
782 | fido_log_debug("%s: aes256_cbc_enc", __func__); | ||
783 | goto fail; | ||
784 | } | ||
785 | |||
786 | item = cbor_build_bytestring(phe->ptr, phe->len); | ||
787 | fail: | ||
788 | fido_blob_free(&ph); | ||
789 | fido_blob_free(&phe); | ||
790 | |||
791 | return (item); | ||
792 | } | ||
793 | |||
794 | cbor_item_t * | ||
795 | cbor_encode_hmac_secret_param(const fido_blob_t *ecdh, const es256_pk_t *pk, | ||
796 | const fido_blob_t *hmac_salt) | ||
797 | { | ||
798 | cbor_item_t *item = NULL; | ||
799 | cbor_item_t *param = NULL; | ||
800 | cbor_item_t *argv[3]; | ||
801 | struct cbor_pair pair; | ||
802 | |||
803 | memset(argv, 0, sizeof(argv)); | ||
804 | memset(&pair, 0, sizeof(pair)); | ||
805 | |||
806 | if (ecdh == NULL || pk == NULL || hmac_salt->ptr == NULL) { | ||
807 | fido_log_debug("%s: ecdh=%p, pk=%p, hmac_salt->ptr=%p", | ||
808 | __func__, (const void *)ecdh, (const void *)pk, | ||
809 | (const void *)hmac_salt->ptr); | ||
810 | goto fail; | ||
811 | } | ||
812 | |||
813 | if (hmac_salt->len != 32 && hmac_salt->len != 64) { | ||
814 | fido_log_debug("%s: hmac_salt->len=%zu", __func__, | ||
815 | hmac_salt->len); | ||
816 | goto fail; | ||
817 | } | ||
818 | |||
819 | /* XXX not pin, but salt */ | ||
820 | if ((argv[0] = es256_pk_encode(pk, 1)) == NULL || | ||
821 | (argv[1] = cbor_encode_pin_enc(ecdh, hmac_salt)) == NULL || | ||
822 | (argv[2] = cbor_encode_set_pin_auth(ecdh, hmac_salt)) == NULL) { | ||
823 | fido_log_debug("%s: cbor encode", __func__); | ||
824 | goto fail; | ||
825 | } | ||
826 | |||
827 | if ((param = cbor_flatten_vector(argv, 3)) == NULL) { | ||
828 | fido_log_debug("%s: cbor_flatten_vector", __func__); | ||
829 | goto fail; | ||
830 | } | ||
831 | |||
832 | if ((item = cbor_new_definite_map(1)) == NULL) { | ||
833 | fido_log_debug("%s: cbor_new_definite_map", __func__); | ||
834 | goto fail; | ||
835 | } | ||
836 | |||
837 | if ((pair.key = cbor_build_string("hmac-secret")) == NULL) { | ||
838 | fido_log_debug("%s: cbor_build", __func__); | ||
839 | goto fail; | ||
840 | } | ||
841 | |||
842 | pair.value = param; | ||
843 | |||
844 | if (!cbor_map_add(item, pair)) { | ||
845 | fido_log_debug("%s: cbor_map_add", __func__); | ||
846 | cbor_decref(&item); | ||
847 | item = NULL; | ||
848 | goto fail; | ||
849 | } | ||
850 | |||
851 | fail: | ||
852 | for (size_t i = 0; i < 3; i++) | ||
853 | if (argv[i] != NULL) | ||
854 | cbor_decref(&argv[i]); | ||
855 | |||
856 | if (param != NULL) | ||
857 | cbor_decref(¶m); | ||
858 | if (pair.key != NULL) | ||
859 | cbor_decref(&pair.key); | ||
860 | |||
861 | return (item); | ||
862 | } | ||
863 | |||
864 | int | ||
865 | cbor_decode_fmt(const cbor_item_t *item, char **fmt) | ||
866 | { | ||
867 | char *type = NULL; | ||
868 | |||
869 | if (cbor_string_copy(item, &type) < 0) { | ||
870 | fido_log_debug("%s: cbor_string_copy", __func__); | ||
871 | return (-1); | ||
872 | } | ||
873 | |||
874 | if (strcmp(type, "packed") && strcmp(type, "fido-u2f")) { | ||
875 | fido_log_debug("%s: type=%s", __func__, type); | ||
876 | free(type); | ||
877 | return (-1); | ||
878 | } | ||
879 | |||
880 | *fmt = type; | ||
881 | |||
882 | return (0); | ||
883 | } | ||
884 | |||
885 | struct cose_key { | ||
886 | int kty; | ||
887 | int alg; | ||
888 | int crv; | ||
889 | }; | ||
890 | |||
891 | static int | ||
892 | find_cose_alg(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
893 | { | ||
894 | struct cose_key *cose_key = arg; | ||
895 | |||
896 | if (cbor_isa_uint(key) == true && | ||
897 | cbor_int_get_width(key) == CBOR_INT_8) { | ||
898 | switch (cbor_get_uint8(key)) { | ||
899 | case 1: | ||
900 | if (cbor_isa_uint(val) == false || | ||
901 | cbor_get_int(val) > INT_MAX || cose_key->kty != 0) { | ||
902 | fido_log_debug("%s: kty", __func__); | ||
903 | return (-1); | ||
904 | } | ||
905 | |||
906 | cose_key->kty = (int)cbor_get_int(val); | ||
907 | |||
908 | break; | ||
909 | case 3: | ||
910 | if (cbor_isa_negint(val) == false || | ||
911 | cbor_get_int(val) > INT_MAX || cose_key->alg != 0) { | ||
912 | fido_log_debug("%s: alg", __func__); | ||
913 | return (-1); | ||
914 | } | ||
915 | |||
916 | cose_key->alg = -(int)cbor_get_int(val) - 1; | ||
917 | |||
918 | break; | ||
919 | } | ||
920 | } else if (cbor_isa_negint(key) == true && | ||
921 | cbor_int_get_width(key) == CBOR_INT_8) { | ||
922 | if (cbor_get_uint8(key) == 0) { | ||
923 | /* get crv if not rsa, otherwise ignore */ | ||
924 | if (cbor_isa_uint(val) == true && | ||
925 | cbor_get_int(val) <= INT_MAX && | ||
926 | cose_key->crv == 0) | ||
927 | cose_key->crv = (int)cbor_get_int(val); | ||
928 | } | ||
929 | } | ||
930 | |||
931 | return (0); | ||
932 | } | ||
933 | |||
934 | static int | ||
935 | get_cose_alg(const cbor_item_t *item, int *cose_alg) | ||
936 | { | ||
937 | struct cose_key cose_key; | ||
938 | |||
939 | memset(&cose_key, 0, sizeof(cose_key)); | ||
940 | |||
941 | *cose_alg = 0; | ||
942 | |||
943 | if (cbor_isa_map(item) == false || | ||
944 | cbor_map_is_definite(item) == false || | ||
945 | cbor_map_iter(item, &cose_key, find_cose_alg) < 0) { | ||
946 | fido_log_debug("%s: cbor type", __func__); | ||
947 | return (-1); | ||
948 | } | ||
949 | |||
950 | switch (cose_key.alg) { | ||
951 | case COSE_ES256: | ||
952 | if (cose_key.kty != COSE_KTY_EC2 || | ||
953 | cose_key.crv != COSE_P256) { | ||
954 | fido_log_debug("%s: invalid kty/crv", __func__); | ||
955 | return (-1); | ||
956 | } | ||
957 | |||
958 | break; | ||
959 | case COSE_EDDSA: | ||
960 | if (cose_key.kty != COSE_KTY_OKP || | ||
961 | cose_key.crv != COSE_ED25519) { | ||
962 | fido_log_debug("%s: invalid kty/crv", __func__); | ||
963 | return (-1); | ||
964 | } | ||
965 | |||
966 | break; | ||
967 | case COSE_RS256: | ||
968 | if (cose_key.kty != COSE_KTY_RSA) { | ||
969 | fido_log_debug("%s: invalid kty/crv", __func__); | ||
970 | return (-1); | ||
971 | } | ||
972 | |||
973 | break; | ||
974 | default: | ||
975 | fido_log_debug("%s: unknown alg %d", __func__, cose_key.alg); | ||
976 | |||
977 | return (-1); | ||
978 | } | ||
979 | |||
980 | *cose_alg = cose_key.alg; | ||
981 | |||
982 | return (0); | ||
983 | } | ||
984 | |||
985 | int | ||
986 | cbor_decode_pubkey(const cbor_item_t *item, int *type, void *key) | ||
987 | { | ||
988 | if (get_cose_alg(item, type) < 0) { | ||
989 | fido_log_debug("%s: get_cose_alg", __func__); | ||
990 | return (-1); | ||
991 | } | ||
992 | |||
993 | switch (*type) { | ||
994 | case COSE_ES256: | ||
995 | if (es256_pk_decode(item, key) < 0) { | ||
996 | fido_log_debug("%s: es256_pk_decode", __func__); | ||
997 | return (-1); | ||
998 | } | ||
999 | break; | ||
1000 | case COSE_RS256: | ||
1001 | if (rs256_pk_decode(item, key) < 0) { | ||
1002 | fido_log_debug("%s: rs256_pk_decode", __func__); | ||
1003 | return (-1); | ||
1004 | } | ||
1005 | break; | ||
1006 | case COSE_EDDSA: | ||
1007 | if (eddsa_pk_decode(item, key) < 0) { | ||
1008 | fido_log_debug("%s: eddsa_pk_decode", __func__); | ||
1009 | return (-1); | ||
1010 | } | ||
1011 | break; | ||
1012 | default: | ||
1013 | fido_log_debug("%s: invalid cose_alg %d", __func__, *type); | ||
1014 | return (-1); | ||
1015 | } | ||
1016 | |||
1017 | return (0); | ||
1018 | } | ||
1019 | |||
1020 | static int | ||
1021 | decode_attcred(const unsigned char **buf, size_t *len, int cose_alg, | ||
1022 | fido_attcred_t *attcred) | ||
1023 | { | ||
1024 | cbor_item_t *item = NULL; | ||
1025 | struct cbor_load_result cbor; | ||
1026 | uint16_t id_len; | ||
1027 | int ok = -1; | ||
1028 | |||
1029 | fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf, | ||
1030 | *len); | ||
1031 | |||
1032 | if (fido_buf_read(buf, len, &attcred->aaguid, | ||
1033 | sizeof(attcred->aaguid)) < 0) { | ||
1034 | fido_log_debug("%s: fido_buf_read aaguid", __func__); | ||
1035 | return (-1); | ||
1036 | } | ||
1037 | |||
1038 | if (fido_buf_read(buf, len, &id_len, sizeof(id_len)) < 0) { | ||
1039 | fido_log_debug("%s: fido_buf_read id_len", __func__); | ||
1040 | return (-1); | ||
1041 | } | ||
1042 | |||
1043 | attcred->id.len = (size_t)be16toh(id_len); | ||
1044 | if ((attcred->id.ptr = malloc(attcred->id.len)) == NULL) | ||
1045 | return (-1); | ||
1046 | |||
1047 | fido_log_debug("%s: attcred->id.len=%zu", __func__, attcred->id.len); | ||
1048 | |||
1049 | if (fido_buf_read(buf, len, attcred->id.ptr, attcred->id.len) < 0) { | ||
1050 | fido_log_debug("%s: fido_buf_read id", __func__); | ||
1051 | return (-1); | ||
1052 | } | ||
1053 | |||
1054 | if ((item = cbor_load(*buf, *len, &cbor)) == NULL) { | ||
1055 | fido_log_debug("%s: cbor_load", __func__); | ||
1056 | fido_log_xxd(*buf, *len); | ||
1057 | goto fail; | ||
1058 | } | ||
1059 | |||
1060 | if (cbor_decode_pubkey(item, &attcred->type, &attcred->pubkey) < 0) { | ||
1061 | fido_log_debug("%s: cbor_decode_pubkey", __func__); | ||
1062 | goto fail; | ||
1063 | } | ||
1064 | |||
1065 | if (attcred->type != cose_alg) { | ||
1066 | fido_log_debug("%s: cose_alg mismatch (%d != %d)", __func__, | ||
1067 | attcred->type, cose_alg); | ||
1068 | goto fail; | ||
1069 | } | ||
1070 | |||
1071 | *buf += cbor.read; | ||
1072 | *len -= cbor.read; | ||
1073 | |||
1074 | ok = 0; | ||
1075 | fail: | ||
1076 | if (item != NULL) | ||
1077 | cbor_decref(&item); | ||
1078 | |||
1079 | return (ok); | ||
1080 | } | ||
1081 | |||
1082 | static int | ||
1083 | decode_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
1084 | { | ||
1085 | int *authdata_ext = arg; | ||
1086 | char *type = NULL; | ||
1087 | int ok = -1; | ||
1088 | |||
1089 | if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) { | ||
1090 | fido_log_debug("%s: cbor type", __func__); | ||
1091 | ok = 0; /* ignore */ | ||
1092 | goto out; | ||
1093 | } | ||
1094 | |||
1095 | if (cbor_isa_float_ctrl(val) == false || | ||
1096 | cbor_float_get_width(val) != CBOR_FLOAT_0 || | ||
1097 | cbor_is_bool(val) == false || *authdata_ext != 0) { | ||
1098 | fido_log_debug("%s: cbor type", __func__); | ||
1099 | goto out; | ||
1100 | } | ||
1101 | |||
1102 | if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE) | ||
1103 | *authdata_ext |= FIDO_EXT_HMAC_SECRET; | ||
1104 | |||
1105 | ok = 0; | ||
1106 | out: | ||
1107 | free(type); | ||
1108 | |||
1109 | return (ok); | ||
1110 | } | ||
1111 | |||
1112 | static int | ||
1113 | decode_extensions(const unsigned char **buf, size_t *len, int *authdata_ext) | ||
1114 | { | ||
1115 | cbor_item_t *item = NULL; | ||
1116 | struct cbor_load_result cbor; | ||
1117 | int ok = -1; | ||
1118 | |||
1119 | fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf, | ||
1120 | *len); | ||
1121 | |||
1122 | *authdata_ext = 0; | ||
1123 | |||
1124 | if ((item = cbor_load(*buf, *len, &cbor)) == NULL) { | ||
1125 | fido_log_debug("%s: cbor_load", __func__); | ||
1126 | fido_log_xxd(*buf, *len); | ||
1127 | goto fail; | ||
1128 | } | ||
1129 | |||
1130 | if (cbor_isa_map(item) == false || | ||
1131 | cbor_map_is_definite(item) == false || | ||
1132 | cbor_map_size(item) != 1 || | ||
1133 | cbor_map_iter(item, authdata_ext, decode_extension) < 0) { | ||
1134 | fido_log_debug("%s: cbor type", __func__); | ||
1135 | goto fail; | ||
1136 | } | ||
1137 | |||
1138 | *buf += cbor.read; | ||
1139 | *len -= cbor.read; | ||
1140 | |||
1141 | ok = 0; | ||
1142 | fail: | ||
1143 | if (item != NULL) | ||
1144 | cbor_decref(&item); | ||
1145 | |||
1146 | return (ok); | ||
1147 | } | ||
1148 | |||
1149 | static int | ||
1150 | decode_hmac_secret_aux(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
1151 | { | ||
1152 | fido_blob_t *out = arg; | ||
1153 | char *type = NULL; | ||
1154 | int ok = -1; | ||
1155 | |||
1156 | if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) { | ||
1157 | fido_log_debug("%s: cbor type", __func__); | ||
1158 | ok = 0; /* ignore */ | ||
1159 | goto out; | ||
1160 | } | ||
1161 | |||
1162 | ok = cbor_bytestring_copy(val, &out->ptr, &out->len); | ||
1163 | out: | ||
1164 | free(type); | ||
1165 | |||
1166 | return (ok); | ||
1167 | } | ||
1168 | |||
1169 | static int | ||
1170 | decode_hmac_secret(const unsigned char **buf, size_t *len, fido_blob_t *out) | ||
1171 | { | ||
1172 | cbor_item_t *item = NULL; | ||
1173 | struct cbor_load_result cbor; | ||
1174 | int ok = -1; | ||
1175 | |||
1176 | fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf, | ||
1177 | *len); | ||
1178 | |||
1179 | if ((item = cbor_load(*buf, *len, &cbor)) == NULL) { | ||
1180 | fido_log_debug("%s: cbor_load", __func__); | ||
1181 | fido_log_xxd(*buf, *len); | ||
1182 | goto fail; | ||
1183 | } | ||
1184 | |||
1185 | if (cbor_isa_map(item) == false || | ||
1186 | cbor_map_is_definite(item) == false || | ||
1187 | cbor_map_size(item) != 1 || | ||
1188 | cbor_map_iter(item, out, decode_hmac_secret_aux) < 0) { | ||
1189 | fido_log_debug("%s: cbor type", __func__); | ||
1190 | goto fail; | ||
1191 | } | ||
1192 | |||
1193 | *buf += cbor.read; | ||
1194 | *len -= cbor.read; | ||
1195 | |||
1196 | ok = 0; | ||
1197 | fail: | ||
1198 | if (item != NULL) | ||
1199 | cbor_decref(&item); | ||
1200 | |||
1201 | return (ok); | ||
1202 | } | ||
1203 | |||
1204 | int | ||
1205 | cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg, | ||
1206 | fido_blob_t *authdata_cbor, fido_authdata_t *authdata, | ||
1207 | fido_attcred_t *attcred, int *authdata_ext) | ||
1208 | { | ||
1209 | const unsigned char *buf = NULL; | ||
1210 | size_t len; | ||
1211 | size_t alloc_len; | ||
1212 | |||
1213 | if (cbor_isa_bytestring(item) == false || | ||
1214 | cbor_bytestring_is_definite(item) == false) { | ||
1215 | fido_log_debug("%s: cbor type", __func__); | ||
1216 | return (-1); | ||
1217 | } | ||
1218 | |||
1219 | if (authdata_cbor->ptr != NULL || | ||
1220 | (authdata_cbor->len = cbor_serialize_alloc(item, | ||
1221 | &authdata_cbor->ptr, &alloc_len)) == 0) { | ||
1222 | fido_log_debug("%s: cbor_serialize_alloc", __func__); | ||
1223 | return (-1); | ||
1224 | } | ||
1225 | |||
1226 | buf = cbor_bytestring_handle(item); | ||
1227 | len = cbor_bytestring_length(item); | ||
1228 | |||
1229 | fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len); | ||
1230 | |||
1231 | if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) { | ||
1232 | fido_log_debug("%s: fido_buf_read", __func__); | ||
1233 | return (-1); | ||
1234 | } | ||
1235 | |||
1236 | authdata->sigcount = be32toh(authdata->sigcount); | ||
1237 | |||
1238 | if (attcred != NULL) { | ||
1239 | if ((authdata->flags & CTAP_AUTHDATA_ATT_CRED) == 0 || | ||
1240 | decode_attcred(&buf, &len, cose_alg, attcred) < 0) | ||
1241 | return (-1); | ||
1242 | } | ||
1243 | |||
1244 | if (authdata_ext != NULL) { | ||
1245 | if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 && | ||
1246 | decode_extensions(&buf, &len, authdata_ext) < 0) | ||
1247 | return (-1); | ||
1248 | } | ||
1249 | |||
1250 | /* XXX we should probably ensure that len == 0 at this point */ | ||
1251 | |||
1252 | return (FIDO_OK); | ||
1253 | } | ||
1254 | |||
1255 | int | ||
1256 | cbor_decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor, | ||
1257 | fido_authdata_t *authdata, int *authdata_ext, fido_blob_t *hmac_secret_enc) | ||
1258 | { | ||
1259 | const unsigned char *buf = NULL; | ||
1260 | size_t len; | ||
1261 | size_t alloc_len; | ||
1262 | |||
1263 | if (cbor_isa_bytestring(item) == false || | ||
1264 | cbor_bytestring_is_definite(item) == false) { | ||
1265 | fido_log_debug("%s: cbor type", __func__); | ||
1266 | return (-1); | ||
1267 | } | ||
1268 | |||
1269 | if (authdata_cbor->ptr != NULL || | ||
1270 | (authdata_cbor->len = cbor_serialize_alloc(item, | ||
1271 | &authdata_cbor->ptr, &alloc_len)) == 0) { | ||
1272 | fido_log_debug("%s: cbor_serialize_alloc", __func__); | ||
1273 | return (-1); | ||
1274 | } | ||
1275 | |||
1276 | buf = cbor_bytestring_handle(item); | ||
1277 | len = cbor_bytestring_length(item); | ||
1278 | |||
1279 | fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len); | ||
1280 | |||
1281 | if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) { | ||
1282 | fido_log_debug("%s: fido_buf_read", __func__); | ||
1283 | return (-1); | ||
1284 | } | ||
1285 | |||
1286 | authdata->sigcount = be32toh(authdata->sigcount); | ||
1287 | |||
1288 | *authdata_ext = 0; | ||
1289 | if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0) { | ||
1290 | /* XXX semantic leap: extensions -> hmac_secret */ | ||
1291 | if (decode_hmac_secret(&buf, &len, hmac_secret_enc) < 0) { | ||
1292 | fido_log_debug("%s: decode_hmac_secret", __func__); | ||
1293 | return (-1); | ||
1294 | } | ||
1295 | *authdata_ext = FIDO_EXT_HMAC_SECRET; | ||
1296 | } | ||
1297 | |||
1298 | /* XXX we should probably ensure that len == 0 at this point */ | ||
1299 | |||
1300 | return (FIDO_OK); | ||
1301 | } | ||
1302 | |||
1303 | static int | ||
1304 | decode_x5c(const cbor_item_t *item, void *arg) | ||
1305 | { | ||
1306 | fido_blob_t *x5c = arg; | ||
1307 | |||
1308 | if (x5c->len) | ||
1309 | return (0); /* ignore */ | ||
1310 | |||
1311 | return (cbor_bytestring_copy(item, &x5c->ptr, &x5c->len)); | ||
1312 | } | ||
1313 | |||
1314 | static int | ||
1315 | decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
1316 | { | ||
1317 | fido_attstmt_t *attstmt = arg; | ||
1318 | char *name = NULL; | ||
1319 | int ok = -1; | ||
1320 | |||
1321 | if (cbor_string_copy(key, &name) < 0) { | ||
1322 | fido_log_debug("%s: cbor type", __func__); | ||
1323 | ok = 0; /* ignore */ | ||
1324 | goto out; | ||
1325 | } | ||
1326 | |||
1327 | if (!strcmp(name, "alg")) { | ||
1328 | if (cbor_isa_negint(val) == false || | ||
1329 | cbor_int_get_width(val) != CBOR_INT_8 || | ||
1330 | cbor_get_uint8(val) != -COSE_ES256 - 1) { | ||
1331 | fido_log_debug("%s: alg", __func__); | ||
1332 | goto out; | ||
1333 | } | ||
1334 | } else if (!strcmp(name, "sig")) { | ||
1335 | if (cbor_bytestring_copy(val, &attstmt->sig.ptr, | ||
1336 | &attstmt->sig.len) < 0) { | ||
1337 | fido_log_debug("%s: sig", __func__); | ||
1338 | goto out; | ||
1339 | } | ||
1340 | } else if (!strcmp(name, "x5c")) { | ||
1341 | if (cbor_isa_array(val) == false || | ||
1342 | cbor_array_is_definite(val) == false || | ||
1343 | cbor_array_iter(val, &attstmt->x5c, decode_x5c) < 0) { | ||
1344 | fido_log_debug("%s: x5c", __func__); | ||
1345 | goto out; | ||
1346 | } | ||
1347 | } | ||
1348 | |||
1349 | ok = 0; | ||
1350 | out: | ||
1351 | free(name); | ||
1352 | |||
1353 | return (ok); | ||
1354 | } | ||
1355 | |||
1356 | int | ||
1357 | cbor_decode_attstmt(const cbor_item_t *item, fido_attstmt_t *attstmt) | ||
1358 | { | ||
1359 | if (cbor_isa_map(item) == false || | ||
1360 | cbor_map_is_definite(item) == false || | ||
1361 | cbor_map_iter(item, attstmt, decode_attstmt_entry) < 0) { | ||
1362 | fido_log_debug("%s: cbor type", __func__); | ||
1363 | return (-1); | ||
1364 | } | ||
1365 | |||
1366 | return (0); | ||
1367 | } | ||
1368 | |||
1369 | int | ||
1370 | cbor_decode_uint64(const cbor_item_t *item, uint64_t *n) | ||
1371 | { | ||
1372 | if (cbor_isa_uint(item) == false) { | ||
1373 | fido_log_debug("%s: cbor type", __func__); | ||
1374 | return (-1); | ||
1375 | } | ||
1376 | |||
1377 | *n = cbor_get_int(item); | ||
1378 | |||
1379 | return (0); | ||
1380 | } | ||
1381 | |||
1382 | static int | ||
1383 | decode_cred_id_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
1384 | { | ||
1385 | fido_blob_t *id = arg; | ||
1386 | char *name = NULL; | ||
1387 | int ok = -1; | ||
1388 | |||
1389 | if (cbor_string_copy(key, &name) < 0) { | ||
1390 | fido_log_debug("%s: cbor type", __func__); | ||
1391 | ok = 0; /* ignore */ | ||
1392 | goto out; | ||
1393 | } | ||
1394 | |||
1395 | if (!strcmp(name, "id")) | ||
1396 | if (cbor_bytestring_copy(val, &id->ptr, &id->len) < 0) { | ||
1397 | fido_log_debug("%s: cbor_bytestring_copy", __func__); | ||
1398 | goto out; | ||
1399 | } | ||
1400 | |||
1401 | ok = 0; | ||
1402 | out: | ||
1403 | free(name); | ||
1404 | |||
1405 | return (ok); | ||
1406 | } | ||
1407 | |||
1408 | int | ||
1409 | cbor_decode_cred_id(const cbor_item_t *item, fido_blob_t *id) | ||
1410 | { | ||
1411 | if (cbor_isa_map(item) == false || | ||
1412 | cbor_map_is_definite(item) == false || | ||
1413 | cbor_map_iter(item, id, decode_cred_id_entry) < 0) { | ||
1414 | fido_log_debug("%s: cbor type", __func__); | ||
1415 | return (-1); | ||
1416 | } | ||
1417 | |||
1418 | return (0); | ||
1419 | } | ||
1420 | |||
1421 | static int | ||
1422 | decode_user_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
1423 | { | ||
1424 | fido_user_t *user = arg; | ||
1425 | char *name = NULL; | ||
1426 | int ok = -1; | ||
1427 | |||
1428 | if (cbor_string_copy(key, &name) < 0) { | ||
1429 | fido_log_debug("%s: cbor type", __func__); | ||
1430 | ok = 0; /* ignore */ | ||
1431 | goto out; | ||
1432 | } | ||
1433 | |||
1434 | if (!strcmp(name, "icon")) { | ||
1435 | if (cbor_string_copy(val, &user->icon) < 0) { | ||
1436 | fido_log_debug("%s: icon", __func__); | ||
1437 | goto out; | ||
1438 | } | ||
1439 | } else if (!strcmp(name, "name")) { | ||
1440 | if (cbor_string_copy(val, &user->name) < 0) { | ||
1441 | fido_log_debug("%s: name", __func__); | ||
1442 | goto out; | ||
1443 | } | ||
1444 | } else if (!strcmp(name, "displayName")) { | ||
1445 | if (cbor_string_copy(val, &user->display_name) < 0) { | ||
1446 | fido_log_debug("%s: display_name", __func__); | ||
1447 | goto out; | ||
1448 | } | ||
1449 | } else if (!strcmp(name, "id")) { | ||
1450 | if (cbor_bytestring_copy(val, &user->id.ptr, &user->id.len) < 0) { | ||
1451 | fido_log_debug("%s: id", __func__); | ||
1452 | goto out; | ||
1453 | } | ||
1454 | } | ||
1455 | |||
1456 | ok = 0; | ||
1457 | out: | ||
1458 | free(name); | ||
1459 | |||
1460 | return (ok); | ||
1461 | } | ||
1462 | |||
1463 | int | ||
1464 | cbor_decode_user(const cbor_item_t *item, fido_user_t *user) | ||
1465 | { | ||
1466 | if (cbor_isa_map(item) == false || | ||
1467 | cbor_map_is_definite(item) == false || | ||
1468 | cbor_map_iter(item, user, decode_user_entry) < 0) { | ||
1469 | fido_log_debug("%s: cbor type", __func__); | ||
1470 | return (-1); | ||
1471 | } | ||
1472 | |||
1473 | return (0); | ||
1474 | } | ||
1475 | |||
1476 | static int | ||
1477 | decode_rp_entity_entry(const cbor_item_t *key, const cbor_item_t *val, | ||
1478 | void *arg) | ||
1479 | { | ||
1480 | fido_rp_t *rp = arg; | ||
1481 | char *name = NULL; | ||
1482 | int ok = -1; | ||
1483 | |||
1484 | if (cbor_string_copy(key, &name) < 0) { | ||
1485 | fido_log_debug("%s: cbor type", __func__); | ||
1486 | ok = 0; /* ignore */ | ||
1487 | goto out; | ||
1488 | } | ||
1489 | |||
1490 | if (!strcmp(name, "id")) { | ||
1491 | if (cbor_string_copy(val, &rp->id) < 0) { | ||
1492 | fido_log_debug("%s: id", __func__); | ||
1493 | goto out; | ||
1494 | } | ||
1495 | } else if (!strcmp(name, "name")) { | ||
1496 | if (cbor_string_copy(val, &rp->name) < 0) { | ||
1497 | fido_log_debug("%s: name", __func__); | ||
1498 | goto out; | ||
1499 | } | ||
1500 | } | ||
1501 | |||
1502 | ok = 0; | ||
1503 | out: | ||
1504 | free(name); | ||
1505 | |||
1506 | return (ok); | ||
1507 | } | ||
1508 | |||
1509 | int | ||
1510 | cbor_decode_rp_entity(const cbor_item_t *item, fido_rp_t *rp) | ||
1511 | { | ||
1512 | if (cbor_isa_map(item) == false || | ||
1513 | cbor_map_is_definite(item) == false || | ||
1514 | cbor_map_iter(item, rp, decode_rp_entity_entry) < 0) { | ||
1515 | fido_log_debug("%s: cbor type", __func__); | ||
1516 | return (-1); | ||
1517 | } | ||
1518 | |||
1519 | return (0); | ||
1520 | } | ||
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 | |||
16 | static int | ||
17 | parse_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 | |||
42 | static int | ||
43 | fido_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; | ||
117 | fail: | ||
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 | |||
126 | static int | ||
127 | fido_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 | |||
157 | static int | ||
158 | fido_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 | |||
169 | int | ||
170 | fido_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 | |||
181 | static int | ||
182 | check_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 | |||
193 | int | ||
194 | fido_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 | |||
210 | static int | ||
211 | get_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; | ||
245 | fail: | ||
246 | if (item != NULL) | ||
247 | cbor_decref(&item); | ||
248 | |||
249 | return (ok); | ||
250 | } | ||
251 | |||
252 | static int | ||
253 | get_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 | |||
277 | static int | ||
278 | verify_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; | ||
310 | fail: | ||
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 | |||
321 | int | ||
322 | fido_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; | ||
389 | out: | ||
390 | explicit_bzero(buf, sizeof(buf)); | ||
391 | |||
392 | return (r); | ||
393 | } | ||
394 | |||
395 | int | ||
396 | fido_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 | |||
482 | out: | ||
483 | explicit_bzero(buf, sizeof(buf)); | ||
484 | |||
485 | return (r); | ||
486 | } | ||
487 | |||
488 | fido_cred_t * | ||
489 | fido_cred_new(void) | ||
490 | { | ||
491 | return (calloc(1, sizeof(fido_cred_t))); | ||
492 | } | ||
493 | |||
494 | static void | ||
495 | fido_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 | |||
506 | void | ||
507 | fido_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 | |||
529 | static void | ||
530 | fido_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 | |||
537 | static void | ||
538 | fido_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 | |||
545 | void | ||
546 | fido_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 | |||
556 | void | ||
557 | fido_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 | |||
572 | int | ||
573 | fido_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; | ||
600 | fail: | ||
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 | |||
611 | int | ||
612 | fido_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; | ||
639 | fail: | ||
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 | |||
650 | int | ||
651 | fido_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 | |||
669 | int | ||
670 | fido_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 | |||
688 | int | ||
689 | fido_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 | |||
716 | int | ||
717 | fido_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 | |||
726 | int | ||
727 | fido_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); | ||
746 | fail: | ||
747 | free(rp->id); | ||
748 | free(rp->name); | ||
749 | rp->id = NULL; | ||
750 | rp->name = NULL; | ||
751 | |||
752 | return (FIDO_ERR_INTERNAL); | ||
753 | } | ||
754 | |||
755 | int | ||
756 | fido_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); | ||
795 | fail: | ||
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 | |||
810 | int | ||
811 | fido_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 | |||
821 | int | ||
822 | fido_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 | |||
830 | int | ||
831 | fido_cred_set_rk(fido_cred_t *cred, fido_opt_t rk) | ||
832 | { | ||
833 | cred->rk = rk; | ||
834 | |||
835 | return (FIDO_OK); | ||
836 | } | ||
837 | |||
838 | int | ||
839 | fido_cred_set_uv(fido_cred_t *cred, fido_opt_t uv) | ||
840 | { | ||
841 | cred->uv = uv; | ||
842 | |||
843 | return (FIDO_OK); | ||
844 | } | ||
845 | |||
846 | int | ||
847 | fido_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 | |||
864 | int | ||
865 | fido_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 | |||
876 | int | ||
877 | fido_cred_type(const fido_cred_t *cred) | ||
878 | { | ||
879 | return (cred->type); | ||
880 | } | ||
881 | |||
882 | uint8_t | ||
883 | fido_cred_flags(const fido_cred_t *cred) | ||
884 | { | ||
885 | return (cred->authdata.flags); | ||
886 | } | ||
887 | |||
888 | const unsigned char * | ||
889 | fido_cred_clientdata_hash_ptr(const fido_cred_t *cred) | ||
890 | { | ||
891 | return (cred->cdh.ptr); | ||
892 | } | ||
893 | |||
894 | size_t | ||
895 | fido_cred_clientdata_hash_len(const fido_cred_t *cred) | ||
896 | { | ||
897 | return (cred->cdh.len); | ||
898 | } | ||
899 | |||
900 | const unsigned char * | ||
901 | fido_cred_x5c_ptr(const fido_cred_t *cred) | ||
902 | { | ||
903 | return (cred->attstmt.x5c.ptr); | ||
904 | } | ||
905 | |||
906 | size_t | ||
907 | fido_cred_x5c_len(const fido_cred_t *cred) | ||
908 | { | ||
909 | return (cred->attstmt.x5c.len); | ||
910 | } | ||
911 | |||
912 | const unsigned char * | ||
913 | fido_cred_sig_ptr(const fido_cred_t *cred) | ||
914 | { | ||
915 | return (cred->attstmt.sig.ptr); | ||
916 | } | ||
917 | |||
918 | size_t | ||
919 | fido_cred_sig_len(const fido_cred_t *cred) | ||
920 | { | ||
921 | return (cred->attstmt.sig.len); | ||
922 | } | ||
923 | |||
924 | const unsigned char * | ||
925 | fido_cred_authdata_ptr(const fido_cred_t *cred) | ||
926 | { | ||
927 | return (cred->authdata_cbor.ptr); | ||
928 | } | ||
929 | |||
930 | size_t | ||
931 | fido_cred_authdata_len(const fido_cred_t *cred) | ||
932 | { | ||
933 | return (cred->authdata_cbor.len); | ||
934 | } | ||
935 | |||
936 | const unsigned char * | ||
937 | fido_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 | |||
959 | size_t | ||
960 | fido_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 | |||
982 | const unsigned char * | ||
983 | fido_cred_id_ptr(const fido_cred_t *cred) | ||
984 | { | ||
985 | return (cred->attcred.id.ptr); | ||
986 | } | ||
987 | |||
988 | size_t | ||
989 | fido_cred_id_len(const fido_cred_t *cred) | ||
990 | { | ||
991 | return (cred->attcred.id.len); | ||
992 | } | ||
993 | |||
994 | const char * | ||
995 | fido_cred_fmt(const fido_cred_t *cred) | ||
996 | { | ||
997 | return (cred->fmt); | ||
998 | } | ||
999 | |||
1000 | const char * | ||
1001 | fido_cred_rp_id(const fido_cred_t *cred) | ||
1002 | { | ||
1003 | return (cred->rp.id); | ||
1004 | } | ||
1005 | |||
1006 | const char * | ||
1007 | fido_cred_rp_name(const fido_cred_t *cred) | ||
1008 | { | ||
1009 | return (cred->rp.name); | ||
1010 | } | ||
1011 | |||
1012 | const char * | ||
1013 | fido_cred_user_name(const fido_cred_t *cred) | ||
1014 | { | ||
1015 | return (cred->user.name); | ||
1016 | } | ||
1017 | |||
1018 | const char * | ||
1019 | fido_cred_display_name(const fido_cred_t *cred) | ||
1020 | { | ||
1021 | return (cred->user.display_name); | ||
1022 | } | ||
1023 | |||
1024 | const unsigned char * | ||
1025 | fido_cred_user_id_ptr(const fido_cred_t *cred) | ||
1026 | { | ||
1027 | return (cred->user.id.ptr); | ||
1028 | } | ||
1029 | |||
1030 | size_t | ||
1031 | fido_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 | |||
22 | static int | ||
23 | credman_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 | |||
54 | static int | ||
55 | credman_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(¶m_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; | ||
95 | fail: | ||
96 | cbor_vector_free(param_cbor, nitems(param_cbor)); | ||
97 | |||
98 | return (ok); | ||
99 | } | ||
100 | |||
101 | static int | ||
102 | credman_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; | ||
148 | fail: | ||
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 | |||
158 | static int | ||
159 | credman_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 | |||
181 | static int | ||
182 | credman_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 | |||
205 | static int | ||
206 | credman_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 | |||
218 | int | ||
219 | fido_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 | |||
230 | static int | ||
231 | credman_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 | |||
258 | static void | ||
259 | credman_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 | |||
271 | static int | ||
272 | credman_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 | |||
300 | static int | ||
301 | credman_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 | |||
339 | static int | ||
340 | credman_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 | |||
368 | static int | ||
369 | credman_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 | |||
398 | int | ||
399 | fido_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 | |||
410 | static int | ||
411 | credman_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; | ||
427 | fail: | ||
428 | free(cred.ptr); | ||
429 | |||
430 | return (r); | ||
431 | } | ||
432 | |||
433 | int | ||
434 | fido_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 | |||
445 | static int | ||
446 | credman_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 | |||
467 | static void | ||
468 | credman_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 | |||
485 | static int | ||
486 | credman_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 | |||
514 | static int | ||
515 | credman_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 | |||
553 | static int | ||
554 | credman_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 | |||
582 | static int | ||
583 | credman_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 | |||
602 | int | ||
603 | fido_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 | |||
613 | fido_credman_rk_t * | ||
614 | fido_credman_rk_new(void) | ||
615 | { | ||
616 | return (calloc(1, sizeof(fido_credman_rk_t))); | ||
617 | } | ||
618 | |||
619 | void | ||
620 | fido_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 | |||
632 | size_t | ||
633 | fido_credman_rk_count(const fido_credman_rk_t *rk) | ||
634 | { | ||
635 | return (rk->n_rx); | ||
636 | } | ||
637 | |||
638 | const fido_cred_t * | ||
639 | fido_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 | |||
647 | fido_credman_metadata_t * | ||
648 | fido_credman_metadata_new(void) | ||
649 | { | ||
650 | return (calloc(1, sizeof(fido_credman_metadata_t))); | ||
651 | } | ||
652 | |||
653 | void | ||
654 | fido_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 | |||
665 | uint64_t | ||
666 | fido_credman_rk_existing(const fido_credman_metadata_t *metadata) | ||
667 | { | ||
668 | return (metadata->rk_existing); | ||
669 | } | ||
670 | |||
671 | uint64_t | ||
672 | fido_credman_rk_remaining(const fido_credman_metadata_t *metadata) | ||
673 | { | ||
674 | return (metadata->rk_remaining); | ||
675 | } | ||
676 | |||
677 | fido_credman_rp_t * | ||
678 | fido_credman_rp_new(void) | ||
679 | { | ||
680 | return (calloc(1, sizeof(fido_credman_rp_t))); | ||
681 | } | ||
682 | |||
683 | void | ||
684 | fido_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 | |||
696 | size_t | ||
697 | fido_credman_rp_count(const fido_credman_rp_t *rp) | ||
698 | { | ||
699 | return (rp->n_rx); | ||
700 | } | ||
701 | |||
702 | const char * | ||
703 | fido_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 | |||
711 | const char * | ||
712 | fido_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 | |||
720 | size_t | ||
721 | fido_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 | |||
729 | const unsigned char * | ||
730 | fido_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 | |||
29 | static int | ||
30 | obtain_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) | ||
43 | static int | ||
44 | obtain_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; | ||
57 | fail: | ||
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 | |||
67 | static int | ||
68 | fido_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 | |||
102 | static int | ||
103 | fido_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); | ||
125 | fail: | ||
126 | dev->io.close(dev->io_handle); | ||
127 | dev->io_handle = NULL; | ||
128 | |||
129 | return (FIDO_ERR_RX); | ||
130 | } | ||
131 | |||
132 | static int | ||
133 | fido_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 | |||
144 | int | ||
145 | fido_dev_open(fido_dev_t *dev, const char *path) | ||
146 | { | ||
147 | return (fido_dev_open_wait(dev, path, -1)); | ||
148 | } | ||
149 | |||
150 | int | ||
151 | fido_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 | |||
162 | int | ||
163 | fido_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 | |||
171 | int | ||
172 | fido_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 | |||
193 | void | ||
194 | fido_init(int flags) | ||
195 | { | ||
196 | if (flags & FIDO_DEBUG || getenv("FIDO_DEBUG") != NULL) | ||
197 | fido_log_init(); | ||
198 | } | ||
199 | |||
200 | fido_dev_t * | ||
201 | fido_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 | |||
225 | void | ||
226 | fido_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 | |||
238 | uint8_t | ||
239 | fido_dev_protocol(const fido_dev_t *dev) | ||
240 | { | ||
241 | return (dev->attr.protocol); | ||
242 | } | ||
243 | |||
244 | uint8_t | ||
245 | fido_dev_major(const fido_dev_t *dev) | ||
246 | { | ||
247 | return (dev->attr.major); | ||
248 | } | ||
249 | |||
250 | uint8_t | ||
251 | fido_dev_minor(const fido_dev_t *dev) | ||
252 | { | ||
253 | return (dev->attr.minor); | ||
254 | } | ||
255 | |||
256 | uint8_t | ||
257 | fido_dev_build(const fido_dev_t *dev) | ||
258 | { | ||
259 | return (dev->attr.build); | ||
260 | } | ||
261 | |||
262 | uint8_t | ||
263 | fido_dev_flags(const fido_dev_t *dev) | ||
264 | { | ||
265 | return (dev->attr.flags); | ||
266 | } | ||
267 | |||
268 | bool | ||
269 | fido_dev_is_fido2(const fido_dev_t *dev) | ||
270 | { | ||
271 | return (dev->attr.flags & FIDO_CAP_CBOR); | ||
272 | } | ||
273 | |||
274 | void | ||
275 | fido_dev_force_u2f(fido_dev_t *dev) | ||
276 | { | ||
277 | dev->attr.flags &= ~FIDO_CAP_CBOR; | ||
278 | } | ||
279 | |||
280 | void | ||
281 | fido_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 | |||
9 | TMPDIR=$(mktemp -d) | ||
10 | GNU=${TMPDIR}/gnu | ||
11 | LLVM=${TMPDIR}/llvm | ||
12 | MSVC=${TMPDIR}/msvc | ||
13 | |||
14 | egrep -o $'([^*{}\t]+);$' export.gnu | tr -d ';' | sort > ${GNU} | ||
15 | sed 's/^_//g' export.llvm | sort > ${LLVM} | ||
16 | egrep -v "^EXPORTS$" export.msvc | sort > ${MSVC} | ||
17 | diff -u ${GNU} ${LLVM} && diff -u ${MSVC} ${LLVM} | ||
18 | ERROR=$? | ||
19 | |||
20 | rm ${GNU} ${LLVM} ${MSVC} | ||
21 | rmdir ${TMPDIR} | ||
22 | |||
23 | exit ${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 | |||
13 | static int | ||
14 | do_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; | ||
61 | fail: | ||
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 | |||
76 | int | ||
77 | fido_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; | ||
111 | fail: | ||
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 | ||
17 | EVP_PKEY * | ||
18 | EVP_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 | |||
29 | int | ||
30 | EVP_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 | |||
40 | int | ||
41 | EVP_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 | ||
55 | EVP_MD_CTX * | ||
56 | EVP_MD_CTX_new(void) | ||
57 | { | ||
58 | return (NULL); | ||
59 | } | ||
60 | |||
61 | void | ||
62 | EVP_MD_CTX_free(EVP_MD_CTX *ctx) | ||
63 | { | ||
64 | (void)ctx; | ||
65 | } | ||
66 | #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ | ||
67 | |||
68 | static int | ||
69 | decode_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 | |||
83 | static int | ||
84 | decode_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 | |||
100 | int | ||
101 | eddsa_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 | |||
113 | eddsa_pk_t * | ||
114 | eddsa_pk_new(void) | ||
115 | { | ||
116 | return (calloc(1, sizeof(eddsa_pk_t))); | ||
117 | } | ||
118 | |||
119 | void | ||
120 | eddsa_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 | |||
133 | int | ||
134 | eddsa_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 | |||
144 | EVP_PKEY * | ||
145 | eddsa_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 | |||
156 | int | ||
157 | eddsa_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 | |||
9 | const char * | ||
10 | fido_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 | |||
16 | static int | ||
17 | decode_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 | |||
31 | static int | ||
32 | decode_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 | |||
50 | int | ||
51 | es256_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 | |||
63 | cbor_item_t * | ||
64 | es256_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; | ||
118 | fail: | ||
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 | |||
136 | es256_sk_t * | ||
137 | es256_sk_new(void) | ||
138 | { | ||
139 | return (calloc(1, sizeof(es256_sk_t))); | ||
140 | } | ||
141 | |||
142 | void | ||
143 | es256_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 | |||
156 | es256_pk_t * | ||
157 | es256_pk_new(void) | ||
158 | { | ||
159 | return (calloc(1, sizeof(es256_pk_t))); | ||
160 | } | ||
161 | |||
162 | void | ||
163 | es256_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 | |||
176 | int | ||
177 | es256_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 | |||
187 | int | ||
188 | es256_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 | |||
195 | int | ||
196 | es256_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 | |||
203 | int | ||
204 | es256_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; | ||
239 | fail: | ||
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 | |||
252 | EVP_PKEY * | ||
253 | es256_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; | ||
298 | fail: | ||
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 | |||
313 | int | ||
314 | es256_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; | ||
348 | fail: | ||
349 | if (ctx != NULL) | ||
350 | BN_CTX_free(ctx); | ||
351 | |||
352 | return (ok); | ||
353 | } | ||
354 | |||
355 | EVP_PKEY * | ||
356 | es256_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; | ||
386 | fail: | ||
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 | |||
399 | int | ||
400 | es256_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; | ||
425 | fail: | ||
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 @@ | |||
1 | EXPORTS | ||
2 | eddsa_pk_free | ||
3 | eddsa_pk_from_EVP_PKEY | ||
4 | eddsa_pk_from_ptr | ||
5 | eddsa_pk_new | ||
6 | eddsa_pk_to_EVP_PKEY | ||
7 | es256_pk_free | ||
8 | es256_pk_from_EC_KEY | ||
9 | es256_pk_from_ptr | ||
10 | es256_pk_new | ||
11 | es256_pk_to_EVP_PKEY | ||
12 | fido_assert_allow_cred | ||
13 | fido_assert_authdata_len | ||
14 | fido_assert_authdata_ptr | ||
15 | fido_assert_clientdata_hash_len | ||
16 | fido_assert_clientdata_hash_ptr | ||
17 | fido_assert_count | ||
18 | fido_assert_flags | ||
19 | fido_assert_free | ||
20 | fido_assert_hmac_secret_len | ||
21 | fido_assert_hmac_secret_ptr | ||
22 | fido_assert_id_len | ||
23 | fido_assert_id_ptr | ||
24 | fido_assert_new | ||
25 | fido_assert_rp_id | ||
26 | fido_assert_set_authdata | ||
27 | fido_assert_set_authdata_raw | ||
28 | fido_assert_set_clientdata_hash | ||
29 | fido_assert_set_count | ||
30 | fido_assert_set_extensions | ||
31 | fido_assert_set_hmac_salt | ||
32 | fido_assert_set_options | ||
33 | fido_assert_set_rp | ||
34 | fido_assert_set_sig | ||
35 | fido_assert_set_up | ||
36 | fido_assert_set_uv | ||
37 | fido_assert_sigcount | ||
38 | fido_assert_sig_len | ||
39 | fido_assert_sig_ptr | ||
40 | fido_assert_user_display_name | ||
41 | fido_assert_user_icon | ||
42 | fido_assert_user_id_len | ||
43 | fido_assert_user_id_ptr | ||
44 | fido_assert_user_name | ||
45 | fido_assert_verify | ||
46 | fido_bio_dev_enroll_begin | ||
47 | fido_bio_dev_enroll_cancel | ||
48 | fido_bio_dev_enroll_continue | ||
49 | fido_bio_dev_enroll_remove | ||
50 | fido_bio_dev_get_info | ||
51 | fido_bio_dev_get_template_array | ||
52 | fido_bio_dev_set_template_name | ||
53 | fido_bio_enroll_free | ||
54 | fido_bio_enroll_last_status | ||
55 | fido_bio_enroll_new | ||
56 | fido_bio_enroll_remaining_samples | ||
57 | fido_bio_info_free | ||
58 | fido_bio_info_max_samples | ||
59 | fido_bio_info_new | ||
60 | fido_bio_info_type | ||
61 | fido_bio_template | ||
62 | fido_bio_template_array_count | ||
63 | fido_bio_template_array_free | ||
64 | fido_bio_template_array_new | ||
65 | fido_bio_template_free | ||
66 | fido_bio_template_id_len | ||
67 | fido_bio_template_id_ptr | ||
68 | fido_bio_template_name | ||
69 | fido_bio_template_new | ||
70 | fido_bio_template_set_id | ||
71 | fido_bio_template_set_name | ||
72 | fido_cbor_info_aaguid_len | ||
73 | fido_cbor_info_aaguid_ptr | ||
74 | fido_cbor_info_extensions_len | ||
75 | fido_cbor_info_extensions_ptr | ||
76 | fido_cbor_info_free | ||
77 | fido_cbor_info_maxmsgsiz | ||
78 | fido_cbor_info_new | ||
79 | fido_cbor_info_options_len | ||
80 | fido_cbor_info_options_name_ptr | ||
81 | fido_cbor_info_options_value_ptr | ||
82 | fido_cbor_info_protocols_len | ||
83 | fido_cbor_info_protocols_ptr | ||
84 | fido_cbor_info_versions_len | ||
85 | fido_cbor_info_versions_ptr | ||
86 | fido_cred_authdata_len | ||
87 | fido_cred_authdata_ptr | ||
88 | fido_cred_clientdata_hash_len | ||
89 | fido_cred_clientdata_hash_ptr | ||
90 | fido_cred_display_name | ||
91 | fido_cred_exclude | ||
92 | fido_cred_flags | ||
93 | fido_cred_fmt | ||
94 | fido_cred_free | ||
95 | fido_cred_id_len | ||
96 | fido_cred_id_ptr | ||
97 | fido_credman_del_dev_rk | ||
98 | fido_credman_get_dev_metadata | ||
99 | fido_credman_get_dev_rk | ||
100 | fido_credman_get_dev_rp | ||
101 | fido_credman_metadata_free | ||
102 | fido_credman_metadata_new | ||
103 | fido_credman_rk | ||
104 | fido_credman_rk_count | ||
105 | fido_credman_rk_existing | ||
106 | fido_credman_rk_free | ||
107 | fido_credman_rk_new | ||
108 | fido_credman_rk_remaining | ||
109 | fido_credman_rp_count | ||
110 | fido_credman_rp_free | ||
111 | fido_credman_rp_id | ||
112 | fido_credman_rp_id_hash_len | ||
113 | fido_credman_rp_id_hash_ptr | ||
114 | fido_credman_rp_name | ||
115 | fido_credman_rp_new | ||
116 | fido_cred_new | ||
117 | fido_cred_pubkey_len | ||
118 | fido_cred_pubkey_ptr | ||
119 | fido_cred_rp_id | ||
120 | fido_cred_rp_name | ||
121 | fido_cred_set_authdata | ||
122 | fido_cred_set_authdata_raw | ||
123 | fido_cred_set_clientdata_hash | ||
124 | fido_cred_set_extensions | ||
125 | fido_cred_set_fmt | ||
126 | fido_cred_set_options | ||
127 | fido_cred_set_rk | ||
128 | fido_cred_set_rp | ||
129 | fido_cred_set_sig | ||
130 | fido_cred_set_type | ||
131 | fido_cred_set_user | ||
132 | fido_cred_set_uv | ||
133 | fido_cred_set_x509 | ||
134 | fido_cred_sig_len | ||
135 | fido_cred_sig_ptr | ||
136 | fido_cred_type | ||
137 | fido_cred_user_id_len | ||
138 | fido_cred_user_id_ptr | ||
139 | fido_cred_user_name | ||
140 | fido_cred_verify | ||
141 | fido_cred_verify_self | ||
142 | fido_cred_x5c_len | ||
143 | fido_cred_x5c_ptr | ||
144 | fido_dev_build | ||
145 | fido_dev_cancel | ||
146 | fido_dev_close | ||
147 | fido_dev_flags | ||
148 | fido_dev_force_fido2 | ||
149 | fido_dev_force_u2f | ||
150 | fido_dev_free | ||
151 | fido_dev_get_assert | ||
152 | fido_dev_get_cbor_info | ||
153 | fido_dev_get_retry_count | ||
154 | fido_dev_info_free | ||
155 | fido_dev_info_manifest | ||
156 | fido_dev_info_manufacturer_string | ||
157 | fido_dev_info_new | ||
158 | fido_dev_info_path | ||
159 | fido_dev_info_product | ||
160 | fido_dev_info_product_string | ||
161 | fido_dev_info_ptr | ||
162 | fido_dev_info_vendor | ||
163 | fido_dev_is_fido2 | ||
164 | fido_dev_major | ||
165 | fido_dev_make_cred | ||
166 | fido_dev_minor | ||
167 | fido_dev_new | ||
168 | fido_dev_open | ||
169 | fido_dev_protocol | ||
170 | fido_dev_reset | ||
171 | fido_dev_set_io_functions | ||
172 | fido_dev_set_pin | ||
173 | fido_init | ||
174 | fido_strerr | ||
175 | rs256_pk_free | ||
176 | rs256_pk_from_ptr | ||
177 | rs256_pk_from_RSA | ||
178 | rs256_pk_new | ||
179 | rs256_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 */ | ||
11 | int aes256_cbc_dec(const fido_blob_t *, const fido_blob_t *, fido_blob_t *); | ||
12 | int aes256_cbc_enc(const fido_blob_t *, const fido_blob_t *, fido_blob_t *); | ||
13 | |||
14 | /* cbor encoding functions */ | ||
15 | cbor_item_t *cbor_flatten_vector(cbor_item_t **, size_t); | ||
16 | cbor_item_t *cbor_encode_assert_options(fido_opt_t, fido_opt_t); | ||
17 | cbor_item_t *cbor_encode_change_pin_auth(const fido_blob_t *, | ||
18 | const fido_blob_t *, const fido_blob_t *); | ||
19 | cbor_item_t *cbor_encode_extensions(int); | ||
20 | cbor_item_t *cbor_encode_hmac_secret_param(const fido_blob_t *, | ||
21 | const es256_pk_t *, const fido_blob_t *); | ||
22 | cbor_item_t *cbor_encode_options(fido_opt_t, fido_opt_t); | ||
23 | cbor_item_t *cbor_encode_pin_auth(const fido_blob_t *, const fido_blob_t *); | ||
24 | cbor_item_t *cbor_encode_pin_enc(const fido_blob_t *, const fido_blob_t *); | ||
25 | cbor_item_t *cbor_encode_pin_hash_enc(const fido_blob_t *, const fido_blob_t *); | ||
26 | cbor_item_t *cbor_encode_pin_opt(void); | ||
27 | cbor_item_t *cbor_encode_pubkey(const fido_blob_t *); | ||
28 | cbor_item_t *cbor_encode_pubkey_list(const fido_blob_array_t *); | ||
29 | cbor_item_t *cbor_encode_pubkey_param(int); | ||
30 | cbor_item_t *cbor_encode_rp_entity(const fido_rp_t *); | ||
31 | cbor_item_t *cbor_encode_set_pin_auth(const fido_blob_t *, const fido_blob_t *); | ||
32 | cbor_item_t *cbor_encode_user_entity(const fido_user_t *); | ||
33 | cbor_item_t *es256_pk_encode(const es256_pk_t *, int); | ||
34 | |||
35 | /* cbor decoding functions */ | ||
36 | int cbor_decode_attstmt(const cbor_item_t *, fido_attstmt_t *); | ||
37 | int cbor_decode_cred_authdata(const cbor_item_t *, int, fido_blob_t *, | ||
38 | fido_authdata_t *, fido_attcred_t *, int *); | ||
39 | int cbor_decode_assert_authdata(const cbor_item_t *, fido_blob_t *, | ||
40 | fido_authdata_t *, int *, fido_blob_t *); | ||
41 | int cbor_decode_cred_id(const cbor_item_t *, fido_blob_t *); | ||
42 | int cbor_decode_fmt(const cbor_item_t *, char **); | ||
43 | int cbor_decode_pubkey(const cbor_item_t *, int *, void *); | ||
44 | int cbor_decode_rp_entity(const cbor_item_t *, fido_rp_t *); | ||
45 | int cbor_decode_uint64(const cbor_item_t *, uint64_t *); | ||
46 | int cbor_decode_user(const cbor_item_t *, fido_user_t *); | ||
47 | int es256_pk_decode(const cbor_item_t *, es256_pk_t *); | ||
48 | int rs256_pk_decode(const cbor_item_t *, rs256_pk_t *); | ||
49 | int eddsa_pk_decode(const cbor_item_t *, eddsa_pk_t *); | ||
50 | |||
51 | /* auxiliary cbor routines */ | ||
52 | int cbor_add_bool(cbor_item_t *, const char *, fido_opt_t); | ||
53 | int cbor_add_bytestring(cbor_item_t *, const char *, const unsigned char *, | ||
54 | size_t); | ||
55 | int cbor_add_string(cbor_item_t *, const char *, const char *); | ||
56 | int cbor_array_iter(const cbor_item_t *, void *, int(*)(const cbor_item_t *, | ||
57 | void *)); | ||
58 | int cbor_build_frame(uint8_t, cbor_item_t *[], size_t, fido_blob_t *); | ||
59 | int cbor_bytestring_copy(const cbor_item_t *, unsigned char **, size_t *); | ||
60 | int cbor_map_iter(const cbor_item_t *, void *, int(*)(const cbor_item_t *, | ||
61 | const cbor_item_t *, void *)); | ||
62 | int cbor_string_copy(const cbor_item_t *, char **); | ||
63 | int cbor_parse_reply(const unsigned char *, size_t, void *, | ||
64 | int(*)(const cbor_item_t *, const cbor_item_t *, void *)); | ||
65 | int 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 **); | ||
67 | void 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 */ | ||
74 | int fido_buf_read(const unsigned char **, size_t *, void *, size_t); | ||
75 | int fido_buf_write(unsigned char **, size_t *, const void *, size_t); | ||
76 | |||
77 | /* hid i/o */ | ||
78 | void *fido_hid_open(const char *); | ||
79 | void fido_hid_close(void *); | ||
80 | int fido_hid_read(void *, unsigned char *, size_t, int); | ||
81 | int fido_hid_write(void *, const unsigned char *, size_t); | ||
82 | |||
83 | /* generic i/o */ | ||
84 | int fido_rx_cbor_status(fido_dev_t *, int); | ||
85 | int fido_rx(fido_dev_t *, uint8_t, void *, size_t, int); | ||
86 | int 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__ | ||
95 | void fido_log_init(void); | ||
96 | void fido_log_debug(const char *, ...) | ||
97 | __attribute__((__format__ (printf, 1, 2))); | ||
98 | void fido_log_xxd(const void *, size_t); | ||
99 | #else | ||
100 | void fido_log_init(void); | ||
101 | void fido_log_debug(const char *, ...); | ||
102 | void fido_log_xxd(const void *, size_t); | ||
103 | #endif /* __GNUC__ */ | ||
104 | #endif /* FIDO_NO_DIAGNOSTIC */ | ||
105 | |||
106 | /* u2f */ | ||
107 | int u2f_register(fido_dev_t *, fido_cred_t *, int); | ||
108 | int u2f_authenticate(fido_dev_t *, fido_assert_t *, int); | ||
109 | |||
110 | /* unexposed fido ops */ | ||
111 | int fido_dev_authkey(fido_dev_t *, es256_pk_t *); | ||
112 | int fido_dev_get_pin_token(fido_dev_t *, const char *, const fido_blob_t *, | ||
113 | const es256_pk_t *, fido_blob_t *); | ||
114 | int fido_do_ecdh(fido_dev_t *, es256_pk_t **, fido_blob_t **); | ||
115 | |||
116 | /* misc */ | ||
117 | void fido_assert_reset_rx(fido_assert_t *); | ||
118 | void fido_assert_reset_tx(fido_assert_t *); | ||
119 | void fido_cred_reset_rx(fido_cred_t *); | ||
120 | void fido_cred_reset_tx(fido_cred_t *); | ||
121 | int fido_check_rp_id(const char *, const unsigned char *); | ||
122 | int fido_check_flags(uint8_t, fido_opt_t, fido_opt_t); | ||
123 | |||
124 | /* crypto */ | ||
125 | int fido_verify_sig_es256(const fido_blob_t *, const es256_pk_t *, | ||
126 | const fido_blob_t *); | ||
127 | int fido_verify_sig_rs256(const fido_blob_t *, const rs256_pk_t *, | ||
128 | const fido_blob_t *); | ||
129 | int 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 | |||
17 | typedef void *fido_dev_io_open_t(const char *); | ||
18 | typedef void fido_dev_io_close_t(void *); | ||
19 | typedef int fido_dev_io_read_t(void *, unsigned char *, size_t, int); | ||
20 | typedef int fido_dev_io_write_t(void *, const unsigned char *, size_t); | ||
21 | |||
22 | typedef 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 | |||
29 | typedef 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 | ||
50 | typedef struct fido_assert fido_assert_t; | ||
51 | typedef struct fido_cbor_info fido_cbor_info_t; | ||
52 | typedef struct fido_cred fido_cred_t; | ||
53 | typedef struct fido_dev fido_dev_t; | ||
54 | typedef struct fido_dev_info fido_dev_info_t; | ||
55 | typedef struct es256_pk es256_pk_t; | ||
56 | typedef struct es256_sk es256_sk_t; | ||
57 | typedef struct rs256_pk rs256_pk_t; | ||
58 | typedef struct eddsa_pk eddsa_pk_t; | ||
59 | #endif | ||
60 | |||
61 | fido_assert_t *fido_assert_new(void); | ||
62 | fido_cred_t *fido_cred_new(void); | ||
63 | fido_dev_t *fido_dev_new(void); | ||
64 | fido_dev_info_t *fido_dev_info_new(size_t); | ||
65 | fido_cbor_info_t *fido_cbor_info_new(void); | ||
66 | |||
67 | void fido_assert_free(fido_assert_t **); | ||
68 | void fido_cbor_info_free(fido_cbor_info_t **); | ||
69 | void fido_cred_free(fido_cred_t **); | ||
70 | void fido_dev_force_fido2(fido_dev_t *); | ||
71 | void fido_dev_force_u2f(fido_dev_t *); | ||
72 | void fido_dev_free(fido_dev_t **); | ||
73 | void fido_dev_info_free(fido_dev_info_t **, size_t); | ||
74 | |||
75 | /* fido_init() flags. */ | ||
76 | #define FIDO_DEBUG 0x01 | ||
77 | |||
78 | void fido_init(int); | ||
79 | |||
80 | const unsigned char *fido_assert_authdata_ptr(const fido_assert_t *, size_t); | ||
81 | const unsigned char *fido_assert_clientdata_hash_ptr(const fido_assert_t *); | ||
82 | const unsigned char *fido_assert_hmac_secret_ptr(const fido_assert_t *, size_t); | ||
83 | const unsigned char *fido_assert_id_ptr(const fido_assert_t *, size_t); | ||
84 | const unsigned char *fido_assert_sig_ptr(const fido_assert_t *, size_t); | ||
85 | const unsigned char *fido_assert_user_id_ptr(const fido_assert_t *, size_t); | ||
86 | |||
87 | char **fido_cbor_info_extensions_ptr(const fido_cbor_info_t *); | ||
88 | char **fido_cbor_info_options_name_ptr(const fido_cbor_info_t *); | ||
89 | char **fido_cbor_info_versions_ptr(const fido_cbor_info_t *); | ||
90 | const bool *fido_cbor_info_options_value_ptr(const fido_cbor_info_t *); | ||
91 | const char *fido_assert_rp_id(const fido_assert_t *); | ||
92 | const char *fido_assert_user_display_name(const fido_assert_t *, size_t); | ||
93 | const char *fido_assert_user_icon(const fido_assert_t *, size_t); | ||
94 | const char *fido_assert_user_name(const fido_assert_t *, size_t); | ||
95 | const char *fido_cred_display_name(const fido_cred_t *); | ||
96 | const char *fido_cred_fmt(const fido_cred_t *); | ||
97 | const char *fido_cred_rp_id(const fido_cred_t *); | ||
98 | const char *fido_cred_rp_name(const fido_cred_t *); | ||
99 | const char *fido_cred_user_name(const fido_cred_t *); | ||
100 | const char *fido_dev_info_manufacturer_string(const fido_dev_info_t *); | ||
101 | const char *fido_dev_info_path(const fido_dev_info_t *); | ||
102 | const char *fido_dev_info_product_string(const fido_dev_info_t *); | ||
103 | const fido_dev_info_t *fido_dev_info_ptr(const fido_dev_info_t *, size_t); | ||
104 | const uint8_t *fido_cbor_info_protocols_ptr(const fido_cbor_info_t *); | ||
105 | const unsigned char *fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *); | ||
106 | const unsigned char *fido_cred_authdata_ptr(const fido_cred_t *); | ||
107 | const unsigned char *fido_cred_clientdata_hash_ptr(const fido_cred_t *); | ||
108 | const unsigned char *fido_cred_id_ptr(const fido_cred_t *); | ||
109 | const unsigned char *fido_cred_user_id_ptr(const fido_cred_t *); | ||
110 | const unsigned char *fido_cred_pubkey_ptr(const fido_cred_t *); | ||
111 | const unsigned char *fido_cred_sig_ptr(const fido_cred_t *); | ||
112 | const unsigned char *fido_cred_x5c_ptr(const fido_cred_t *); | ||
113 | |||
114 | int fido_assert_allow_cred(fido_assert_t *, const unsigned char *, size_t); | ||
115 | int fido_assert_set_authdata(fido_assert_t *, size_t, const unsigned char *, | ||
116 | size_t); | ||
117 | int fido_assert_set_authdata_raw(fido_assert_t *, size_t, const unsigned char *, | ||
118 | size_t); | ||
119 | int fido_assert_set_clientdata_hash(fido_assert_t *, const unsigned char *, | ||
120 | size_t); | ||
121 | int fido_assert_set_count(fido_assert_t *, size_t); | ||
122 | int fido_assert_set_extensions(fido_assert_t *, int); | ||
123 | int fido_assert_set_hmac_salt(fido_assert_t *, const unsigned char *, size_t); | ||
124 | int fido_assert_set_options(fido_assert_t *, bool, bool) __attribute__((__deprecated__)); | ||
125 | int fido_assert_set_rp(fido_assert_t *, const char *); | ||
126 | int fido_assert_set_up(fido_assert_t *, fido_opt_t); | ||
127 | int fido_assert_set_uv(fido_assert_t *, fido_opt_t); | ||
128 | int fido_assert_set_sig(fido_assert_t *, size_t, const unsigned char *, size_t); | ||
129 | int fido_assert_verify(const fido_assert_t *, size_t, int, const void *); | ||
130 | int fido_cred_exclude(fido_cred_t *, const unsigned char *, size_t); | ||
131 | int fido_cred_set_authdata(fido_cred_t *, const unsigned char *, size_t); | ||
132 | int fido_cred_set_authdata_raw(fido_cred_t *, const unsigned char *, size_t); | ||
133 | int fido_cred_set_clientdata_hash(fido_cred_t *, const unsigned char *, size_t); | ||
134 | int fido_cred_set_extensions(fido_cred_t *, int); | ||
135 | int fido_cred_set_fmt(fido_cred_t *, const char *); | ||
136 | int fido_cred_set_options(fido_cred_t *, bool, bool) __attribute__((__deprecated__)); | ||
137 | int fido_cred_set_rk(fido_cred_t *, fido_opt_t); | ||
138 | int fido_cred_set_rp(fido_cred_t *, const char *, const char *); | ||
139 | int fido_cred_set_sig(fido_cred_t *, const unsigned char *, size_t); | ||
140 | int fido_cred_set_type(fido_cred_t *, int); | ||
141 | int fido_cred_set_uv(fido_cred_t *, fido_opt_t); | ||
142 | int fido_cred_type(const fido_cred_t *); | ||
143 | int fido_cred_set_user(fido_cred_t *, const unsigned char *, size_t, | ||
144 | const char *, const char *, const char *); | ||
145 | int fido_cred_set_x509(fido_cred_t *, const unsigned char *, size_t); | ||
146 | int fido_cred_verify(const fido_cred_t *); | ||
147 | int fido_cred_verify_self(const fido_cred_t *); | ||
148 | int fido_dev_cancel(fido_dev_t *); | ||
149 | int fido_dev_close(fido_dev_t *); | ||
150 | int fido_dev_get_assert(fido_dev_t *, fido_assert_t *, const char *); | ||
151 | int fido_dev_get_cbor_info(fido_dev_t *, fido_cbor_info_t *); | ||
152 | int fido_dev_get_retry_count(fido_dev_t *, int *); | ||
153 | int fido_dev_info_manifest(fido_dev_info_t *, size_t, size_t *); | ||
154 | int fido_dev_make_cred(fido_dev_t *, fido_cred_t *, const char *); | ||
155 | int fido_dev_open(fido_dev_t *, const char *); | ||
156 | int fido_dev_reset(fido_dev_t *); | ||
157 | int fido_dev_set_io_functions(fido_dev_t *, const fido_dev_io_t *); | ||
158 | int fido_dev_set_pin(fido_dev_t *, const char *, const char *); | ||
159 | |||
160 | size_t fido_assert_authdata_len(const fido_assert_t *, size_t); | ||
161 | size_t fido_assert_clientdata_hash_len(const fido_assert_t *); | ||
162 | size_t fido_assert_count(const fido_assert_t *); | ||
163 | size_t fido_assert_hmac_secret_len(const fido_assert_t *, size_t); | ||
164 | size_t fido_assert_id_len(const fido_assert_t *, size_t); | ||
165 | size_t fido_assert_sig_len(const fido_assert_t *, size_t); | ||
166 | size_t fido_assert_user_id_len(const fido_assert_t *, size_t); | ||
167 | size_t fido_cbor_info_aaguid_len(const fido_cbor_info_t *); | ||
168 | size_t fido_cbor_info_extensions_len(const fido_cbor_info_t *); | ||
169 | size_t fido_cbor_info_options_len(const fido_cbor_info_t *); | ||
170 | size_t fido_cbor_info_protocols_len(const fido_cbor_info_t *); | ||
171 | size_t fido_cbor_info_versions_len(const fido_cbor_info_t *); | ||
172 | size_t fido_cred_authdata_len(const fido_cred_t *); | ||
173 | size_t fido_cred_clientdata_hash_len(const fido_cred_t *); | ||
174 | size_t fido_cred_id_len(const fido_cred_t *); | ||
175 | size_t fido_cred_user_id_len(const fido_cred_t *); | ||
176 | size_t fido_cred_pubkey_len(const fido_cred_t *); | ||
177 | size_t fido_cred_sig_len(const fido_cred_t *); | ||
178 | size_t fido_cred_x5c_len(const fido_cred_t *); | ||
179 | |||
180 | uint8_t fido_assert_flags(const fido_assert_t *, size_t); | ||
181 | uint32_t fido_assert_sigcount(const fido_assert_t *, size_t); | ||
182 | uint8_t fido_cred_flags(const fido_cred_t *); | ||
183 | uint8_t fido_dev_protocol(const fido_dev_t *); | ||
184 | uint8_t fido_dev_major(const fido_dev_t *); | ||
185 | uint8_t fido_dev_minor(const fido_dev_t *); | ||
186 | uint8_t fido_dev_build(const fido_dev_t *); | ||
187 | uint8_t fido_dev_flags(const fido_dev_t *); | ||
188 | int16_t fido_dev_info_vendor(const fido_dev_info_t *); | ||
189 | int16_t fido_dev_info_product(const fido_dev_info_t *); | ||
190 | uint64_t fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *); | ||
191 | |||
192 | bool 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 | ||
17 | struct fido_bio_template { | ||
18 | fido_blob_t id; | ||
19 | char *name; | ||
20 | }; | ||
21 | |||
22 | struct 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 | |||
28 | struct fido_bio_enroll { | ||
29 | uint8_t remaining_samples; | ||
30 | uint8_t last_status; | ||
31 | fido_blob_t *token; | ||
32 | }; | ||
33 | |||
34 | struct fido_bio_info { | ||
35 | uint8_t type; | ||
36 | uint8_t max_samples; | ||
37 | }; | ||
38 | #endif | ||
39 | |||
40 | typedef struct fido_bio_template fido_bio_template_t; | ||
41 | typedef struct fido_bio_template_array fido_bio_template_array_t; | ||
42 | typedef struct fido_bio_enroll fido_bio_enroll_t; | ||
43 | typedef 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 | |||
61 | const char *fido_bio_template_name(const fido_bio_template_t *); | ||
62 | const fido_bio_template_t *fido_bio_template(const fido_bio_template_array_t *, | ||
63 | size_t); | ||
64 | const unsigned char *fido_bio_template_id_ptr(const fido_bio_template_t *); | ||
65 | fido_bio_enroll_t *fido_bio_enroll_new(void); | ||
66 | fido_bio_info_t *fido_bio_info_new(void); | ||
67 | fido_bio_template_array_t *fido_bio_template_array_new(void); | ||
68 | fido_bio_template_t *fido_bio_template_new(void); | ||
69 | int fido_bio_dev_enroll_begin(fido_dev_t *, fido_bio_template_t *, | ||
70 | fido_bio_enroll_t *, uint32_t, const char *); | ||
71 | int fido_bio_dev_enroll_cancel(fido_dev_t *); | ||
72 | int fido_bio_dev_enroll_continue(fido_dev_t *, const fido_bio_template_t *, | ||
73 | fido_bio_enroll_t *, uint32_t); | ||
74 | int fido_bio_dev_enroll_remove(fido_dev_t *, const fido_bio_template_t *, | ||
75 | const char *); | ||
76 | int fido_bio_dev_get_info(fido_dev_t *, fido_bio_info_t *); | ||
77 | int fido_bio_dev_get_template_array(fido_dev_t *, fido_bio_template_array_t *, | ||
78 | const char *); | ||
79 | int fido_bio_dev_set_template_name(fido_dev_t *, const fido_bio_template_t *, | ||
80 | const char *); | ||
81 | int fido_bio_template_set_id(fido_bio_template_t *, const unsigned char *, | ||
82 | size_t); | ||
83 | int fido_bio_template_set_name(fido_bio_template_t *, const char *); | ||
84 | size_t fido_bio_template_array_count(const fido_bio_template_array_t *); | ||
85 | size_t fido_bio_template_id_len(const fido_bio_template_t *); | ||
86 | uint8_t fido_bio_enroll_last_status(const fido_bio_enroll_t *); | ||
87 | uint8_t fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *); | ||
88 | uint8_t fido_bio_info_max_samples(const fido_bio_info_t *); | ||
89 | uint8_t fido_bio_info_type(const fido_bio_info_t *); | ||
90 | void fido_bio_enroll_free(fido_bio_enroll_t **); | ||
91 | void fido_bio_info_free(fido_bio_info_t **); | ||
92 | void fido_bio_template_array_free(fido_bio_template_array_t **); | ||
93 | void 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 | ||
17 | struct fido_credman_metadata { | ||
18 | uint64_t rk_existing; | ||
19 | uint64_t rk_remaining; | ||
20 | }; | ||
21 | |||
22 | struct fido_credman_single_rp { | ||
23 | fido_rp_t rp_entity; | ||
24 | fido_blob_t rp_id_hash; | ||
25 | }; | ||
26 | |||
27 | struct 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 | |||
33 | struct 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 | |||
40 | typedef struct fido_credman_metadata fido_credman_metadata_t; | ||
41 | typedef struct fido_credman_rk fido_credman_rk_t; | ||
42 | typedef struct fido_credman_rp fido_credman_rp_t; | ||
43 | |||
44 | const char *fido_credman_rp_id(const fido_credman_rp_t *, size_t); | ||
45 | const char *fido_credman_rp_name(const fido_credman_rp_t *, size_t); | ||
46 | |||
47 | const fido_cred_t *fido_credman_rk(const fido_credman_rk_t *, size_t); | ||
48 | const unsigned char *fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *, | ||
49 | size_t); | ||
50 | |||
51 | fido_credman_metadata_t *fido_credman_metadata_new(void); | ||
52 | fido_credman_rk_t *fido_credman_rk_new(void); | ||
53 | fido_credman_rp_t *fido_credman_rp_new(void); | ||
54 | |||
55 | int fido_credman_del_dev_rk(fido_dev_t *, const unsigned char *, size_t, | ||
56 | const char *); | ||
57 | int fido_credman_get_dev_metadata(fido_dev_t *, fido_credman_metadata_t *, | ||
58 | const char *); | ||
59 | int fido_credman_get_dev_rk(fido_dev_t *, const char *, fido_credman_rk_t *, | ||
60 | const char *); | ||
61 | int fido_credman_get_dev_rp(fido_dev_t *, fido_credman_rp_t *, const char *); | ||
62 | |||
63 | size_t fido_credman_rk_count(const fido_credman_rk_t *); | ||
64 | size_t fido_credman_rp_count(const fido_credman_rp_t *); | ||
65 | size_t fido_credman_rp_id_hash_len(const fido_credman_rp_t *, size_t); | ||
66 | |||
67 | uint64_t fido_credman_rk_existing(const fido_credman_metadata_t *); | ||
68 | uint64_t fido_credman_rk_remaining(const fido_credman_metadata_t *); | ||
69 | |||
70 | void fido_credman_metadata_free(fido_credman_metadata_t **); | ||
71 | void fido_credman_rk_free(fido_credman_rk_t **); | ||
72 | void 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 | |||
15 | eddsa_pk_t *eddsa_pk_new(void); | ||
16 | void eddsa_pk_free(eddsa_pk_t **); | ||
17 | EVP_PKEY *eddsa_pk_to_EVP_PKEY(const eddsa_pk_t *); | ||
18 | |||
19 | int eddsa_pk_from_EVP_PKEY(eddsa_pk_t *, const EVP_PKEY *); | ||
20 | int 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 | ||
26 | int EVP_PKEY_get_raw_public_key(const EVP_PKEY *, unsigned char *, size_t *); | ||
27 | EVP_PKEY *EVP_PKEY_new_raw_public_key(int, ENGINE *, const unsigned char *, | ||
28 | size_t); | ||
29 | int 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 | ||
34 | EVP_MD_CTX *EVP_MD_CTX_new(void); | ||
35 | void 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 | |||
67 | const 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 | |||
15 | es256_pk_t *es256_pk_new(void); | ||
16 | void es256_pk_free(es256_pk_t **); | ||
17 | EVP_PKEY *es256_pk_to_EVP_PKEY(const es256_pk_t *); | ||
18 | |||
19 | int es256_pk_from_EC_KEY(es256_pk_t *, const EC_KEY *); | ||
20 | int es256_pk_from_ptr(es256_pk_t *, const void *, size_t); | ||
21 | |||
22 | #ifdef _FIDO_INTERNAL | ||
23 | es256_sk_t *es256_sk_new(void); | ||
24 | void es256_sk_free(es256_sk_t **); | ||
25 | EVP_PKEY *es256_sk_to_EVP_PKEY(const es256_sk_t *); | ||
26 | |||
27 | int es256_derive_pk(const es256_sk_t *, es256_pk_t *); | ||
28 | int es256_sk_create(es256_sk_t *); | ||
29 | |||
30 | int es256_pk_set_x(es256_pk_t *, const unsigned char *); | ||
31 | int 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 | |||
15 | rs256_pk_t *rs256_pk_new(void); | ||
16 | void rs256_pk_free(rs256_pk_t **); | ||
17 | EVP_PKEY *rs256_pk_to_EVP_PKEY(const rs256_pk_t *); | ||
18 | |||
19 | int rs256_pk_from_RSA(rs256_pk_t *, const RSA *); | ||
20 | int 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 | |||
10 | fido_dev_info_t * | ||
11 | fido_dev_info_new(size_t n) | ||
12 | { | ||
13 | return (calloc(n, sizeof(fido_dev_info_t))); | ||
14 | } | ||
15 | |||
16 | void | ||
17 | fido_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 | |||
36 | const fido_dev_info_t * | ||
37 | fido_dev_info_ptr(const fido_dev_info_t *devlist, size_t i) | ||
38 | { | ||
39 | return (&devlist[i]); | ||
40 | } | ||
41 | |||
42 | const char * | ||
43 | fido_dev_info_path(const fido_dev_info_t *di) | ||
44 | { | ||
45 | return (di->path); | ||
46 | } | ||
47 | |||
48 | int16_t | ||
49 | fido_dev_info_vendor(const fido_dev_info_t *di) | ||
50 | { | ||
51 | return (di->vendor_id); | ||
52 | } | ||
53 | |||
54 | int16_t | ||
55 | fido_dev_info_product(const fido_dev_info_t *di) | ||
56 | { | ||
57 | return (di->product_id); | ||
58 | } | ||
59 | |||
60 | const char * | ||
61 | fido_dev_info_manufacturer_string(const fido_dev_info_t *di) | ||
62 | { | ||
63 | return (di->manufacturer); | ||
64 | } | ||
65 | |||
66 | const char * | ||
67 | fido_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 | |||
21 | static int | ||
22 | get_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 | |||
38 | static int | ||
39 | get_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 | |||
61 | static int | ||
62 | get_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 | |||
98 | static int | ||
99 | get_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; | ||
125 | fail: | ||
126 | if (fd != -1) | ||
127 | close(fd); | ||
128 | |||
129 | return (ok); | ||
130 | } | ||
131 | |||
132 | static bool | ||
133 | is_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 | |||
149 | static int | ||
150 | parse_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 | |||
182 | static int | ||
183 | copy_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; | ||
228 | fail: | ||
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 | |||
242 | int | ||
243 | fido_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; | ||
276 | fail: | ||
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 | |||
285 | void * | ||
286 | fido_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 | |||
299 | void | ||
300 | fido_hid_close(void *handle) | ||
301 | { | ||
302 | int *fd = handle; | ||
303 | |||
304 | close(*fd); | ||
305 | free(fd); | ||
306 | } | ||
307 | |||
308 | int | ||
309 | fido_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 | |||
327 | int | ||
328 | fido_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 | |||
25 | struct hid_openbsd { | ||
26 | int fd; | ||
27 | size_t report_in_len; | ||
28 | size_t report_out_len; | ||
29 | }; | ||
30 | |||
31 | int | ||
32 | fido_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 | */ | ||
127 | static int | ||
128 | terrible_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 | |||
177 | void * | ||
178 | fido_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 | |||
231 | void | ||
232 | fido_hid_close(void *handle) | ||
233 | { | ||
234 | struct hid_openbsd *ctx = (struct hid_openbsd *)handle; | ||
235 | |||
236 | close(ctx->fd); | ||
237 | free(ctx); | ||
238 | } | ||
239 | |||
240 | int | ||
241 | fido_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 | |||
260 | int | ||
261 | fido_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 | |||
24 | struct dev { | ||
25 | IOHIDDeviceRef ref; | ||
26 | CFStringRef loop_id; | ||
27 | }; | ||
28 | |||
29 | static int | ||
30 | get_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 | |||
54 | static int | ||
55 | get_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 | |||
75 | static bool | ||
76 | is_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 | |||
94 | static int | ||
95 | get_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 | |||
118 | static int | ||
119 | get_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; | ||
148 | fail: | ||
149 | if (ok < 0) { | ||
150 | free(*manufacturer); | ||
151 | free(*product); | ||
152 | *manufacturer = NULL; | ||
153 | *product = NULL; | ||
154 | } | ||
155 | |||
156 | return (ok); | ||
157 | } | ||
158 | |||
159 | static char * | ||
160 | get_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 | |||
178 | static int | ||
179 | copy_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 | |||
199 | int | ||
200 | fido_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; | ||
249 | fail: | ||
250 | if (manager != NULL) | ||
251 | CFRelease(manager); | ||
252 | if (devset != NULL) | ||
253 | CFRelease(devset); | ||
254 | |||
255 | free(devs); | ||
256 | |||
257 | return (r); | ||
258 | } | ||
259 | |||
260 | void * | ||
261 | fido_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; | ||
305 | fail: | ||
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 | |||
321 | void | ||
322 | fido_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 | |||
336 | static void | ||
337 | read_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 | |||
350 | static void | ||
351 | removal_callback(void *context, IOReturn result, void *sender) | ||
352 | { | ||
353 | (void)context; | ||
354 | (void)result; | ||
355 | (void)sender; | ||
356 | |||
357 | CFRunLoopStop(CFRunLoopGetCurrent()); | ||
358 | } | ||
359 | |||
360 | int | ||
361 | fido_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 | |||
393 | int | ||
394 | fido_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 | |||
24 | static bool | ||
25 | is_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; | ||
48 | fail: | ||
49 | if (data != NULL) | ||
50 | HidD_FreePreparsedData(data); | ||
51 | |||
52 | return (usage_page == 0xf1d0); | ||
53 | } | ||
54 | |||
55 | static int | ||
56 | get_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 | |||
73 | static int | ||
74 | get_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; | ||
128 | fail: | ||
129 | if (ok < 0) { | ||
130 | free(*manufacturer); | ||
131 | free(*product); | ||
132 | *manufacturer = NULL; | ||
133 | *product = NULL; | ||
134 | } | ||
135 | |||
136 | return (ok); | ||
137 | } | ||
138 | |||
139 | static int | ||
140 | copy_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; | ||
160 | fail: | ||
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 | |||
174 | int | ||
175 | fido_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; | ||
246 | fail: | ||
247 | if (devinfo != INVALID_HANDLE_VALUE) | ||
248 | SetupDiDestroyDeviceInfoList(devinfo); | ||
249 | |||
250 | free(ifdetail); | ||
251 | |||
252 | return (r); | ||
253 | } | ||
254 | |||
255 | void * | ||
256 | fido_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 | |||
270 | void | ||
271 | fido_hid_close(void *handle) | ||
272 | { | ||
273 | CloseHandle(handle); | ||
274 | } | ||
275 | |||
276 | int | ||
277 | fido_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 | |||
301 | fail: | ||
302 | explicit_bzero(report, sizeof(report)); | ||
303 | |||
304 | return (r); | ||
305 | } | ||
306 | |||
307 | int | ||
308 | fido_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 | |||
10 | static int | ||
11 | decode_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 | |||
27 | static int | ||
28 | decode_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 | |||
51 | static int | ||
52 | decode_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 | |||
68 | static int | ||
69 | decode_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 | |||
92 | static int | ||
93 | decode_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 | |||
107 | static int | ||
108 | decode_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 | |||
132 | static int | ||
133 | decode_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 | |||
153 | static int | ||
154 | decode_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 | |||
172 | static int | ||
173 | decode_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 | |||
196 | static int | ||
197 | parse_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 | |||
226 | static int | ||
227 | fido_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 | |||
242 | static int | ||
243 | fido_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 | |||
263 | static int | ||
264 | fido_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 | |||
275 | int | ||
276 | fido_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 | |||
285 | fido_cbor_info_t * | ||
286 | fido_cbor_info_new(void) | ||
287 | { | ||
288 | return (calloc(1, sizeof(fido_cbor_info_t))); | ||
289 | } | ||
290 | |||
291 | static void | ||
292 | free_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 | |||
302 | static void | ||
303 | free_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 | |||
314 | static void | ||
315 | free_byte_array(fido_byte_array_t *ba) | ||
316 | { | ||
317 | free(ba->ptr); | ||
318 | |||
319 | ba->ptr = NULL; | ||
320 | ba->len = 0; | ||
321 | } | ||
322 | |||
323 | void | ||
324 | fido_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 | |||
340 | char ** | ||
341 | fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci) | ||
342 | { | ||
343 | return (ci->versions.ptr); | ||
344 | } | ||
345 | |||
346 | size_t | ||
347 | fido_cbor_info_versions_len(const fido_cbor_info_t *ci) | ||
348 | { | ||
349 | return (ci->versions.len); | ||
350 | } | ||
351 | |||
352 | char ** | ||
353 | fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci) | ||
354 | { | ||
355 | return (ci->extensions.ptr); | ||
356 | } | ||
357 | |||
358 | size_t | ||
359 | fido_cbor_info_extensions_len(const fido_cbor_info_t *ci) | ||
360 | { | ||
361 | return (ci->extensions.len); | ||
362 | } | ||
363 | |||
364 | const unsigned char * | ||
365 | fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci) | ||
366 | { | ||
367 | return (ci->aaguid); | ||
368 | } | ||
369 | |||
370 | size_t | ||
371 | fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci) | ||
372 | { | ||
373 | return (sizeof(ci->aaguid)); | ||
374 | } | ||
375 | |||
376 | char ** | ||
377 | fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci) | ||
378 | { | ||
379 | return (ci->options.name); | ||
380 | } | ||
381 | |||
382 | const bool * | ||
383 | fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci) | ||
384 | { | ||
385 | return (ci->options.value); | ||
386 | } | ||
387 | |||
388 | size_t | ||
389 | fido_cbor_info_options_len(const fido_cbor_info_t *ci) | ||
390 | { | ||
391 | return (ci->options.len); | ||
392 | } | ||
393 | |||
394 | uint64_t | ||
395 | fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci) | ||
396 | { | ||
397 | return (ci->maxmsgsiz); | ||
398 | } | ||
399 | |||
400 | const uint8_t * | ||
401 | fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci) | ||
402 | { | ||
403 | return (ci->protocols.ptr); | ||
404 | } | ||
405 | |||
406 | size_t | ||
407 | fido_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 | |||
14 | PACKED_TYPE(frame_t, | ||
15 | struct 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 | |||
36 | static size_t | ||
37 | tx_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 | |||
63 | static size_t | ||
64 | tx_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 | |||
87 | int | ||
88 | fido_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 | |||
125 | static int | ||
126 | rx_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 | |||
140 | static int | ||
141 | rx_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 | |||
155 | int | ||
156 | fido_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 | |||
242 | int | ||
243 | fido_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 | |||
10 | iso7816_apdu_t * | ||
11 | iso7816_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 | |||
32 | void | ||
33 | iso7816_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 | |||
46 | int | ||
47 | iso7816_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 | |||
59 | const unsigned char * | ||
60 | iso7816_ptr(const iso7816_apdu_t *apdu) | ||
61 | { | ||
62 | return ((const unsigned char *)&apdu->header); | ||
63 | } | ||
64 | |||
65 | size_t | ||
66 | iso7816_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 | |||
12 | PACKED_TYPE(iso7816_header_t, | ||
13 | struct 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 | |||
23 | PACKED_TYPE(iso7816_apdu_t, | ||
24 | struct 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 | |||
32 | const unsigned char *iso7816_ptr(const iso7816_apdu_t *); | ||
33 | int iso7816_add(iso7816_apdu_t *, const void *, size_t); | ||
34 | iso7816_apdu_t *iso7816_new(uint8_t, uint8_t, uint16_t); | ||
35 | size_t iso7816_len(const iso7816_apdu_t *); | ||
36 | void 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 @@ | |||
1 | prefix=@CMAKE_INSTALL_PREFIX@ | ||
2 | exec_prefix=${prefix} | ||
3 | libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ | ||
4 | includedir=${prefix}/include | ||
5 | |||
6 | Name: @PROJECT_NAME@ | ||
7 | Description: A FIDO2 library | ||
8 | URL: https://github.com/yubico/libfido2 | ||
9 | Version: @FIDO_VERSION@ | ||
10 | Requires: libcrypto | ||
11 | Libs: -L${libdir} -lfido2 | ||
12 | Cflags: -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 | |||
18 | static TLS int logging; | ||
19 | |||
20 | void | ||
21 | fido_log_init(void) | ||
22 | { | ||
23 | logging = 1; | ||
24 | } | ||
25 | |||
26 | void | ||
27 | fido_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 | |||
47 | void | ||
48 | fido_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 | |||
11 | static int | ||
12 | parse_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 | |||
26 | static int | ||
27 | fido_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; | ||
62 | fail: | ||
63 | cbor_vector_free(argv, nitems(argv)); | ||
64 | fido_blob_free(&p); | ||
65 | free(f.ptr); | ||
66 | |||
67 | return (r); | ||
68 | } | ||
69 | |||
70 | static int | ||
71 | fido_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; | ||
104 | fail: | ||
105 | fido_blob_free(&aes_token); | ||
106 | |||
107 | return (r); | ||
108 | } | ||
109 | |||
110 | static int | ||
111 | fido_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 | |||
123 | int | ||
124 | fido_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 | |||
130 | static int | ||
131 | pad64(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 | |||
157 | static int | ||
158 | fido_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; | ||
207 | fail: | ||
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 | |||
219 | static int | ||
220 | fido_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; | ||
260 | fail: | ||
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 | |||
270 | static int | ||
271 | fido_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 | |||
296 | int | ||
297 | fido_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 | |||
302 | static int | ||
303 | parse_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 | |||
325 | static int | ||
326 | fido_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; | ||
349 | fail: | ||
350 | cbor_vector_free(argv, nitems(argv)); | ||
351 | free(f.ptr); | ||
352 | |||
353 | return (r); | ||
354 | } | ||
355 | |||
356 | static int | ||
357 | fido_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 | |||
380 | static int | ||
381 | fido_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 | |||
392 | int | ||
393 | fido_dev_get_retry_count(fido_dev_t *dev, int *retries) | ||
394 | { | ||
395 | return (fido_dev_get_retry_count_wait(dev, retries, -1)); | ||
396 | } | ||
397 | |||
398 | int | ||
399 | cbor_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; | ||
424 | fail: | ||
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 | |||
10 | static int | ||
11 | fido_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 | |||
24 | static int | ||
25 | fido_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 | |||
36 | int | ||
37 | fido_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 | ||
17 | static int | ||
18 | RSA_bits(const RSA *r) | ||
19 | { | ||
20 | return (BN_num_bits(r->n)); | ||
21 | } | ||
22 | |||
23 | static int | ||
24 | RSA_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 | |||
33 | static void | ||
34 | RSA_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 | |||
42 | static int | ||
43 | decode_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 | |||
57 | static int | ||
58 | decode_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 | |||
76 | int | ||
77 | rs256_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 | |||
89 | rs256_pk_t * | ||
90 | rs256_pk_new(void) | ||
91 | { | ||
92 | return (calloc(1, sizeof(rs256_pk_t))); | ||
93 | } | ||
94 | |||
95 | void | ||
96 | rs256_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 | |||
109 | int | ||
110 | rs256_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 | |||
120 | EVP_PKEY * | ||
121 | rs256_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; | ||
156 | fail: | ||
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 | |||
171 | int | ||
172 | rs256_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 */ | ||
13 | typedef 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 */ | ||
19 | typedef 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 */ | ||
24 | typedef struct rs256_pk { | ||
25 | unsigned char n[256]; | ||
26 | unsigned char e[3]; | ||
27 | } rs256_pk_t; | ||
28 | |||
29 | /* COSE EDDSA (ED25519) */ | ||
30 | typedef struct eddsa_pk { | ||
31 | unsigned char x[32]; | ||
32 | } eddsa_pk_t; | ||
33 | |||
34 | PACKED_TYPE(fido_authdata_t, | ||
35 | struct 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 | |||
42 | PACKED_TYPE(fido_attcred_raw_t, | ||
43 | struct 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 | |||
49 | typedef 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 | |||
60 | typedef struct fido_attstmt { | ||
61 | fido_blob_t x5c; /* attestation certificate */ | ||
62 | fido_blob_t sig; /* attestation signature */ | ||
63 | } fido_attstmt_t; | ||
64 | |||
65 | typedef struct fido_rp { | ||
66 | char *id; /* relying party id */ | ||
67 | char *name; /* relying party name */ | ||
68 | } fido_rp_t; | ||
69 | |||
70 | typedef 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 | |||
77 | typedef 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 | |||
94 | typedef 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 | |||
105 | typedef 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 | |||
118 | typedef struct fido_opt_array { | ||
119 | char **name; | ||
120 | bool *value; | ||
121 | size_t len; | ||
122 | } fido_opt_array_t; | ||
123 | |||
124 | typedef struct fido_str_array { | ||
125 | char **ptr; | ||
126 | size_t len; | ||
127 | } fido_str_array_t; | ||
128 | |||
129 | typedef struct fido_byte_array { | ||
130 | uint8_t *ptr; | ||
131 | size_t len; | ||
132 | } fido_byte_array_t; | ||
133 | |||
134 | typedef 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 | |||
143 | typedef 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 | |||
151 | PACKED_TYPE(fido_ctap_info_t, | ||
152 | /* defined in section 8.1.9.1.3 (CTAPHID_INIT) of the fido2 ctap spec */ | ||
153 | struct 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 | |||
163 | typedef 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) | ||
19 | static int | ||
20 | usleep(unsigned int usec) | ||
21 | { | ||
22 | Sleep(usec / 1000); | ||
23 | |||
24 | return (0); | ||
25 | } | ||
26 | #endif | ||
27 | |||
28 | static int | ||
29 | sig_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 | |||
47 | static int | ||
48 | x5c_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; | ||
74 | fail: | ||
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 | |||
87 | static int | ||
88 | authdata_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 | |||
125 | static int | ||
126 | send_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; | ||
172 | fail: | ||
173 | iso7816_free(&apdu); | ||
174 | |||
175 | return (r); | ||
176 | } | ||
177 | |||
178 | static int | ||
179 | key_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; | ||
245 | fail: | ||
246 | iso7816_free(&apdu); | ||
247 | |||
248 | return (r); | ||
249 | } | ||
250 | |||
251 | static int | ||
252 | parse_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 | |||
284 | static int | ||
285 | do_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 | |||
354 | fail: | ||
355 | iso7816_free(&apdu); | ||
356 | |||
357 | return (r); | ||
358 | } | ||
359 | |||
360 | static int | ||
361 | cbor_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; | ||
394 | fail: | ||
395 | es256_pk_free(&pk); | ||
396 | |||
397 | if (pk_cbor) | ||
398 | cbor_decref(&pk_cbor); | ||
399 | |||
400 | return (ok); | ||
401 | } | ||
402 | |||
403 | static int | ||
404 | encode_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; | ||
473 | fail: | ||
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 | |||
489 | static int | ||
490 | parse_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; | ||
554 | fail: | ||
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 | |||
575 | int | ||
576 | u2f_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 | } | ||
661 | fail: | ||
662 | iso7816_free(&apdu); | ||
663 | |||
664 | return (r); | ||
665 | } | ||
666 | |||
667 | static int | ||
668 | u2f_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; | ||
711 | fail: | ||
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 | |||
724 | int | ||
725 | u2f_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 | } | ||