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 /tools |
Import upstream release 1.3.0
Closes: #951184
Diffstat (limited to 'tools')
-rw-r--r-- | tools/CMakeLists.txt | 65 | ||||
-rw-r--r-- | tools/assert_get.c | 233 | ||||
-rw-r--r-- | tools/assert_verify.c | 201 | ||||
-rw-r--r-- | tools/base64.c | 135 | ||||
-rw-r--r-- | tools/bio.c | 270 | ||||
-rw-r--r-- | tools/cred_make.c | 221 | ||||
-rw-r--r-- | tools/cred_verify.c | 177 | ||||
-rw-r--r-- | tools/credman.c | 237 | ||||
-rw-r--r-- | tools/extern.h | 64 | ||||
-rw-r--r-- | tools/fido2-assert.c | 54 | ||||
-rw-r--r-- | tools/fido2-cred.c | 52 | ||||
-rw-r--r-- | tools/fido2-token.c | 95 | ||||
-rw-r--r-- | tools/pin.c | 146 | ||||
-rw-r--r-- | tools/sk-libfido2.c | 784 | ||||
-rwxr-xr-x | tools/test.sh | 96 | ||||
-rw-r--r-- | tools/token.c | 364 | ||||
-rw-r--r-- | tools/util.c | 364 |
17 files changed, 3558 insertions, 0 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..5f27e88 --- /dev/null +++ b/tools/CMakeLists.txt | |||
@@ -0,0 +1,65 @@ | |||
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 | list(APPEND COMPAT_SOURCES | ||
6 | ../openbsd-compat/explicit_bzero.c | ||
7 | ../openbsd-compat/strlcpy.c | ||
8 | ../openbsd-compat/strlcat.c | ||
9 | ) | ||
10 | |||
11 | if(WIN32) | ||
12 | list(APPEND COMPAT_SOURCES | ||
13 | ../openbsd-compat/bsd-getline.c | ||
14 | ../openbsd-compat/explicit_bzero_win32.c | ||
15 | ../openbsd-compat/getopt_long.c | ||
16 | ../openbsd-compat/posix_win.c | ||
17 | ../openbsd-compat/readpassphrase_win32.c | ||
18 | ) | ||
19 | else() | ||
20 | list(APPEND COMPAT_SOURCES ../openbsd-compat/readpassphrase.c) | ||
21 | endif() | ||
22 | |||
23 | add_executable(fido2-cred | ||
24 | fido2-cred.c | ||
25 | cred_make.c | ||
26 | cred_verify.c | ||
27 | base64.c | ||
28 | util.c | ||
29 | ${COMPAT_SOURCES} | ||
30 | ) | ||
31 | |||
32 | add_executable(fido2-assert | ||
33 | fido2-assert.c | ||
34 | assert_get.c | ||
35 | assert_verify.c | ||
36 | base64.c | ||
37 | util.c | ||
38 | ${COMPAT_SOURCES} | ||
39 | ) | ||
40 | |||
41 | add_executable(fido2-token | ||
42 | fido2-token.c | ||
43 | base64.c | ||
44 | bio.c | ||
45 | credman.c | ||
46 | pin.c | ||
47 | token.c | ||
48 | util.c | ||
49 | ${COMPAT_SOURCES} | ||
50 | ) | ||
51 | |||
52 | add_library(sk-libfido2 MODULE sk-libfido2.c) | ||
53 | set_target_properties(sk-libfido2 PROPERTIES | ||
54 | COMPILE_FLAGS "-DSK_STANDALONE -DWITH_OPENSSL" | ||
55 | OUTPUT_NAME sk-libfido2 | ||
56 | ) | ||
57 | |||
58 | target_link_libraries(fido2-cred ${CRYPTO_LIBRARIES} fido2_shared) | ||
59 | target_link_libraries(fido2-assert ${CRYPTO_LIBRARIES} fido2_shared) | ||
60 | target_link_libraries(fido2-token ${CRYPTO_LIBRARIES} fido2_shared) | ||
61 | target_link_libraries(sk-libfido2 ${CRYPTO_LIBRARIES} fido2_shared) | ||
62 | |||
63 | install(TARGETS fido2-cred fido2-assert fido2-token | ||
64 | DESTINATION ${CMAKE_INSTALL_BINDIR}) | ||
65 | install(TARGETS sk-libfido2 DESTINATION ${CMAKE_INSTALL_LIBDIR}) | ||
diff --git a/tools/assert_get.c b/tools/assert_get.c new file mode 100644 index 0000000..5e209cd --- /dev/null +++ b/tools/assert_get.c | |||
@@ -0,0 +1,233 @@ | |||
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.h> | ||
8 | #include <stdio.h> | ||
9 | #include <stdlib.h> | ||
10 | #include <string.h> | ||
11 | #ifdef HAVE_UNISTD_H | ||
12 | #include <unistd.h> | ||
13 | #endif | ||
14 | |||
15 | #include "../openbsd-compat/openbsd-compat.h" | ||
16 | #include "extern.h" | ||
17 | |||
18 | static fido_assert_t * | ||
19 | prepare_assert(FILE *in_f, int flags) | ||
20 | { | ||
21 | fido_assert_t *assert = NULL; | ||
22 | struct blob cdh; | ||
23 | struct blob id; | ||
24 | struct blob hmac_salt; | ||
25 | char *rpid = NULL; | ||
26 | int r; | ||
27 | |||
28 | memset(&cdh, 0, sizeof(cdh)); | ||
29 | memset(&id, 0, sizeof(id)); | ||
30 | memset(&hmac_salt, 0, sizeof(hmac_salt)); | ||
31 | |||
32 | r = base64_read(in_f, &cdh); | ||
33 | r |= string_read(in_f, &rpid); | ||
34 | if ((flags & FLAG_RK) == 0) | ||
35 | r |= base64_read(in_f, &id); | ||
36 | if (flags & FLAG_HMAC) | ||
37 | r |= base64_read(in_f, &hmac_salt); | ||
38 | if (r < 0) | ||
39 | errx(1, "input error"); | ||
40 | |||
41 | if (flags & FLAG_DEBUG) { | ||
42 | fprintf(stderr, "client data hash:\n"); | ||
43 | xxd(cdh.ptr, cdh.len); | ||
44 | fprintf(stderr, "relying party id: %s\n", rpid); | ||
45 | if ((flags & FLAG_RK) == 0) { | ||
46 | fprintf(stderr, "credential id:\n"); | ||
47 | xxd(id.ptr, id.len); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | if ((assert = fido_assert_new()) == NULL) | ||
52 | errx(1, "fido_assert_new"); | ||
53 | |||
54 | if ((r = fido_assert_set_clientdata_hash(assert, cdh.ptr, | ||
55 | cdh.len)) != FIDO_OK || | ||
56 | (r = fido_assert_set_rp(assert, rpid)) != FIDO_OK) | ||
57 | errx(1, "fido_assert_set: %s", fido_strerr(r)); | ||
58 | |||
59 | if (flags & FLAG_UP) { | ||
60 | if ((r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK) | ||
61 | errx(1, "fido_assert_set_up: %s", fido_strerr(r)); | ||
62 | } | ||
63 | if (flags & FLAG_UV) { | ||
64 | if ((r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK) | ||
65 | errx(1, "fido_assert_set_uv: %s", fido_strerr(r)); | ||
66 | } | ||
67 | if (flags & FLAG_HMAC) { | ||
68 | if ((r = fido_assert_set_extensions(assert, | ||
69 | FIDO_EXT_HMAC_SECRET)) != FIDO_OK) | ||
70 | errx(1, "fido_assert_set_extensions: %s", | ||
71 | fido_strerr(r)); | ||
72 | if ((r = fido_assert_set_hmac_salt(assert, hmac_salt.ptr, | ||
73 | hmac_salt.len)) != FIDO_OK) | ||
74 | errx(1, "fido_assert_set_hmac_salt: %s", | ||
75 | fido_strerr(r)); | ||
76 | } | ||
77 | if ((flags & FLAG_RK) == 0) { | ||
78 | if ((r = fido_assert_allow_cred(assert, id.ptr, | ||
79 | id.len)) != FIDO_OK) | ||
80 | errx(1, "fido_assert_allow_cred: %s", fido_strerr(r)); | ||
81 | } | ||
82 | |||
83 | free(hmac_salt.ptr); | ||
84 | free(cdh.ptr); | ||
85 | free(id.ptr); | ||
86 | free(rpid); | ||
87 | |||
88 | return (assert); | ||
89 | } | ||
90 | |||
91 | static void | ||
92 | print_assert(FILE *out_f, const fido_assert_t *assert, size_t idx, int flags) | ||
93 | { | ||
94 | char *cdh = NULL; | ||
95 | char *authdata = NULL; | ||
96 | char *sig = NULL; | ||
97 | char *user_id = NULL; | ||
98 | char *hmac_secret = NULL; | ||
99 | int r; | ||
100 | |||
101 | r = base64_encode(fido_assert_clientdata_hash_ptr(assert), | ||
102 | fido_assert_clientdata_hash_len(assert), &cdh); | ||
103 | r |= base64_encode(fido_assert_authdata_ptr(assert, idx), | ||
104 | fido_assert_authdata_len(assert, 0), &authdata); | ||
105 | r |= base64_encode(fido_assert_sig_ptr(assert, idx), | ||
106 | fido_assert_sig_len(assert, idx), &sig); | ||
107 | if (flags & FLAG_RK) | ||
108 | r |= base64_encode(fido_assert_user_id_ptr(assert, idx), | ||
109 | fido_assert_user_id_len(assert, idx), &user_id); | ||
110 | if (flags & FLAG_HMAC) | ||
111 | r |= base64_encode(fido_assert_hmac_secret_ptr(assert, idx), | ||
112 | fido_assert_hmac_secret_len(assert, idx), &hmac_secret); | ||
113 | if (r < 0) | ||
114 | errx(1, "output error"); | ||
115 | |||
116 | fprintf(out_f, "%s\n", cdh); | ||
117 | fprintf(out_f, "%s\n", fido_assert_rp_id(assert)); | ||
118 | fprintf(out_f, "%s\n", authdata); | ||
119 | fprintf(out_f, "%s\n", sig); | ||
120 | if (flags & FLAG_RK) | ||
121 | fprintf(out_f, "%s\n", user_id); | ||
122 | if (hmac_secret) { | ||
123 | fprintf(out_f, "%s\n", hmac_secret); | ||
124 | explicit_bzero(hmac_secret, strlen(hmac_secret)); | ||
125 | } | ||
126 | |||
127 | free(hmac_secret); | ||
128 | free(cdh); | ||
129 | free(authdata); | ||
130 | free(sig); | ||
131 | free(user_id); | ||
132 | } | ||
133 | |||
134 | int | ||
135 | assert_get(int argc, char **argv) | ||
136 | { | ||
137 | fido_dev_t *dev = NULL; | ||
138 | fido_assert_t *assert = NULL; | ||
139 | char pin[1024]; | ||
140 | char prompt[1024]; | ||
141 | char *in_path = NULL; | ||
142 | char *out_path = NULL; | ||
143 | FILE *in_f = NULL; | ||
144 | FILE *out_f = NULL; | ||
145 | int flags = 0; | ||
146 | int ch; | ||
147 | int r; | ||
148 | |||
149 | while ((ch = getopt(argc, argv, "dhi:o:pruv")) != -1) { | ||
150 | switch (ch) { | ||
151 | case 'd': | ||
152 | flags |= FLAG_DEBUG; | ||
153 | break; | ||
154 | case 'h': | ||
155 | flags |= FLAG_HMAC; | ||
156 | break; | ||
157 | case 'i': | ||
158 | in_path = optarg; | ||
159 | break; | ||
160 | case 'o': | ||
161 | out_path = optarg; | ||
162 | break; | ||
163 | case 'p': | ||
164 | flags |= FLAG_UP; | ||
165 | break; | ||
166 | case 'r': | ||
167 | flags |= FLAG_RK; | ||
168 | break; | ||
169 | case 'u': | ||
170 | flags |= FLAG_U2F; | ||
171 | break; | ||
172 | case 'v': | ||
173 | flags |= FLAG_UV; | ||
174 | break; | ||
175 | default: | ||
176 | usage(); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | argc -= optind; | ||
181 | argv += optind; | ||
182 | |||
183 | if (argc < 1) | ||
184 | usage(); | ||
185 | |||
186 | in_f = open_read(in_path); | ||
187 | out_f = open_write(out_path); | ||
188 | |||
189 | fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0); | ||
190 | |||
191 | assert = prepare_assert(in_f, flags); | ||
192 | |||
193 | dev = open_dev(argv[0]); | ||
194 | if (flags & FLAG_U2F) | ||
195 | fido_dev_force_u2f(dev); | ||
196 | |||
197 | if (flags & FLAG_UV) { | ||
198 | r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", | ||
199 | argv[0]); | ||
200 | if (r < 0 || (size_t)r >= sizeof(prompt)) | ||
201 | errx(1, "snprintf"); | ||
202 | if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF)) | ||
203 | errx(1, "readpassphrase"); | ||
204 | r = fido_dev_get_assert(dev, assert, pin); | ||
205 | } else | ||
206 | r = fido_dev_get_assert(dev, assert, NULL); | ||
207 | |||
208 | explicit_bzero(pin, sizeof(pin)); | ||
209 | |||
210 | if (r != FIDO_OK) | ||
211 | errx(1, "fido_dev_get_assert: %s", fido_strerr(r)); | ||
212 | |||
213 | if (flags & FLAG_RK) { | ||
214 | for (size_t idx = 0; idx < fido_assert_count(assert); idx++) | ||
215 | print_assert(out_f, assert, idx, flags); | ||
216 | } else { | ||
217 | if (fido_assert_count(assert) != 1) | ||
218 | errx(1, "fido_assert_count: %zu", | ||
219 | fido_assert_count(assert)); | ||
220 | print_assert(out_f, assert, 0, flags); | ||
221 | } | ||
222 | |||
223 | fido_dev_close(dev); | ||
224 | fido_dev_free(&dev); | ||
225 | fido_assert_free(&assert); | ||
226 | |||
227 | fclose(in_f); | ||
228 | fclose(out_f); | ||
229 | in_f = NULL; | ||
230 | out_f = NULL; | ||
231 | |||
232 | exit(0); | ||
233 | } | ||
diff --git a/tools/assert_verify.c b/tools/assert_verify.c new file mode 100644 index 0000000..ccff57a --- /dev/null +++ b/tools/assert_verify.c | |||
@@ -0,0 +1,201 @@ | |||
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.h> | ||
8 | #include <fido/es256.h> | ||
9 | #include <fido/rs256.h> | ||
10 | #include <fido/eddsa.h> | ||
11 | |||
12 | #include <stdbool.h> | ||
13 | #include <stdio.h> | ||
14 | #include <stdlib.h> | ||
15 | #include <string.h> | ||
16 | #ifdef HAVE_UNISTD_H | ||
17 | #include <unistd.h> | ||
18 | #endif | ||
19 | |||
20 | #include "../openbsd-compat/openbsd-compat.h" | ||
21 | #include "extern.h" | ||
22 | |||
23 | static fido_assert_t * | ||
24 | prepare_assert(FILE *in_f, int flags) | ||
25 | { | ||
26 | fido_assert_t *assert = NULL; | ||
27 | struct blob cdh; | ||
28 | struct blob authdata; | ||
29 | struct blob sig; | ||
30 | char *rpid = NULL; | ||
31 | int r; | ||
32 | |||
33 | memset(&cdh, 0, sizeof(cdh)); | ||
34 | memset(&authdata, 0, sizeof(authdata)); | ||
35 | memset(&sig, 0, sizeof(sig)); | ||
36 | |||
37 | r = base64_read(in_f, &cdh); | ||
38 | r |= string_read(in_f, &rpid); | ||
39 | r |= base64_read(in_f, &authdata); | ||
40 | r |= base64_read(in_f, &sig); | ||
41 | if (r < 0) | ||
42 | errx(1, "input error"); | ||
43 | |||
44 | if (flags & FLAG_DEBUG) { | ||
45 | fprintf(stderr, "client data hash:\n"); | ||
46 | xxd(cdh.ptr, cdh.len); | ||
47 | fprintf(stderr, "relying party id: %s\n", rpid); | ||
48 | fprintf(stderr, "authenticator data:\n"); | ||
49 | xxd(authdata.ptr, authdata.len); | ||
50 | fprintf(stderr, "signature:\n"); | ||
51 | xxd(sig.ptr, sig.len); | ||
52 | } | ||
53 | |||
54 | if ((assert = fido_assert_new()) == NULL) | ||
55 | errx(1, "fido_assert_new"); | ||
56 | if ((r = fido_assert_set_count(assert, 1)) != FIDO_OK) | ||
57 | errx(1, "fido_assert_count: %s", fido_strerr(r)); | ||
58 | |||
59 | if ((r = fido_assert_set_clientdata_hash(assert, cdh.ptr, | ||
60 | cdh.len)) != FIDO_OK || | ||
61 | (r = fido_assert_set_rp(assert, rpid)) != FIDO_OK || | ||
62 | (r = fido_assert_set_authdata(assert, 0, authdata.ptr, | ||
63 | authdata.len)) != FIDO_OK || | ||
64 | (r = fido_assert_set_sig(assert, 0, sig.ptr, sig.len)) != FIDO_OK) | ||
65 | errx(1, "fido_assert_set: %s", fido_strerr(r)); | ||
66 | |||
67 | if (flags & FLAG_UP) { | ||
68 | if ((r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK) | ||
69 | errx(1, "fido_assert_set_up: %s", fido_strerr(r)); | ||
70 | } | ||
71 | if (flags & FLAG_UV) { | ||
72 | if ((r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK) | ||
73 | errx(1, "fido_assert_set_uv: %s", fido_strerr(r)); | ||
74 | } | ||
75 | if (flags & FLAG_HMAC) { | ||
76 | if ((r = fido_assert_set_extensions(assert, | ||
77 | FIDO_EXT_HMAC_SECRET)) != FIDO_OK) | ||
78 | errx(1, "fido_assert_set_extensions: %s", | ||
79 | fido_strerr(r)); | ||
80 | } | ||
81 | |||
82 | free(cdh.ptr); | ||
83 | free(authdata.ptr); | ||
84 | free(sig.ptr); | ||
85 | free(rpid); | ||
86 | |||
87 | return (assert); | ||
88 | } | ||
89 | |||
90 | static void * | ||
91 | load_pubkey(int type, const char *file) | ||
92 | { | ||
93 | EC_KEY *ec = NULL; | ||
94 | RSA *rsa = NULL; | ||
95 | EVP_PKEY *eddsa = NULL; | ||
96 | es256_pk_t *es256_pk = NULL; | ||
97 | rs256_pk_t *rs256_pk = NULL; | ||
98 | eddsa_pk_t *eddsa_pk = NULL; | ||
99 | void *pk = NULL; | ||
100 | |||
101 | if (type == COSE_ES256) { | ||
102 | if ((ec = read_ec_pubkey(file)) == NULL) | ||
103 | errx(1, "read_ec_pubkey"); | ||
104 | if ((es256_pk = es256_pk_new()) == NULL) | ||
105 | errx(1, "es256_pk_new"); | ||
106 | if (es256_pk_from_EC_KEY(es256_pk, ec) != FIDO_OK) | ||
107 | errx(1, "es256_pk_from_EC_KEY"); | ||
108 | |||
109 | pk = es256_pk; | ||
110 | EC_KEY_free(ec); | ||
111 | } else if (type == COSE_RS256) { | ||
112 | if ((rsa = read_rsa_pubkey(file)) == NULL) | ||
113 | errx(1, "read_rsa_pubkey"); | ||
114 | if ((rs256_pk = rs256_pk_new()) == NULL) | ||
115 | errx(1, "rs256_pk_new"); | ||
116 | if (rs256_pk_from_RSA(rs256_pk, rsa) != FIDO_OK) | ||
117 | errx(1, "rs256_pk_from_RSA"); | ||
118 | |||
119 | pk = rs256_pk; | ||
120 | RSA_free(rsa); | ||
121 | } else if (type == COSE_EDDSA) { | ||
122 | if ((eddsa = read_eddsa_pubkey(file)) == NULL) | ||
123 | errx(1, "read_eddsa_pubkey"); | ||
124 | if ((eddsa_pk = eddsa_pk_new()) == NULL) | ||
125 | errx(1, "eddsa_pk_new"); | ||
126 | if (eddsa_pk_from_EVP_PKEY(eddsa_pk, eddsa) != FIDO_OK) | ||
127 | errx(1, "eddsa_pk_from_EVP_PKEY"); | ||
128 | |||
129 | pk = eddsa_pk; | ||
130 | EVP_PKEY_free(eddsa); | ||
131 | } | ||
132 | |||
133 | return (pk); | ||
134 | } | ||
135 | |||
136 | int | ||
137 | assert_verify(int argc, char **argv) | ||
138 | { | ||
139 | fido_assert_t *assert = NULL; | ||
140 | void *pk = NULL; | ||
141 | char *in_path = NULL; | ||
142 | FILE *in_f = NULL; | ||
143 | int type = COSE_ES256; | ||
144 | int flags = 0; | ||
145 | int ch; | ||
146 | int r; | ||
147 | |||
148 | while ((ch = getopt(argc, argv, "dhi:pv")) != -1) { | ||
149 | switch (ch) { | ||
150 | case 'd': | ||
151 | flags |= FLAG_DEBUG; | ||
152 | break; | ||
153 | case 'h': | ||
154 | flags |= FLAG_HMAC; | ||
155 | break; | ||
156 | case 'i': | ||
157 | in_path = optarg; | ||
158 | break; | ||
159 | case 'p': | ||
160 | flags |= FLAG_UP; | ||
161 | break; | ||
162 | case 'v': | ||
163 | flags |= FLAG_UV; | ||
164 | break; | ||
165 | default: | ||
166 | usage(); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | argc -= optind; | ||
171 | argv += optind; | ||
172 | |||
173 | if (argc < 1 || argc > 2) | ||
174 | usage(); | ||
175 | |||
176 | in_f = open_read(in_path); | ||
177 | |||
178 | if (argc > 1) { | ||
179 | if (strcmp(argv[1], "es256") == 0) | ||
180 | type = COSE_ES256; | ||
181 | else if (strcmp(argv[1], "rs256") == 0) | ||
182 | type = COSE_RS256; | ||
183 | else if (strcmp(argv[1], "eddsa") == 0) | ||
184 | type = COSE_EDDSA; | ||
185 | else | ||
186 | errx(1, "unknown type %s", argv[1]); | ||
187 | } | ||
188 | |||
189 | fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0); | ||
190 | |||
191 | pk = load_pubkey(type, argv[0]); | ||
192 | assert = prepare_assert(in_f, flags); | ||
193 | if ((r = fido_assert_verify(assert, 0, type, pk)) != FIDO_OK) | ||
194 | errx(1, "fido_assert_verify: %s", fido_strerr(r)); | ||
195 | fido_assert_free(&assert); | ||
196 | |||
197 | fclose(in_f); | ||
198 | in_f = NULL; | ||
199 | |||
200 | exit(0); | ||
201 | } | ||
diff --git a/tools/base64.c b/tools/base64.c new file mode 100644 index 0000000..9f31def --- /dev/null +++ b/tools/base64.c | |||
@@ -0,0 +1,135 @@ | |||
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/bio.h> | ||
8 | #include <openssl/evp.h> | ||
9 | |||
10 | #include <fido.h> | ||
11 | #include <limits.h> | ||
12 | #include <stdint.h> | ||
13 | #include <string.h> | ||
14 | |||
15 | #include "../openbsd-compat/openbsd-compat.h" | ||
16 | #include "extern.h" | ||
17 | |||
18 | int | ||
19 | base64_encode(const void *ptr, size_t len, char **out) | ||
20 | { | ||
21 | BIO *bio_b64 = NULL; | ||
22 | BIO *bio_mem = NULL; | ||
23 | char *b64_ptr = NULL; | ||
24 | long b64_len; | ||
25 | int n; | ||
26 | int ok = -1; | ||
27 | |||
28 | if (ptr == NULL || out == NULL || len > INT_MAX) | ||
29 | return (-1); | ||
30 | |||
31 | *out = NULL; | ||
32 | |||
33 | if ((bio_b64 = BIO_new(BIO_f_base64())) == NULL) | ||
34 | goto fail; | ||
35 | if ((bio_mem = BIO_new(BIO_s_mem())) == NULL) | ||
36 | goto fail; | ||
37 | |||
38 | BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL); | ||
39 | BIO_push(bio_b64, bio_mem); | ||
40 | |||
41 | n = BIO_write(bio_b64, ptr, (int)len); | ||
42 | if (n < 0 || (size_t)n != len) | ||
43 | goto fail; | ||
44 | |||
45 | if (BIO_flush(bio_b64) < 0) | ||
46 | goto fail; | ||
47 | |||
48 | b64_len = BIO_get_mem_data(bio_b64, &b64_ptr); | ||
49 | if (b64_len < 0 || (size_t)b64_len == SIZE_MAX || b64_ptr == NULL) | ||
50 | goto fail; | ||
51 | if ((*out = calloc(1, (size_t)b64_len + 1)) == NULL) | ||
52 | goto fail; | ||
53 | |||
54 | memcpy(*out, b64_ptr, (size_t)b64_len); | ||
55 | ok = 0; | ||
56 | |||
57 | fail: | ||
58 | BIO_free(bio_b64); | ||
59 | BIO_free(bio_mem); | ||
60 | |||
61 | return (ok); | ||
62 | } | ||
63 | |||
64 | int | ||
65 | base64_decode(char *in, void **ptr, size_t *len) | ||
66 | { | ||
67 | BIO *bio_mem = NULL; | ||
68 | BIO *bio_b64 = NULL; | ||
69 | size_t alloc_len; | ||
70 | int n; | ||
71 | int ok = -1; | ||
72 | |||
73 | if (in == NULL || ptr == NULL || len == NULL || strlen(in) > INT_MAX) | ||
74 | return (-1); | ||
75 | |||
76 | *ptr = NULL; | ||
77 | *len = 0; | ||
78 | |||
79 | if ((bio_b64 = BIO_new(BIO_f_base64())) == NULL) | ||
80 | goto fail; | ||
81 | if ((bio_mem = BIO_new_mem_buf((void *)in, -1)) == NULL) | ||
82 | goto fail; | ||
83 | |||
84 | BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL); | ||
85 | BIO_push(bio_b64, bio_mem); | ||
86 | |||
87 | alloc_len = strlen(in); | ||
88 | if ((*ptr = calloc(1, alloc_len)) == NULL) | ||
89 | goto fail; | ||
90 | |||
91 | n = BIO_read(bio_b64, *ptr, (int)alloc_len); | ||
92 | if (n <= 0 || BIO_eof(bio_b64) == 0) | ||
93 | goto fail; | ||
94 | |||
95 | *len = (size_t)n; | ||
96 | ok = 0; | ||
97 | |||
98 | fail: | ||
99 | BIO_free(bio_b64); | ||
100 | BIO_free(bio_mem); | ||
101 | |||
102 | if (ok < 0) { | ||
103 | free(*ptr); | ||
104 | *ptr = NULL; | ||
105 | *len = 0; | ||
106 | } | ||
107 | |||
108 | return (ok); | ||
109 | } | ||
110 | |||
111 | int | ||
112 | base64_read(FILE *f, struct blob *out) | ||
113 | { | ||
114 | char *line = NULL; | ||
115 | size_t linesize = 0; | ||
116 | ssize_t n; | ||
117 | |||
118 | out->ptr = NULL; | ||
119 | out->len = 0; | ||
120 | |||
121 | if ((n = getline(&line, &linesize, f)) <= 0 || | ||
122 | (size_t)n != strlen(line)) { | ||
123 | free(line); /* XXX should be free'd _even_ if getline() fails */ | ||
124 | return (-1); | ||
125 | } | ||
126 | |||
127 | if (base64_decode(line, (void **)&out->ptr, &out->len) < 0) { | ||
128 | free(line); | ||
129 | return (-1); | ||
130 | } | ||
131 | |||
132 | free(line); | ||
133 | |||
134 | return (0); | ||
135 | } | ||
diff --git a/tools/bio.c b/tools/bio.c new file mode 100644 index 0000000..b8f9b38 --- /dev/null +++ b/tools/bio.c | |||
@@ -0,0 +1,270 @@ | |||
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 <fido.h> | ||
8 | #include <fido/bio.h> | ||
9 | |||
10 | #include <stdbool.h> | ||
11 | #include <stdio.h> | ||
12 | #include <stdlib.h> | ||
13 | #include <string.h> | ||
14 | #ifdef HAVE_UNISTD_H | ||
15 | #include <unistd.h> | ||
16 | #endif | ||
17 | |||
18 | #include "../openbsd-compat/openbsd-compat.h" | ||
19 | #include "extern.h" | ||
20 | |||
21 | static void | ||
22 | print_template(const fido_bio_template_array_t *ta, size_t idx) | ||
23 | { | ||
24 | char *id = NULL; | ||
25 | const fido_bio_template_t *t = NULL; | ||
26 | |||
27 | if ((t = fido_bio_template(ta, idx)) == NULL) | ||
28 | errx(1, "fido_bio_template"); | ||
29 | |||
30 | if (base64_encode(fido_bio_template_id_ptr(t), | ||
31 | fido_bio_template_id_len(t), &id) < 0) | ||
32 | errx(1, "output error"); | ||
33 | |||
34 | printf("%02u: %s %s\n", (unsigned)idx, id, fido_bio_template_name(t)); | ||
35 | |||
36 | free(id); | ||
37 | } | ||
38 | |||
39 | int | ||
40 | bio_list(char *path) | ||
41 | { | ||
42 | char pin[1024]; | ||
43 | fido_bio_template_array_t *ta = NULL; | ||
44 | fido_dev_t *dev = NULL; | ||
45 | int r; | ||
46 | |||
47 | if (path == NULL) | ||
48 | usage(); | ||
49 | if ((ta = fido_bio_template_array_new()) == NULL) | ||
50 | errx(1, "fido_bio_template_array_new"); | ||
51 | |||
52 | dev = open_dev(path); | ||
53 | read_pin(path, pin, sizeof(pin)); | ||
54 | r = fido_bio_dev_get_template_array(dev, ta, pin); | ||
55 | explicit_bzero(pin, sizeof(pin)); | ||
56 | |||
57 | if (r != FIDO_OK) | ||
58 | errx(1, "fido_bio_dev_get_template_array: %s", fido_strerr(r)); | ||
59 | for (size_t i = 0; i < fido_bio_template_array_count(ta); i++) | ||
60 | print_template(ta, i); | ||
61 | |||
62 | fido_bio_template_array_free(&ta); | ||
63 | fido_dev_close(dev); | ||
64 | fido_dev_free(&dev); | ||
65 | |||
66 | exit(0); | ||
67 | } | ||
68 | |||
69 | int | ||
70 | bio_set_name(char *path, char *id, char *name) | ||
71 | { | ||
72 | char pin[1024]; | ||
73 | fido_bio_template_t *t = NULL; | ||
74 | fido_dev_t *dev = NULL; | ||
75 | int r; | ||
76 | size_t id_blob_len = 0; | ||
77 | void *id_blob_ptr = NULL; | ||
78 | |||
79 | if (path == NULL) | ||
80 | usage(); | ||
81 | if ((t = fido_bio_template_new()) == NULL) | ||
82 | errx(1, "fido_bio_template_new"); | ||
83 | |||
84 | if (base64_decode(id, &id_blob_ptr, &id_blob_len) < 0) | ||
85 | errx(1, "base64_decode"); | ||
86 | |||
87 | if ((r = fido_bio_template_set_name(t, name)) != FIDO_OK) | ||
88 | errx(1, "fido_bio_template_set_name: %s", fido_strerr(r)); | ||
89 | if ((r = fido_bio_template_set_id(t, id_blob_ptr, | ||
90 | id_blob_len)) != FIDO_OK) | ||
91 | errx(1, "fido_bio_template_set_id: %s", fido_strerr(r)); | ||
92 | |||
93 | dev = open_dev(path); | ||
94 | read_pin(path, pin, sizeof(pin)); | ||
95 | r = fido_bio_dev_set_template_name(dev, t, pin); | ||
96 | explicit_bzero(pin, sizeof(pin)); | ||
97 | |||
98 | if (r != FIDO_OK) | ||
99 | errx(1, "fido_bio_dev_set_template_name: %s", fido_strerr(r)); | ||
100 | |||
101 | free(id_blob_ptr); | ||
102 | fido_bio_template_free(&t); | ||
103 | fido_dev_close(dev); | ||
104 | fido_dev_free(&dev); | ||
105 | |||
106 | exit(0); | ||
107 | } | ||
108 | |||
109 | static const char * | ||
110 | plural(uint8_t n) | ||
111 | { | ||
112 | if (n == 1) | ||
113 | return ""; | ||
114 | return "s"; | ||
115 | } | ||
116 | |||
117 | static const char * | ||
118 | enroll_strerr(uint8_t n) | ||
119 | { | ||
120 | switch (n) { | ||
121 | case FIDO_BIO_ENROLL_FP_GOOD: | ||
122 | return "Sample ok"; | ||
123 | case FIDO_BIO_ENROLL_FP_TOO_HIGH: | ||
124 | return "Sample too high"; | ||
125 | case FIDO_BIO_ENROLL_FP_TOO_LOW: | ||
126 | return "Sample too low"; | ||
127 | case FIDO_BIO_ENROLL_FP_TOO_LEFT: | ||
128 | return "Sample too left"; | ||
129 | case FIDO_BIO_ENROLL_FP_TOO_RIGHT: | ||
130 | return "Sample too right"; | ||
131 | case FIDO_BIO_ENROLL_FP_TOO_FAST: | ||
132 | return "Sample too fast"; | ||
133 | case FIDO_BIO_ENROLL_FP_TOO_SLOW: | ||
134 | return "Sample too slow"; | ||
135 | case FIDO_BIO_ENROLL_FP_POOR_QUALITY: | ||
136 | return "Poor quality sample"; | ||
137 | case FIDO_BIO_ENROLL_FP_TOO_SKEWED: | ||
138 | return "Sample too skewed"; | ||
139 | case FIDO_BIO_ENROLL_FP_TOO_SHORT: | ||
140 | return "Sample too short"; | ||
141 | case FIDO_BIO_ENROLL_FP_MERGE_FAILURE: | ||
142 | return "Sample merge failure"; | ||
143 | case FIDO_BIO_ENROLL_FP_EXISTS: | ||
144 | return "Sample exists"; | ||
145 | case FIDO_BIO_ENROLL_FP_DATABASE_FULL: | ||
146 | return "Fingerprint database full"; | ||
147 | case FIDO_BIO_ENROLL_NO_USER_ACTIVITY: | ||
148 | return "No user activity"; | ||
149 | case FIDO_BIO_ENROLL_NO_USER_PRESENCE_TRANSITION: | ||
150 | return "No user presence transition"; | ||
151 | default: | ||
152 | return "Unknown error"; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | int | ||
157 | bio_enroll(char *path) | ||
158 | { | ||
159 | char pin[1024]; | ||
160 | fido_bio_enroll_t *e = NULL; | ||
161 | fido_bio_template_t *t = NULL; | ||
162 | fido_dev_t *dev = NULL; | ||
163 | int r; | ||
164 | |||
165 | if (path == NULL) | ||
166 | usage(); | ||
167 | if ((t = fido_bio_template_new()) == NULL) | ||
168 | errx(1, "fido_bio_template_new"); | ||
169 | if ((e = fido_bio_enroll_new()) == NULL) | ||
170 | errx(1, "fido_bio_enroll_new"); | ||
171 | |||
172 | dev = open_dev(path); | ||
173 | read_pin(path, pin, sizeof(pin)); | ||
174 | |||
175 | printf("Touch your security key.\n"); | ||
176 | |||
177 | r = fido_bio_dev_enroll_begin(dev, t, e, 10000, pin); | ||
178 | explicit_bzero(pin, sizeof(pin)); | ||
179 | if (r != FIDO_OK) | ||
180 | errx(1, "fido_bio_dev_enroll_begin: %s", fido_strerr(r)); | ||
181 | |||
182 | printf("%s.\n", enroll_strerr(fido_bio_enroll_last_status(e))); | ||
183 | |||
184 | while (fido_bio_enroll_remaining_samples(e) > 0) { | ||
185 | printf("Touch your security key (%u sample%s left).\n", | ||
186 | (unsigned)fido_bio_enroll_remaining_samples(e), | ||
187 | plural(fido_bio_enroll_remaining_samples(e))); | ||
188 | if ((r = fido_bio_dev_enroll_continue(dev, t, e, | ||
189 | 10000)) != FIDO_OK) { | ||
190 | errx(1, "fido_bio_dev_enroll_continue: %s", | ||
191 | fido_strerr(r)); | ||
192 | } | ||
193 | printf("%s.\n", enroll_strerr(fido_bio_enroll_last_status(e))); | ||
194 | } | ||
195 | |||
196 | fido_bio_template_free(&t); | ||
197 | fido_bio_enroll_free(&e); | ||
198 | fido_dev_close(dev); | ||
199 | fido_dev_free(&dev); | ||
200 | |||
201 | exit(0); | ||
202 | } | ||
203 | |||
204 | int | ||
205 | bio_delete(fido_dev_t *dev, char *path, char *id) | ||
206 | { | ||
207 | char pin[1024]; | ||
208 | fido_bio_template_t *t = NULL; | ||
209 | int r; | ||
210 | size_t id_blob_len = 0; | ||
211 | void *id_blob_ptr = NULL; | ||
212 | |||
213 | if (path == NULL) | ||
214 | usage(); | ||
215 | if ((t = fido_bio_template_new()) == NULL) | ||
216 | errx(1, "fido_bio_template_new"); | ||
217 | |||
218 | if (base64_decode(id, &id_blob_ptr, &id_blob_len) < 0) | ||
219 | errx(1, "base64_decode"); | ||
220 | if ((r = fido_bio_template_set_id(t, id_blob_ptr, | ||
221 | id_blob_len)) != FIDO_OK) | ||
222 | errx(1, "fido_bio_template_set_id: %s", fido_strerr(r)); | ||
223 | |||
224 | read_pin(path, pin, sizeof(pin)); | ||
225 | r = fido_bio_dev_enroll_remove(dev, t, pin); | ||
226 | explicit_bzero(pin, sizeof(pin)); | ||
227 | |||
228 | if (r != FIDO_OK) | ||
229 | errx(1, "fido_bio_dev_enroll_remove: %s", fido_strerr(r)); | ||
230 | |||
231 | free(id_blob_ptr); | ||
232 | fido_bio_template_free(&t); | ||
233 | fido_dev_close(dev); | ||
234 | fido_dev_free(&dev); | ||
235 | |||
236 | exit(0); | ||
237 | } | ||
238 | |||
239 | static const char * | ||
240 | type_str(uint8_t t) | ||
241 | { | ||
242 | switch (t) { | ||
243 | case 1: | ||
244 | return "touch"; | ||
245 | case 2: | ||
246 | return "swipe"; | ||
247 | default: | ||
248 | return "unknown"; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | void | ||
253 | bio_info(fido_dev_t *dev) | ||
254 | { | ||
255 | fido_bio_info_t *i = NULL; | ||
256 | int r; | ||
257 | |||
258 | if ((i = fido_bio_info_new()) == NULL) | ||
259 | errx(1, "fido_bio_info_new"); | ||
260 | if ((r = fido_bio_dev_get_info(dev, i)) != FIDO_OK) { | ||
261 | fido_bio_info_free(&i); | ||
262 | return; | ||
263 | } | ||
264 | |||
265 | printf("sensor type: %u (%s)\n", (unsigned)fido_bio_info_type(i), | ||
266 | type_str(fido_bio_info_type(i))); | ||
267 | printf("max samples: %u\n", (unsigned)fido_bio_info_max_samples(i)); | ||
268 | |||
269 | fido_bio_info_free(&i); | ||
270 | } | ||
diff --git a/tools/cred_make.c b/tools/cred_make.c new file mode 100644 index 0000000..380c67a --- /dev/null +++ b/tools/cred_make.c | |||
@@ -0,0 +1,221 @@ | |||
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.h> | ||
8 | #include <stdio.h> | ||
9 | #include <stdlib.h> | ||
10 | #include <string.h> | ||
11 | #ifdef HAVE_UNISTD_H | ||
12 | #include <unistd.h> | ||
13 | #endif | ||
14 | |||
15 | #include "../openbsd-compat/openbsd-compat.h" | ||
16 | #include "extern.h" | ||
17 | |||
18 | static fido_cred_t * | ||
19 | prepare_cred(FILE *in_f, int type, int flags) | ||
20 | { | ||
21 | fido_cred_t *cred = NULL; | ||
22 | struct blob cdh; | ||
23 | struct blob uid; | ||
24 | char *rpid = NULL; | ||
25 | char *uname = NULL; | ||
26 | int r; | ||
27 | |||
28 | memset(&cdh, 0, sizeof(cdh)); | ||
29 | memset(&uid, 0, sizeof(uid)); | ||
30 | |||
31 | r = base64_read(in_f, &cdh); | ||
32 | r |= string_read(in_f, &rpid); | ||
33 | r |= string_read(in_f, &uname); | ||
34 | r |= base64_read(in_f, &uid); | ||
35 | if (r < 0) | ||
36 | errx(1, "input error"); | ||
37 | |||
38 | if (flags & FLAG_DEBUG) { | ||
39 | fprintf(stderr, "client data hash:\n"); | ||
40 | xxd(cdh.ptr, cdh.len); | ||
41 | fprintf(stderr, "relying party id: %s\n", rpid); | ||
42 | fprintf(stderr, "user name: %s\n", uname); | ||
43 | fprintf(stderr, "user id:\n"); | ||
44 | xxd(uid.ptr, uid.len); | ||
45 | } | ||
46 | |||
47 | if ((cred = fido_cred_new()) == NULL) | ||
48 | errx(1, "fido_cred_new"); | ||
49 | |||
50 | if ((r = fido_cred_set_type(cred, type)) != FIDO_OK || | ||
51 | (r = fido_cred_set_clientdata_hash(cred, cdh.ptr, | ||
52 | cdh.len)) != FIDO_OK || | ||
53 | (r = fido_cred_set_rp(cred, rpid, NULL)) != FIDO_OK || | ||
54 | (r = fido_cred_set_user(cred, uid.ptr, uid.len, uname, NULL, | ||
55 | NULL)) != FIDO_OK) | ||
56 | errx(1, "fido_cred_set: %s", fido_strerr(r)); | ||
57 | |||
58 | if (flags & FLAG_RK) { | ||
59 | if ((r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK) | ||
60 | errx(1, "fido_cred_set_rk: %s", fido_strerr(r)); | ||
61 | } | ||
62 | if (flags & FLAG_UV) { | ||
63 | if ((r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) | ||
64 | errx(1, "fido_cred_set_uv: %s", fido_strerr(r)); | ||
65 | } | ||
66 | if (flags & FLAG_HMAC) { | ||
67 | if ((r = fido_cred_set_extensions(cred, | ||
68 | FIDO_EXT_HMAC_SECRET)) != FIDO_OK) | ||
69 | errx(1, "fido_cred_set_extensions: %s", fido_strerr(r)); | ||
70 | } | ||
71 | |||
72 | free(cdh.ptr); | ||
73 | free(uid.ptr); | ||
74 | free(rpid); | ||
75 | free(uname); | ||
76 | |||
77 | return (cred); | ||
78 | } | ||
79 | |||
80 | static void | ||
81 | print_attcred(FILE *out_f, const fido_cred_t *cred) | ||
82 | { | ||
83 | char *cdh = NULL; | ||
84 | char *authdata = NULL; | ||
85 | char *id = NULL; | ||
86 | char *sig = NULL; | ||
87 | char *x5c = NULL; | ||
88 | int r; | ||
89 | |||
90 | r = base64_encode(fido_cred_clientdata_hash_ptr(cred), | ||
91 | fido_cred_clientdata_hash_len(cred), &cdh); | ||
92 | r |= base64_encode(fido_cred_authdata_ptr(cred), | ||
93 | fido_cred_authdata_len(cred), &authdata); | ||
94 | r |= base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred), | ||
95 | &id); | ||
96 | r |= base64_encode(fido_cred_sig_ptr(cred), fido_cred_sig_len(cred), | ||
97 | &sig); | ||
98 | if (fido_cred_x5c_ptr(cred) != NULL) | ||
99 | r |= base64_encode(fido_cred_x5c_ptr(cred), | ||
100 | fido_cred_x5c_len(cred), &x5c); | ||
101 | if (r < 0) | ||
102 | errx(1, "output error"); | ||
103 | |||
104 | fprintf(out_f, "%s\n", cdh); | ||
105 | fprintf(out_f, "%s\n", fido_cred_rp_id(cred)); | ||
106 | fprintf(out_f, "%s\n", fido_cred_fmt(cred)); | ||
107 | fprintf(out_f, "%s\n", authdata); | ||
108 | fprintf(out_f, "%s\n", id); | ||
109 | fprintf(out_f, "%s\n", sig); | ||
110 | if (x5c != NULL) | ||
111 | fprintf(out_f, "%s\n", x5c); | ||
112 | |||
113 | free(cdh); | ||
114 | free(authdata); | ||
115 | free(id); | ||
116 | free(sig); | ||
117 | free(x5c); | ||
118 | } | ||
119 | |||
120 | int | ||
121 | cred_make(int argc, char **argv) | ||
122 | { | ||
123 | fido_dev_t *dev = NULL; | ||
124 | fido_cred_t *cred = NULL; | ||
125 | char prompt[1024]; | ||
126 | char pin[1024]; | ||
127 | char *in_path = NULL; | ||
128 | char *out_path = NULL; | ||
129 | FILE *in_f = NULL; | ||
130 | FILE *out_f = NULL; | ||
131 | int type = COSE_ES256; | ||
132 | int flags = 0; | ||
133 | int ch; | ||
134 | int r; | ||
135 | |||
136 | while ((ch = getopt(argc, argv, "dhi:o:qruv")) != -1) { | ||
137 | switch (ch) { | ||
138 | case 'd': | ||
139 | flags |= FLAG_DEBUG; | ||
140 | break; | ||
141 | case 'h': | ||
142 | flags |= FLAG_HMAC; | ||
143 | break; | ||
144 | case 'i': | ||
145 | in_path = optarg; | ||
146 | break; | ||
147 | case 'o': | ||
148 | out_path = optarg; | ||
149 | break; | ||
150 | case 'q': | ||
151 | flags |= FLAG_QUIET; | ||
152 | break; | ||
153 | case 'r': | ||
154 | flags |= FLAG_RK; | ||
155 | break; | ||
156 | case 'u': | ||
157 | flags |= FLAG_U2F; | ||
158 | break; | ||
159 | case 'v': | ||
160 | flags |= FLAG_UV; | ||
161 | break; | ||
162 | default: | ||
163 | usage(); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | argc -= optind; | ||
168 | argv += optind; | ||
169 | |||
170 | if (argc < 1 || argc > 2) | ||
171 | usage(); | ||
172 | |||
173 | in_f = open_read(in_path); | ||
174 | out_f = open_write(out_path); | ||
175 | |||
176 | if (argc > 1) { | ||
177 | if (strcmp(argv[1], "es256") == 0) | ||
178 | type = COSE_ES256; | ||
179 | else if (strcmp(argv[1], "rs256") == 0) | ||
180 | type = COSE_RS256; | ||
181 | else if (strcmp(argv[1], "eddsa") == 0) | ||
182 | type = COSE_EDDSA; | ||
183 | else | ||
184 | errx(1, "unknown type %s", argv[1]); | ||
185 | } | ||
186 | |||
187 | fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0); | ||
188 | |||
189 | cred = prepare_cred(in_f, type, flags); | ||
190 | |||
191 | dev = open_dev(argv[0]); | ||
192 | if (flags & FLAG_U2F) | ||
193 | fido_dev_force_u2f(dev); | ||
194 | |||
195 | r = fido_dev_make_cred(dev, cred, NULL); | ||
196 | if (r == FIDO_ERR_PIN_REQUIRED && !(flags & FLAG_QUIET)) { | ||
197 | r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", | ||
198 | argv[0]); | ||
199 | if (r < 0 || (size_t)r >= sizeof(prompt)) | ||
200 | errx(1, "snprintf"); | ||
201 | if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF)) | ||
202 | errx(1, "readpassphrase"); | ||
203 | r = fido_dev_make_cred(dev, cred, pin); | ||
204 | } | ||
205 | |||
206 | explicit_bzero(pin, sizeof(pin)); | ||
207 | if (r != FIDO_OK) | ||
208 | errx(1, "fido_dev_make_cred: %s", fido_strerr(r)); | ||
209 | print_attcred(out_f, cred); | ||
210 | |||
211 | fido_dev_close(dev); | ||
212 | fido_dev_free(&dev); | ||
213 | fido_cred_free(&cred); | ||
214 | |||
215 | fclose(in_f); | ||
216 | fclose(out_f); | ||
217 | in_f = NULL; | ||
218 | out_f = NULL; | ||
219 | |||
220 | exit(0); | ||
221 | } | ||
diff --git a/tools/cred_verify.c b/tools/cred_verify.c new file mode 100644 index 0000000..3f7a400 --- /dev/null +++ b/tools/cred_verify.c | |||
@@ -0,0 +1,177 @@ | |||
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.h> | ||
8 | #include <stdio.h> | ||
9 | #include <stdlib.h> | ||
10 | #include <string.h> | ||
11 | #ifdef HAVE_UNISTD_H | ||
12 | #include <unistd.h> | ||
13 | #endif | ||
14 | |||
15 | #include "../openbsd-compat/openbsd-compat.h" | ||
16 | #include "extern.h" | ||
17 | |||
18 | static fido_cred_t * | ||
19 | prepare_cred(FILE *in_f, int type, int flags) | ||
20 | { | ||
21 | fido_cred_t *cred = NULL; | ||
22 | struct blob cdh; | ||
23 | struct blob authdata; | ||
24 | struct blob id; | ||
25 | struct blob sig; | ||
26 | struct blob x5c; | ||
27 | char *rpid = NULL; | ||
28 | char *fmt = NULL; | ||
29 | int r; | ||
30 | |||
31 | memset(&cdh, 0, sizeof(cdh)); | ||
32 | memset(&authdata, 0, sizeof(authdata)); | ||
33 | memset(&id, 0, sizeof(id)); | ||
34 | memset(&sig, 0, sizeof(sig)); | ||
35 | memset(&x5c, 0, sizeof(x5c)); | ||
36 | |||
37 | r = base64_read(in_f, &cdh); | ||
38 | r |= string_read(in_f, &rpid); | ||
39 | r |= string_read(in_f, &fmt); | ||
40 | r |= base64_read(in_f, &authdata); | ||
41 | r |= base64_read(in_f, &id); | ||
42 | r |= base64_read(in_f, &sig); | ||
43 | if (r < 0) | ||
44 | errx(1, "input error"); | ||
45 | |||
46 | (void)base64_read(in_f, &x5c); | ||
47 | |||
48 | if (flags & FLAG_DEBUG) { | ||
49 | fprintf(stderr, "client data hash:\n"); | ||
50 | xxd(cdh.ptr, cdh.len); | ||
51 | fprintf(stderr, "relying party id: %s\n", rpid); | ||
52 | fprintf(stderr, "format: %s\n", fmt); | ||
53 | fprintf(stderr, "authenticator data:\n"); | ||
54 | xxd(authdata.ptr, authdata.len); | ||
55 | fprintf(stderr, "credential id:\n"); | ||
56 | xxd(id.ptr, id.len); | ||
57 | fprintf(stderr, "signature:\n"); | ||
58 | xxd(sig.ptr, sig.len); | ||
59 | fprintf(stderr, "x509:\n"); | ||
60 | xxd(x5c.ptr, x5c.len); | ||
61 | } | ||
62 | |||
63 | if ((cred = fido_cred_new()) == NULL) | ||
64 | errx(1, "fido_cred_new"); | ||
65 | |||
66 | if ((r = fido_cred_set_type(cred, type)) != FIDO_OK || | ||
67 | (r = fido_cred_set_clientdata_hash(cred, cdh.ptr, | ||
68 | cdh.len)) != FIDO_OK || | ||
69 | (r = fido_cred_set_rp(cred, rpid, NULL)) != FIDO_OK || | ||
70 | (r = fido_cred_set_authdata(cred, authdata.ptr, | ||
71 | authdata.len)) != FIDO_OK || | ||
72 | (r = fido_cred_set_sig(cred, sig.ptr, sig.len)) != FIDO_OK || | ||
73 | (r = fido_cred_set_fmt(cred, fmt)) != FIDO_OK) | ||
74 | errx(1, "fido_cred_set: %s", fido_strerr(r)); | ||
75 | |||
76 | if (x5c.ptr != NULL) { | ||
77 | if ((r = fido_cred_set_x509(cred, x5c.ptr, x5c.len)) != FIDO_OK) | ||
78 | errx(1, "fido_cred_set_x509: %s", fido_strerr(r)); | ||
79 | } | ||
80 | |||
81 | if (flags & FLAG_UV) { | ||
82 | if ((r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) | ||
83 | errx(1, "fido_cred_set_uv: %s", fido_strerr(r)); | ||
84 | } | ||
85 | if (flags & FLAG_HMAC) { | ||
86 | if ((r = fido_cred_set_extensions(cred, | ||
87 | FIDO_EXT_HMAC_SECRET)) != FIDO_OK) | ||
88 | errx(1, "fido_cred_set_extensions: %s", fido_strerr(r)); | ||
89 | } | ||
90 | |||
91 | free(cdh.ptr); | ||
92 | free(authdata.ptr); | ||
93 | free(id.ptr); | ||
94 | free(sig.ptr); | ||
95 | free(x5c.ptr); | ||
96 | free(rpid); | ||
97 | free(fmt); | ||
98 | |||
99 | return (cred); | ||
100 | } | ||
101 | |||
102 | int | ||
103 | cred_verify(int argc, char **argv) | ||
104 | { | ||
105 | fido_cred_t *cred = NULL; | ||
106 | char *in_path = NULL; | ||
107 | char *out_path = NULL; | ||
108 | FILE *in_f = NULL; | ||
109 | FILE *out_f = NULL; | ||
110 | int type = COSE_ES256; | ||
111 | int flags = 0; | ||
112 | int ch; | ||
113 | int r; | ||
114 | |||
115 | while ((ch = getopt(argc, argv, "dhi:o:v")) != -1) { | ||
116 | switch (ch) { | ||
117 | case 'd': | ||
118 | flags |= FLAG_DEBUG; | ||
119 | break; | ||
120 | case 'h': | ||
121 | flags |= FLAG_HMAC; | ||
122 | break; | ||
123 | case 'i': | ||
124 | in_path = optarg; | ||
125 | break; | ||
126 | case 'o': | ||
127 | out_path = optarg; | ||
128 | break; | ||
129 | case 'v': | ||
130 | flags |= FLAG_UV; | ||
131 | break; | ||
132 | default: | ||
133 | usage(); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | argc -= optind; | ||
138 | argv += optind; | ||
139 | |||
140 | if (argc > 1) | ||
141 | usage(); | ||
142 | |||
143 | in_f = open_read(in_path); | ||
144 | out_f = open_write(out_path); | ||
145 | |||
146 | if (argc > 0) { | ||
147 | if (strcmp(argv[0], "es256") == 0) | ||
148 | type = COSE_ES256; | ||
149 | else if (strcmp(argv[0], "rs256") == 0) | ||
150 | type = COSE_RS256; | ||
151 | else if (strcmp(argv[0], "eddsa") == 0) | ||
152 | type = COSE_EDDSA; | ||
153 | else | ||
154 | errx(1, "unknown type %s", argv[0]); | ||
155 | } | ||
156 | |||
157 | fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0); | ||
158 | cred = prepare_cred(in_f, type, flags); | ||
159 | |||
160 | if (fido_cred_x5c_ptr(cred) == NULL) { | ||
161 | if ((r = fido_cred_verify_self(cred)) != FIDO_OK) | ||
162 | errx(1, "fido_cred_verify_self: %s", fido_strerr(r)); | ||
163 | } else { | ||
164 | if ((r = fido_cred_verify(cred)) != FIDO_OK) | ||
165 | errx(1, "fido_cred_verify: %s", fido_strerr(r)); | ||
166 | } | ||
167 | |||
168 | print_cred(out_f, type, cred); | ||
169 | fido_cred_free(&cred); | ||
170 | |||
171 | fclose(in_f); | ||
172 | fclose(out_f); | ||
173 | in_f = NULL; | ||
174 | out_f = NULL; | ||
175 | |||
176 | exit(0); | ||
177 | } | ||
diff --git a/tools/credman.c b/tools/credman.c new file mode 100644 index 0000000..08c9eb8 --- /dev/null +++ b/tools/credman.c | |||
@@ -0,0 +1,237 @@ | |||
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 <fido.h> | ||
8 | #include <fido/credman.h> | ||
9 | |||
10 | #include <stdbool.h> | ||
11 | #include <stdio.h> | ||
12 | #include <stdlib.h> | ||
13 | #include <string.h> | ||
14 | #ifdef HAVE_UNISTD_H | ||
15 | #include <unistd.h> | ||
16 | #endif | ||
17 | |||
18 | #include "../openbsd-compat/openbsd-compat.h" | ||
19 | #include "extern.h" | ||
20 | |||
21 | int | ||
22 | credman_get_metadata(fido_dev_t *dev, const char *path) | ||
23 | { | ||
24 | fido_credman_metadata_t *metadata = NULL; | ||
25 | char pin[1024]; | ||
26 | int r; | ||
27 | |||
28 | if ((metadata = fido_credman_metadata_new()) == NULL) | ||
29 | errx(1, "fido_credman_metadata_new"); | ||
30 | |||
31 | read_pin(path, pin, sizeof(pin)); | ||
32 | r = fido_credman_get_dev_metadata(dev, metadata, pin); | ||
33 | explicit_bzero(pin, sizeof(pin)); | ||
34 | |||
35 | if (r != FIDO_OK) | ||
36 | errx(1, "fido_credman_get_dev_metadata: %s", fido_strerr(r)); | ||
37 | |||
38 | printf("existing rk(s): %u\n", | ||
39 | (unsigned)fido_credman_rk_existing(metadata)); | ||
40 | printf("possible rk(s): %u\n", | ||
41 | (unsigned)fido_credman_rk_remaining(metadata)); | ||
42 | |||
43 | fido_credman_metadata_free(&metadata); | ||
44 | fido_dev_close(dev); | ||
45 | fido_dev_free(&dev); | ||
46 | |||
47 | exit(0); | ||
48 | } | ||
49 | |||
50 | static void | ||
51 | print_rp(fido_credman_rp_t *rp, size_t idx) | ||
52 | { | ||
53 | char *rp_id_hash = NULL; | ||
54 | |||
55 | if (base64_encode(fido_credman_rp_id_hash_ptr(rp, idx), | ||
56 | fido_credman_rp_id_hash_len(rp, idx), &rp_id_hash) < 0) | ||
57 | errx(1, "output error"); | ||
58 | |||
59 | printf("%02u: %s %s\n", (unsigned)idx, rp_id_hash, | ||
60 | fido_credman_rp_id(rp, idx)); | ||
61 | |||
62 | free(rp_id_hash); | ||
63 | rp_id_hash = NULL; | ||
64 | } | ||
65 | |||
66 | int | ||
67 | credman_list_rp(char *path) | ||
68 | { | ||
69 | fido_dev_t *dev = NULL; | ||
70 | fido_credman_rp_t *rp = NULL; | ||
71 | char pin[1024]; | ||
72 | int r; | ||
73 | |||
74 | if (path == NULL) | ||
75 | usage(); | ||
76 | if ((rp = fido_credman_rp_new()) == NULL) | ||
77 | errx(1, "fido_credman_rp_new"); | ||
78 | |||
79 | dev = open_dev(path); | ||
80 | read_pin(path, pin, sizeof(pin)); | ||
81 | r = fido_credman_get_dev_rp(dev, rp, pin); | ||
82 | explicit_bzero(pin, sizeof(pin)); | ||
83 | |||
84 | if (r != FIDO_OK) | ||
85 | errx(1, "fido_credman_get_dev_rp: %s", fido_strerr(r)); | ||
86 | |||
87 | for (size_t i = 0; i < fido_credman_rp_count(rp); i++) | ||
88 | print_rp(rp, i); | ||
89 | |||
90 | fido_credman_rp_free(&rp); | ||
91 | fido_dev_close(dev); | ||
92 | fido_dev_free(&dev); | ||
93 | |||
94 | exit(0); | ||
95 | } | ||
96 | |||
97 | static void | ||
98 | print_rk(const fido_credman_rk_t *rk, size_t idx) | ||
99 | { | ||
100 | const fido_cred_t *cred; | ||
101 | char *id = NULL; | ||
102 | char *user_id = NULL; | ||
103 | const char *type; | ||
104 | |||
105 | if ((cred = fido_credman_rk(rk, idx)) == NULL) | ||
106 | errx(1, "fido_credman_rk"); | ||
107 | if (base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred), | ||
108 | &id) < 0 || base64_encode(fido_cred_user_id_ptr(cred), | ||
109 | fido_cred_user_id_len(cred), &user_id) < 0) | ||
110 | errx(1, "output error"); | ||
111 | |||
112 | switch (fido_cred_type(cred)) { | ||
113 | case COSE_EDDSA: | ||
114 | type = "eddsa"; | ||
115 | break; | ||
116 | case COSE_ES256: | ||
117 | type = "es256"; | ||
118 | break; | ||
119 | case COSE_RS256: | ||
120 | type = "rs256"; | ||
121 | break; | ||
122 | default: | ||
123 | type = "unknown"; | ||
124 | break; | ||
125 | } | ||
126 | |||
127 | printf("%02u: %s %s (%s) %s\n", (unsigned)idx, id, | ||
128 | fido_cred_display_name(cred), user_id, type); | ||
129 | |||
130 | free(user_id); | ||
131 | free(id); | ||
132 | user_id = NULL; | ||
133 | id = NULL; | ||
134 | } | ||
135 | |||
136 | int | ||
137 | credman_list_rk(char *path, const char *rp_id) | ||
138 | { | ||
139 | fido_dev_t *dev = NULL; | ||
140 | fido_credman_rk_t *rk = NULL; | ||
141 | char pin[1024]; | ||
142 | int r; | ||
143 | |||
144 | if (path == NULL) | ||
145 | usage(); | ||
146 | if ((rk = fido_credman_rk_new()) == NULL) | ||
147 | errx(1, "fido_credman_rk_new"); | ||
148 | |||
149 | dev = open_dev(path); | ||
150 | read_pin(path, pin, sizeof(pin)); | ||
151 | r = fido_credman_get_dev_rk(dev, rp_id, rk, pin); | ||
152 | explicit_bzero(pin, sizeof(pin)); | ||
153 | |||
154 | if (r != FIDO_OK) | ||
155 | errx(1, "fido_credman_get_dev_rk: %s", fido_strerr(r)); | ||
156 | for (size_t i = 0; i < fido_credman_rk_count(rk); i++) | ||
157 | print_rk(rk, i); | ||
158 | |||
159 | fido_credman_rk_free(&rk); | ||
160 | fido_dev_close(dev); | ||
161 | fido_dev_free(&dev); | ||
162 | |||
163 | exit(0); | ||
164 | } | ||
165 | |||
166 | int | ||
167 | credman_print_rk(fido_dev_t *dev, const char *path, char *rp_id, char *cred_id) | ||
168 | { | ||
169 | const fido_cred_t *cred = NULL; | ||
170 | fido_credman_rk_t *rk = NULL; | ||
171 | char pin[1024]; | ||
172 | void *cred_id_ptr = NULL; | ||
173 | size_t cred_id_len = 0; | ||
174 | int r; | ||
175 | |||
176 | if ((rk = fido_credman_rk_new()) == NULL) | ||
177 | errx(1, "fido_credman_rk_new"); | ||
178 | if (base64_decode(cred_id, &cred_id_ptr, &cred_id_len) < 0) | ||
179 | errx(1, "base64_decode"); | ||
180 | |||
181 | read_pin(path, pin, sizeof(pin)); | ||
182 | r = fido_credman_get_dev_rk(dev, rp_id, rk, pin); | ||
183 | explicit_bzero(pin, sizeof(pin)); | ||
184 | |||
185 | if (r != FIDO_OK) | ||
186 | errx(1, "fido_credman_get_dev_rk: %s", fido_strerr(r)); | ||
187 | |||
188 | for (size_t i = 0; i < fido_credman_rk_count(rk); i++) { | ||
189 | if ((cred = fido_credman_rk(rk, i)) == NULL || | ||
190 | fido_cred_id_ptr(cred) == NULL) | ||
191 | errx(1, "output error"); | ||
192 | if (cred_id_len != fido_cred_id_len(cred) || | ||
193 | memcmp(cred_id_ptr, fido_cred_id_ptr(cred), cred_id_len)) | ||
194 | continue; | ||
195 | print_cred(stdout, fido_cred_type(cred), cred); | ||
196 | goto out; | ||
197 | } | ||
198 | |||
199 | errx(1, "credential not found"); | ||
200 | |||
201 | out: | ||
202 | free(cred_id_ptr); | ||
203 | cred_id_ptr = NULL; | ||
204 | |||
205 | fido_credman_rk_free(&rk); | ||
206 | fido_dev_close(dev); | ||
207 | fido_dev_free(&dev); | ||
208 | |||
209 | exit(0); | ||
210 | } | ||
211 | |||
212 | int | ||
213 | credman_delete_rk(fido_dev_t *dev, const char *path, char *id) | ||
214 | { | ||
215 | char pin[1024]; | ||
216 | void *id_ptr = NULL; | ||
217 | size_t id_len = 0; | ||
218 | int r; | ||
219 | |||
220 | if (base64_decode(id, &id_ptr, &id_len) < 0) | ||
221 | errx(1, "base64_decode"); | ||
222 | |||
223 | read_pin(path, pin, sizeof(pin)); | ||
224 | r = fido_credman_del_dev_rk(dev, id_ptr, id_len, pin); | ||
225 | explicit_bzero(pin, sizeof(pin)); | ||
226 | |||
227 | if (r != FIDO_OK) | ||
228 | errx(1, "fido_credman_del_dev_rk: %s", fido_strerr(r)); | ||
229 | |||
230 | free(id_ptr); | ||
231 | id_ptr = NULL; | ||
232 | |||
233 | fido_dev_close(dev); | ||
234 | fido_dev_free(&dev); | ||
235 | |||
236 | exit(0); | ||
237 | } | ||
diff --git a/tools/extern.h b/tools/extern.h new file mode 100644 index 0000000..e79e6f0 --- /dev/null +++ b/tools/extern.h | |||
@@ -0,0 +1,64 @@ | |||
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 | struct blob { | ||
11 | unsigned char *ptr; | ||
12 | size_t len; | ||
13 | }; | ||
14 | |||
15 | #define TOKEN_OPT "CDILPRSVbcdei:k:n:r" | ||
16 | |||
17 | #define FLAG_DEBUG 0x01 | ||
18 | #define FLAG_QUIET 0x02 | ||
19 | #define FLAG_RK 0x04 | ||
20 | #define FLAG_UV 0x08 | ||
21 | #define FLAG_U2F 0x10 | ||
22 | #define FLAG_HMAC 0x20 | ||
23 | #define FLAG_UP 0x40 | ||
24 | |||
25 | EC_KEY *read_ec_pubkey(const char *); | ||
26 | fido_dev_t *open_dev(const char *); | ||
27 | FILE *open_read(const char *); | ||
28 | FILE *open_write(const char *); | ||
29 | int assert_get(int, char **); | ||
30 | int assert_verify(int, char **); | ||
31 | int base64_decode(char *, void **, size_t *); | ||
32 | int base64_encode(const void *, size_t, char **); | ||
33 | int base64_read(FILE *, struct blob *); | ||
34 | int bio_delete(fido_dev_t *, char *, char *); | ||
35 | int bio_enroll(char *); | ||
36 | void bio_info(fido_dev_t *); | ||
37 | int bio_list(char *); | ||
38 | int bio_set_name(char *, char *, char *); | ||
39 | int cred_make(int, char **); | ||
40 | int cred_verify(int, char **); | ||
41 | int credman_delete_rk(fido_dev_t *, const char *, char *); | ||
42 | int credman_get_metadata(fido_dev_t *, const char *); | ||
43 | int credman_list_rk(char *, const char *); | ||
44 | int credman_list_rp(char *); | ||
45 | int credman_print_rk(fido_dev_t *, const char *, char *, char *); | ||
46 | int pin_change(char *); | ||
47 | int pin_set(char *); | ||
48 | int string_read(FILE *, char **); | ||
49 | int token_delete(int, char **, char *); | ||
50 | int token_info(int, char **, char *); | ||
51 | int token_list(int, char **, char *); | ||
52 | int token_reset(char *); | ||
53 | int token_set(int, char **, char *); | ||
54 | int write_ec_pubkey(FILE *, const void *, size_t); | ||
55 | int write_rsa_pubkey(FILE *, const void *, size_t); | ||
56 | RSA *read_rsa_pubkey(const char *); | ||
57 | EVP_PKEY *read_eddsa_pubkey(const char *); | ||
58 | int write_eddsa_pubkey(FILE *, const void *, size_t); | ||
59 | void print_cred(FILE *, int, const fido_cred_t *); | ||
60 | void read_pin(const char *, char *, size_t); | ||
61 | void usage(void); | ||
62 | void xxd(const void *, size_t); | ||
63 | |||
64 | #endif /* _EXTERN_H_ */ | ||
diff --git a/tools/fido2-assert.c b/tools/fido2-assert.c new file mode 100644 index 0000000..9ce537a --- /dev/null +++ b/tools/fido2-assert.c | |||
@@ -0,0 +1,54 @@ | |||
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 | /* | ||
8 | * Example usage: | ||
9 | * | ||
10 | * $ echo assertion challenge | openssl sha256 -binary | base64 > assert_param | ||
11 | * $ echo relying party >> assert_param | ||
12 | * $ head -1 cred >> assert_param # credential id | ||
13 | * $ tail -n +2 cred > pubkey # credential pubkey | ||
14 | * $ fido2-assert -G -i assert_param /dev/hidraw5 | fido2-assert -V pubkey rs256 | ||
15 | * | ||
16 | * See blurb in fido2-cred.c on how to obtain cred. | ||
17 | */ | ||
18 | |||
19 | #include <fido.h> | ||
20 | #include <stdio.h> | ||
21 | #include <stdlib.h> | ||
22 | #include <string.h> | ||
23 | |||
24 | #include "../openbsd-compat/openbsd-compat.h" | ||
25 | #include "extern.h" | ||
26 | |||
27 | void | ||
28 | usage(void) | ||
29 | { | ||
30 | fprintf(stderr, | ||
31 | "usage: fido2-assert -G [-dhpruv] [-i input_file] [-o output_file] device\n" | ||
32 | " fido2-assert -V [-dhpv] [-i input_file] key_file [type]\n" | ||
33 | ); | ||
34 | |||
35 | exit(1); | ||
36 | } | ||
37 | |||
38 | int | ||
39 | main(int argc, char **argv) | ||
40 | { | ||
41 | if (argc < 2 || strlen(argv[1]) != 2 || argv[1][0] != '-') | ||
42 | usage(); | ||
43 | |||
44 | switch (argv[1][1]) { | ||
45 | case 'G': | ||
46 | return (assert_get(--argc, ++argv)); | ||
47 | case 'V': | ||
48 | return (assert_verify(--argc, ++argv)); | ||
49 | } | ||
50 | |||
51 | usage(); | ||
52 | |||
53 | /* NOTREACHED */ | ||
54 | } | ||
diff --git a/tools/fido2-cred.c b/tools/fido2-cred.c new file mode 100644 index 0000000..45efca0 --- /dev/null +++ b/tools/fido2-cred.c | |||
@@ -0,0 +1,52 @@ | |||
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 | /* | ||
8 | * Example usage: | ||
9 | * | ||
10 | * $ echo credential challenge | openssl sha256 -binary | base64 > cred_param | ||
11 | * $ echo relying party >> cred_param | ||
12 | * $ echo user name >> cred_param | ||
13 | * $ dd if=/dev/urandom bs=1 count=32 | base64 >> cred_param | ||
14 | * $ fido2-cred -M -i cred_param /dev/hidraw5 | fido2-cred -V -o cred | ||
15 | */ | ||
16 | |||
17 | #include <fido.h> | ||
18 | #include <stdio.h> | ||
19 | #include <stdlib.h> | ||
20 | #include <string.h> | ||
21 | |||
22 | #include "../openbsd-compat/openbsd-compat.h" | ||
23 | #include "extern.h" | ||
24 | |||
25 | void | ||
26 | usage(void) | ||
27 | { | ||
28 | fprintf(stderr, | ||
29 | "usage: fido2-cred -M [-dhqruv] [-i input_file] [-o output_file] device [type]\n" | ||
30 | " fido2-cred -V [-dhv] [-i input_file] [-o output_file] [type]\n" | ||
31 | ); | ||
32 | |||
33 | exit(1); | ||
34 | } | ||
35 | |||
36 | int | ||
37 | main(int argc, char **argv) | ||
38 | { | ||
39 | if (argc < 2 || strlen(argv[1]) != 2 || argv[1][0] != '-') | ||
40 | usage(); | ||
41 | |||
42 | switch (argv[1][1]) { | ||
43 | case 'M': | ||
44 | return (cred_make(--argc, ++argv)); | ||
45 | case 'V': | ||
46 | return (cred_verify(--argc, ++argv)); | ||
47 | } | ||
48 | |||
49 | usage(); | ||
50 | |||
51 | /* NOTREACHED */ | ||
52 | } | ||
diff --git a/tools/fido2-token.c b/tools/fido2-token.c new file mode 100644 index 0000000..0b02fea --- /dev/null +++ b/tools/fido2-token.c | |||
@@ -0,0 +1,95 @@ | |||
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.h> | ||
8 | #include <stdio.h> | ||
9 | #include <stdlib.h> | ||
10 | #include <string.h> | ||
11 | |||
12 | #include "../openbsd-compat/openbsd-compat.h" | ||
13 | #include "extern.h" | ||
14 | |||
15 | static int action; | ||
16 | |||
17 | void | ||
18 | usage(void) | ||
19 | { | ||
20 | fprintf(stderr, | ||
21 | "usage: fido2-token [-CR] [-d] device\n" | ||
22 | " fido2-token -D [-de] -i id device\n" | ||
23 | " fido2-token -I [-cd] [-k rp_id -i cred_id] device\n" | ||
24 | " fido2-token -L [-der] [-k rp_id] [device]\n" | ||
25 | " fido2-token -S [-de] [-i template_id -n template_name] device\n" | ||
26 | " fido2-token -V\n" | ||
27 | ); | ||
28 | |||
29 | exit(1); | ||
30 | } | ||
31 | |||
32 | static void | ||
33 | setaction(int ch) | ||
34 | { | ||
35 | if (action) | ||
36 | usage(); | ||
37 | action = ch; | ||
38 | } | ||
39 | |||
40 | int | ||
41 | main(int argc, char **argv) | ||
42 | { | ||
43 | int ch; | ||
44 | int flags = 0; | ||
45 | char *device; | ||
46 | |||
47 | while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { | ||
48 | switch (ch) { | ||
49 | case 'b': | ||
50 | case 'c': | ||
51 | case 'e': | ||
52 | case 'i': | ||
53 | case 'k': | ||
54 | case 'n': | ||
55 | case 'r': | ||
56 | break; /* ignore */ | ||
57 | case 'd': | ||
58 | flags = FIDO_DEBUG; | ||
59 | break; | ||
60 | default: | ||
61 | setaction(ch); | ||
62 | break; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | if (argc - optind == 1) | ||
67 | device = argv[optind]; | ||
68 | else | ||
69 | device = NULL; | ||
70 | |||
71 | fido_init(flags); | ||
72 | |||
73 | switch (action) { | ||
74 | case 'C': | ||
75 | return (pin_change(device)); | ||
76 | case 'D': | ||
77 | return (token_delete(argc, argv, device)); | ||
78 | case 'I': | ||
79 | return (token_info(argc, argv, device)); | ||
80 | case 'L': | ||
81 | return (token_list(argc, argv, device)); | ||
82 | case 'R': | ||
83 | return (token_reset(device)); | ||
84 | case 'S': | ||
85 | return (token_set(argc, argv, device)); | ||
86 | case 'V': | ||
87 | fprintf(stderr, "%d.%d.%d\n", _FIDO_MAJOR, _FIDO_MINOR, | ||
88 | _FIDO_PATCH); | ||
89 | exit(0); | ||
90 | } | ||
91 | |||
92 | usage(); | ||
93 | |||
94 | /* NOTREACHED */ | ||
95 | } | ||
diff --git a/tools/pin.c b/tools/pin.c new file mode 100644 index 0000000..eab178a --- /dev/null +++ b/tools/pin.c | |||
@@ -0,0 +1,146 @@ | |||
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.h> | ||
8 | #include <stdbool.h> | ||
9 | #include <stdio.h> | ||
10 | #include <stdlib.h> | ||
11 | #include <string.h> | ||
12 | #ifdef HAVE_UNISTD_H | ||
13 | #include <unistd.h> | ||
14 | #endif | ||
15 | |||
16 | #include "../openbsd-compat/openbsd-compat.h" | ||
17 | #include "extern.h" | ||
18 | |||
19 | int | ||
20 | pin_set(char *path) | ||
21 | { | ||
22 | fido_dev_t *dev = NULL; | ||
23 | char prompt[1024]; | ||
24 | char pin1[1024]; | ||
25 | char pin2[1024]; | ||
26 | int r; | ||
27 | int status = 1; | ||
28 | |||
29 | if (path == NULL) | ||
30 | usage(); | ||
31 | |||
32 | dev = open_dev(path); | ||
33 | |||
34 | r = snprintf(prompt, sizeof(prompt), "Enter new PIN for %s: ", path); | ||
35 | if (r < 0 || (size_t)r >= sizeof(prompt)) { | ||
36 | warnx("snprintf"); | ||
37 | goto out; | ||
38 | } | ||
39 | |||
40 | if (!readpassphrase(prompt, pin1, sizeof(pin1), RPP_ECHO_OFF)) { | ||
41 | warnx("readpassphrase"); | ||
42 | goto out; | ||
43 | } | ||
44 | |||
45 | r = snprintf(prompt, sizeof(prompt), "Enter the same PIN again: "); | ||
46 | if (r < 0 || (size_t)r >= sizeof(prompt)) { | ||
47 | warnx("snprintf"); | ||
48 | goto out; | ||
49 | } | ||
50 | |||
51 | if (!readpassphrase(prompt, pin2, sizeof(pin2), RPP_ECHO_OFF)) { | ||
52 | warnx("readpassphrase"); | ||
53 | goto out; | ||
54 | } | ||
55 | |||
56 | if (strcmp(pin1, pin2) != 0) { | ||
57 | fprintf(stderr, "PINs do not match. Try again.\n"); | ||
58 | goto out; | ||
59 | } | ||
60 | |||
61 | if ((r = fido_dev_set_pin(dev, pin1, NULL)) != FIDO_OK) { | ||
62 | warnx("fido_dev_set_pin: %s", fido_strerr(r)); | ||
63 | goto out; | ||
64 | } | ||
65 | |||
66 | fido_dev_close(dev); | ||
67 | fido_dev_free(&dev); | ||
68 | |||
69 | status = 0; | ||
70 | out: | ||
71 | explicit_bzero(pin1, sizeof(pin1)); | ||
72 | explicit_bzero(pin2, sizeof(pin2)); | ||
73 | |||
74 | exit(status); | ||
75 | } | ||
76 | |||
77 | int | ||
78 | pin_change(char *path) | ||
79 | { | ||
80 | fido_dev_t *dev = NULL; | ||
81 | char prompt[1024]; | ||
82 | char pin0[1024]; | ||
83 | char pin1[1024]; | ||
84 | char pin2[1024]; | ||
85 | int r; | ||
86 | int status = 1; | ||
87 | |||
88 | if (path == NULL) | ||
89 | usage(); | ||
90 | |||
91 | dev = open_dev(path); | ||
92 | |||
93 | r = snprintf(prompt, sizeof(prompt), "Enter current PIN for %s: ", path); | ||
94 | if (r < 0 || (size_t)r >= sizeof(prompt)) { | ||
95 | warnx("snprintf"); | ||
96 | goto out; | ||
97 | } | ||
98 | |||
99 | if (!readpassphrase(prompt, pin0, sizeof(pin0), RPP_ECHO_OFF)) { | ||
100 | warnx("readpassphrase"); | ||
101 | goto out; | ||
102 | } | ||
103 | |||
104 | r = snprintf(prompt, sizeof(prompt), "Enter new PIN for %s: ", path); | ||
105 | if (r < 0 || (size_t)r >= sizeof(prompt)) { | ||
106 | warnx("snprintf"); | ||
107 | goto out; | ||
108 | } | ||
109 | |||
110 | if (!readpassphrase(prompt, pin1, sizeof(pin1), RPP_ECHO_OFF)) { | ||
111 | warnx("readpassphrase"); | ||
112 | goto out; | ||
113 | } | ||
114 | |||
115 | r = snprintf(prompt, sizeof(prompt), "Enter the same PIN again: "); | ||
116 | if (r < 0 || (size_t)r >= sizeof(prompt)) { | ||
117 | warnx("snprintf"); | ||
118 | goto out; | ||
119 | } | ||
120 | |||
121 | if (!readpassphrase(prompt, pin2, sizeof(pin2), RPP_ECHO_OFF)) { | ||
122 | warnx("readpassphrase"); | ||
123 | goto out; | ||
124 | } | ||
125 | |||
126 | if (strcmp(pin1, pin2) != 0) { | ||
127 | fprintf(stderr, "PINs do not match. Try again.\n"); | ||
128 | goto out; | ||
129 | } | ||
130 | |||
131 | if ((r = fido_dev_set_pin(dev, pin1, pin0)) != FIDO_OK) { | ||
132 | warnx("fido_dev_set_pin: %s", fido_strerr(r)); | ||
133 | goto out; | ||
134 | } | ||
135 | |||
136 | fido_dev_close(dev); | ||
137 | fido_dev_free(&dev); | ||
138 | |||
139 | status = 0; | ||
140 | out: | ||
141 | explicit_bzero(pin0, sizeof(pin0)); | ||
142 | explicit_bzero(pin1, sizeof(pin1)); | ||
143 | explicit_bzero(pin2, sizeof(pin2)); | ||
144 | |||
145 | exit(status); | ||
146 | } | ||
diff --git a/tools/sk-libfido2.c b/tools/sk-libfido2.c new file mode 100644 index 0000000..15aa813 --- /dev/null +++ b/tools/sk-libfido2.c | |||
@@ -0,0 +1,784 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2019 Markus Friedl | ||
3 | * | ||
4 | * Permission to use, copy, modify, and distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include <fcntl.h> | ||
18 | #include <stdint.h> | ||
19 | #include <stdlib.h> | ||
20 | #include <string.h> | ||
21 | #include <stdio.h> | ||
22 | #include <stddef.h> | ||
23 | #include <stdarg.h> | ||
24 | #ifdef HAVE_UNISTD_H | ||
25 | #include <unistd.h> | ||
26 | #endif | ||
27 | |||
28 | #ifdef WITH_OPENSSL | ||
29 | #include <openssl/opensslv.h> | ||
30 | #include <openssl/crypto.h> | ||
31 | #include <openssl/bn.h> | ||
32 | #include <openssl/ec.h> | ||
33 | #include <openssl/ecdsa.h> | ||
34 | #endif /* WITH_OPENSSL */ | ||
35 | |||
36 | #include <fido.h> | ||
37 | |||
38 | #ifndef SK_STANDALONE | ||
39 | #include "log.h" | ||
40 | #include "xmalloc.h" | ||
41 | #endif | ||
42 | |||
43 | /* #define SK_DEBUG 1 */ | ||
44 | |||
45 | #if defined(_WIN32) | ||
46 | #include <windows.h> | ||
47 | #include <winternl.h> | ||
48 | #include <winerror.h> | ||
49 | #include <bcrypt.h> | ||
50 | #include <sal.h> | ||
51 | #endif | ||
52 | |||
53 | #define MAX_FIDO_DEVICES 256 | ||
54 | |||
55 | /* Compatibility with OpenSSL 1.0.x */ | ||
56 | #if (OPENSSL_VERSION_NUMBER < 0x10100000L) | ||
57 | #define ECDSA_SIG_get0(sig, pr, ps) \ | ||
58 | do { \ | ||
59 | (*pr) = sig->r; \ | ||
60 | (*ps) = sig->s; \ | ||
61 | } while (0) | ||
62 | #endif | ||
63 | |||
64 | #define SK_VERSION_MAJOR 0x00020000 /* current API version */ | ||
65 | |||
66 | /* Flags */ | ||
67 | #define SK_USER_PRESENCE_REQD 0x01 | ||
68 | |||
69 | /* Algs */ | ||
70 | #define SK_ECDSA 0x00 | ||
71 | #define SK_ED25519 0x01 | ||
72 | |||
73 | struct sk_enroll_response { | ||
74 | uint8_t *public_key; | ||
75 | size_t public_key_len; | ||
76 | uint8_t *key_handle; | ||
77 | size_t key_handle_len; | ||
78 | uint8_t *signature; | ||
79 | size_t signature_len; | ||
80 | uint8_t *attestation_cert; | ||
81 | size_t attestation_cert_len; | ||
82 | }; | ||
83 | |||
84 | struct sk_sign_response { | ||
85 | uint8_t flags; | ||
86 | uint32_t counter; | ||
87 | uint8_t *sig_r; | ||
88 | size_t sig_r_len; | ||
89 | uint8_t *sig_s; | ||
90 | size_t sig_s_len; | ||
91 | }; | ||
92 | |||
93 | /* If building as part of OpenSSH, then rename exported functions */ | ||
94 | #if !defined(SK_STANDALONE) | ||
95 | #define sk_api_version ssh_sk_api_version | ||
96 | #define sk_enroll ssh_sk_enroll | ||
97 | #define sk_sign ssh_sk_sign | ||
98 | #endif | ||
99 | |||
100 | /* Return the version of the middleware API */ | ||
101 | uint32_t sk_api_version(void); | ||
102 | |||
103 | /* Enroll a U2F key (private key generation) */ | ||
104 | int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, | ||
105 | const char *application, uint8_t flags, | ||
106 | struct sk_enroll_response **enroll_response); | ||
107 | |||
108 | /* Sign a challenge */ | ||
109 | int sk_sign(int alg, const uint8_t *message, size_t message_len, | ||
110 | const char *application, const uint8_t *key_handle, size_t key_handle_len, | ||
111 | uint8_t flags, struct sk_sign_response **sign_response); | ||
112 | |||
113 | #ifdef SK_DEBUG | ||
114 | static void skdebug(const char *func, const char *fmt, ...) | ||
115 | __attribute__((__format__ (printf, 2, 3))); | ||
116 | |||
117 | static void | ||
118 | skdebug(const char *func, const char *fmt, ...) | ||
119 | { | ||
120 | #if !defined(SK_STANDALONE) | ||
121 | char *msg; | ||
122 | va_list ap; | ||
123 | |||
124 | va_start(ap, fmt); | ||
125 | xvasprintf(&msg, fmt, ap); | ||
126 | va_end(ap); | ||
127 | debug("%s: %s", func, msg); | ||
128 | free(msg); | ||
129 | #else | ||
130 | va_list ap; | ||
131 | |||
132 | va_start(ap, fmt); | ||
133 | fprintf(stderr, "%s: ", func); | ||
134 | vfprintf(stderr, fmt, ap); | ||
135 | fputc('\n', stderr); | ||
136 | va_end(ap); | ||
137 | #endif /* !SK_STANDALONE */ | ||
138 | } | ||
139 | #else | ||
140 | #define skdebug(...) do { /* nothing */ } while (0) | ||
141 | #endif /* SK_DEBUG */ | ||
142 | |||
143 | uint32_t | ||
144 | sk_api_version(void) | ||
145 | { | ||
146 | return SK_VERSION_MAJOR; | ||
147 | } | ||
148 | |||
149 | /* Select the first identified FIDO device attached to the system */ | ||
150 | static char * | ||
151 | pick_first_device(void) | ||
152 | { | ||
153 | char *ret = NULL; | ||
154 | fido_dev_info_t *devlist = NULL; | ||
155 | size_t olen = 0; | ||
156 | int r; | ||
157 | const fido_dev_info_t *di; | ||
158 | |||
159 | if ((devlist = fido_dev_info_new(1)) == NULL) { | ||
160 | skdebug(__func__, "fido_dev_info_new failed"); | ||
161 | goto out; | ||
162 | } | ||
163 | if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) { | ||
164 | skdebug(__func__, "fido_dev_info_manifest failed: %s", | ||
165 | fido_strerr(r)); | ||
166 | goto out; | ||
167 | } | ||
168 | if (olen != 1) { | ||
169 | skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen); | ||
170 | goto out; | ||
171 | } | ||
172 | di = fido_dev_info_ptr(devlist, 0); | ||
173 | if ((ret = strdup(fido_dev_info_path(di))) == NULL) { | ||
174 | skdebug(__func__, "fido_dev_info_path failed"); | ||
175 | goto out; | ||
176 | } | ||
177 | out: | ||
178 | fido_dev_info_free(&devlist, 1); | ||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | #if defined(HAVE_ARC4RANDOM_BUF) | ||
183 | static int | ||
184 | get_random_challenge(uint8_t *ptr, size_t len) | ||
185 | { | ||
186 | arc4random_buf(ptr, len); | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | #elif defined(HAVE_GETENTROPY) | ||
191 | static int | ||
192 | get_random_challenge(uint8_t *ptr, size_t len) | ||
193 | { | ||
194 | if (getentropy(ptr, len) == -1) { | ||
195 | skdebug(__func__, "getentropy failed"); | ||
196 | return -1; | ||
197 | } | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | #elif defined(HAS_DEV_URANDOM) | ||
202 | static int | ||
203 | get_random_challenge(uint8_t *ptr, size_t len) | ||
204 | { | ||
205 | int fd; | ||
206 | ssize_t n; | ||
207 | |||
208 | if ((fd = open(FIDO_RANDOM_DEV, O_RDONLY)) < 0) { | ||
209 | skdebug(__func__, "open %s failed", FIDO_RANDOM_DEV); | ||
210 | return -1; | ||
211 | } | ||
212 | |||
213 | n = read(fd, ptr, len); | ||
214 | close(fd); | ||
215 | |||
216 | if (n < 0 || (size_t)n != len) { | ||
217 | skdebug(__func__, "read from %s failed", FIDO_RANDOM_DEV); | ||
218 | return -1; | ||
219 | } | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | #elif defined(_WIN32) | ||
224 | static int | ||
225 | get_random_challenge(uint8_t *ptr, size_t len) | ||
226 | { | ||
227 | NTSTATUS status; | ||
228 | |||
229 | status = BCryptGenRandom(NULL, ptr, len, | ||
230 | BCRYPT_USE_SYSTEM_PREFERRED_RNG); | ||
231 | if (!NT_SUCCESS(status)) | ||
232 | return -1; | ||
233 | |||
234 | return 0; | ||
235 | } | ||
236 | #else | ||
237 | #error "please provide an implementation of get_random_challenge() for your platform" | ||
238 | #endif | ||
239 | |||
240 | /* Check if the specified key handle exists on a given device. */ | ||
241 | static int | ||
242 | try_device(fido_dev_t *dev, const char *application, | ||
243 | const uint8_t *key_handle, size_t key_handle_len) | ||
244 | { | ||
245 | fido_assert_t *assert = NULL; | ||
246 | uint8_t challenge[32]; | ||
247 | int r = FIDO_ERR_INTERNAL; | ||
248 | |||
249 | if (get_random_challenge(challenge, sizeof(challenge)) == -1) { | ||
250 | skdebug(__func__, "get_random_challenge failed"); | ||
251 | goto out; | ||
252 | } | ||
253 | |||
254 | if ((assert = fido_assert_new()) == NULL) { | ||
255 | skdebug(__func__, "fido_assert_new failed"); | ||
256 | goto out; | ||
257 | } | ||
258 | if ((r = fido_assert_set_clientdata_hash(assert, challenge, | ||
259 | sizeof(challenge))) != FIDO_OK) { | ||
260 | skdebug(__func__, "fido_assert_set_clientdata_hash: %s", | ||
261 | fido_strerr(r)); | ||
262 | goto out; | ||
263 | } | ||
264 | if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { | ||
265 | skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); | ||
266 | goto out; | ||
267 | } | ||
268 | if ((r = fido_assert_allow_cred(assert, key_handle, | ||
269 | key_handle_len)) != FIDO_OK) { | ||
270 | skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); | ||
271 | goto out; | ||
272 | } | ||
273 | if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) { | ||
274 | skdebug(__func__, "fido_assert_up: %s", fido_strerr(r)); | ||
275 | goto out; | ||
276 | } | ||
277 | r = fido_dev_get_assert(dev, assert, NULL); | ||
278 | skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); | ||
279 | if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) { | ||
280 | /* U2F tokens may return this */ | ||
281 | r = FIDO_OK; | ||
282 | } | ||
283 | out: | ||
284 | fido_assert_free(&assert); | ||
285 | |||
286 | return r != FIDO_OK ? -1 : 0; | ||
287 | } | ||
288 | |||
289 | /* Iterate over configured devices looking for a specific key handle */ | ||
290 | static fido_dev_t * | ||
291 | find_device(const char *application, const uint8_t *key_handle, | ||
292 | size_t key_handle_len) | ||
293 | { | ||
294 | fido_dev_info_t *devlist = NULL; | ||
295 | fido_dev_t *dev = NULL; | ||
296 | size_t devlist_len = 0, i; | ||
297 | const char *path; | ||
298 | int r; | ||
299 | |||
300 | if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { | ||
301 | skdebug(__func__, "fido_dev_info_new failed"); | ||
302 | goto out; | ||
303 | } | ||
304 | if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES, | ||
305 | &devlist_len)) != FIDO_OK) { | ||
306 | skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r)); | ||
307 | goto out; | ||
308 | } | ||
309 | |||
310 | skdebug(__func__, "found %zu device(s)", devlist_len); | ||
311 | |||
312 | for (i = 0; i < devlist_len; i++) { | ||
313 | const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); | ||
314 | |||
315 | if (di == NULL) { | ||
316 | skdebug(__func__, "fido_dev_info_ptr %zu failed", i); | ||
317 | continue; | ||
318 | } | ||
319 | if ((path = fido_dev_info_path(di)) == NULL) { | ||
320 | skdebug(__func__, "fido_dev_info_path %zu failed", i); | ||
321 | continue; | ||
322 | } | ||
323 | skdebug(__func__, "trying device %zu: %s", i, path); | ||
324 | if ((dev = fido_dev_new()) == NULL) { | ||
325 | skdebug(__func__, "fido_dev_new failed"); | ||
326 | continue; | ||
327 | } | ||
328 | if ((r = fido_dev_open(dev, path)) != FIDO_OK) { | ||
329 | skdebug(__func__, "fido_dev_open failed"); | ||
330 | fido_dev_free(&dev); | ||
331 | continue; | ||
332 | } | ||
333 | if (try_device(dev, application, key_handle, | ||
334 | key_handle_len) == 0) { | ||
335 | skdebug(__func__, "found key"); | ||
336 | break; | ||
337 | } | ||
338 | fido_dev_close(dev); | ||
339 | fido_dev_free(&dev); | ||
340 | } | ||
341 | |||
342 | out: | ||
343 | if (devlist != NULL) | ||
344 | fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); | ||
345 | |||
346 | return dev; | ||
347 | } | ||
348 | |||
349 | #ifdef WITH_OPENSSL | ||
350 | /* | ||
351 | * The key returned via fido_cred_pubkey_ptr() is in affine coordinates, | ||
352 | * but the API expects a SEC1 octet string. | ||
353 | */ | ||
354 | static int | ||
355 | pack_public_key_ecdsa(fido_cred_t *cred, struct sk_enroll_response *response) | ||
356 | { | ||
357 | const uint8_t *ptr; | ||
358 | BIGNUM *x = NULL, *y = NULL; | ||
359 | EC_POINT *q = NULL; | ||
360 | EC_GROUP *g = NULL; | ||
361 | int ret = -1; | ||
362 | |||
363 | response->public_key = NULL; | ||
364 | response->public_key_len = 0; | ||
365 | |||
366 | if ((x = BN_new()) == NULL || | ||
367 | (y = BN_new()) == NULL || | ||
368 | (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || | ||
369 | (q = EC_POINT_new(g)) == NULL) { | ||
370 | skdebug(__func__, "libcrypto setup failed"); | ||
371 | goto out; | ||
372 | } | ||
373 | if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { | ||
374 | skdebug(__func__, "fido_cred_pubkey_ptr failed"); | ||
375 | goto out; | ||
376 | } | ||
377 | if (fido_cred_pubkey_len(cred) != 64) { | ||
378 | skdebug(__func__, "bad fido_cred_pubkey_len %zu", | ||
379 | fido_cred_pubkey_len(cred)); | ||
380 | goto out; | ||
381 | } | ||
382 | |||
383 | if (BN_bin2bn(ptr, 32, x) == NULL || | ||
384 | BN_bin2bn(ptr + 32, 32, y) == NULL) { | ||
385 | skdebug(__func__, "BN_bin2bn failed"); | ||
386 | goto out; | ||
387 | } | ||
388 | if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) { | ||
389 | skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed"); | ||
390 | goto out; | ||
391 | } | ||
392 | response->public_key_len = EC_POINT_point2oct(g, q, | ||
393 | POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); | ||
394 | if (response->public_key_len == 0 || response->public_key_len > 2048) { | ||
395 | skdebug(__func__, "bad pubkey length %zu", | ||
396 | response->public_key_len); | ||
397 | goto out; | ||
398 | } | ||
399 | if ((response->public_key = malloc(response->public_key_len)) == NULL) { | ||
400 | skdebug(__func__, "malloc pubkey failed"); | ||
401 | goto out; | ||
402 | } | ||
403 | if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED, | ||
404 | response->public_key, response->public_key_len, NULL) == 0) { | ||
405 | skdebug(__func__, "EC_POINT_point2oct failed"); | ||
406 | goto out; | ||
407 | } | ||
408 | /* success */ | ||
409 | ret = 0; | ||
410 | out: | ||
411 | if (ret != 0 && response->public_key != NULL) { | ||
412 | memset(response->public_key, 0, response->public_key_len); | ||
413 | free(response->public_key); | ||
414 | response->public_key = NULL; | ||
415 | } | ||
416 | EC_POINT_free(q); | ||
417 | EC_GROUP_free(g); | ||
418 | BN_clear_free(x); | ||
419 | BN_clear_free(y); | ||
420 | return ret; | ||
421 | } | ||
422 | #endif /* WITH_OPENSSL */ | ||
423 | |||
424 | static int | ||
425 | pack_public_key_ed25519(fido_cred_t *cred, struct sk_enroll_response *response) | ||
426 | { | ||
427 | const uint8_t *ptr; | ||
428 | size_t len; | ||
429 | int ret = -1; | ||
430 | |||
431 | response->public_key = NULL; | ||
432 | response->public_key_len = 0; | ||
433 | |||
434 | if ((len = fido_cred_pubkey_len(cred)) != 32) { | ||
435 | skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len); | ||
436 | goto out; | ||
437 | } | ||
438 | if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { | ||
439 | skdebug(__func__, "fido_cred_pubkey_ptr failed"); | ||
440 | goto out; | ||
441 | } | ||
442 | response->public_key_len = len; | ||
443 | if ((response->public_key = malloc(response->public_key_len)) == NULL) { | ||
444 | skdebug(__func__, "malloc pubkey failed"); | ||
445 | goto out; | ||
446 | } | ||
447 | memcpy(response->public_key, ptr, len); | ||
448 | ret = 0; | ||
449 | out: | ||
450 | if (ret != 0) | ||
451 | free(response->public_key); | ||
452 | return ret; | ||
453 | } | ||
454 | |||
455 | static int | ||
456 | pack_public_key(int alg, fido_cred_t *cred, struct sk_enroll_response *response) | ||
457 | { | ||
458 | switch(alg) { | ||
459 | #ifdef WITH_OPENSSL | ||
460 | case SK_ECDSA: | ||
461 | return pack_public_key_ecdsa(cred, response); | ||
462 | #endif /* WITH_OPENSSL */ | ||
463 | case SK_ED25519: | ||
464 | return pack_public_key_ed25519(cred, response); | ||
465 | default: | ||
466 | return -1; | ||
467 | } | ||
468 | } | ||
469 | |||
470 | int | ||
471 | sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, | ||
472 | const char *application, uint8_t flags, | ||
473 | struct sk_enroll_response **enroll_response) | ||
474 | { | ||
475 | fido_cred_t *cred = NULL; | ||
476 | fido_dev_t *dev = NULL; | ||
477 | const uint8_t *ptr; | ||
478 | uint8_t user_id[32]; | ||
479 | struct sk_enroll_response *response = NULL; | ||
480 | size_t len; | ||
481 | int cose_alg; | ||
482 | int ret = -1; | ||
483 | int r; | ||
484 | char *device = NULL; | ||
485 | |||
486 | (void)flags; /* XXX; unused */ | ||
487 | #ifdef SK_DEBUG | ||
488 | fido_init(FIDO_DEBUG); | ||
489 | #endif | ||
490 | if (enroll_response == NULL) { | ||
491 | skdebug(__func__, "enroll_response == NULL"); | ||
492 | goto out; | ||
493 | } | ||
494 | *enroll_response = NULL; | ||
495 | switch(alg) { | ||
496 | #ifdef WITH_OPENSSL | ||
497 | case SK_ECDSA: | ||
498 | cose_alg = COSE_ES256; | ||
499 | break; | ||
500 | #endif /* WITH_OPENSSL */ | ||
501 | case SK_ED25519: | ||
502 | cose_alg = COSE_EDDSA; | ||
503 | break; | ||
504 | default: | ||
505 | skdebug(__func__, "unsupported key type %d", alg); | ||
506 | goto out; | ||
507 | } | ||
508 | if ((device = pick_first_device()) == NULL) { | ||
509 | skdebug(__func__, "pick_first_device failed"); | ||
510 | goto out; | ||
511 | } | ||
512 | skdebug(__func__, "using device %s", device); | ||
513 | if ((cred = fido_cred_new()) == NULL) { | ||
514 | skdebug(__func__, "fido_cred_new failed"); | ||
515 | goto out; | ||
516 | } | ||
517 | memset(user_id, 0, sizeof(user_id)); | ||
518 | if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) { | ||
519 | skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r)); | ||
520 | goto out; | ||
521 | } | ||
522 | if ((r = fido_cred_set_clientdata_hash(cred, challenge, | ||
523 | challenge_len)) != FIDO_OK) { | ||
524 | skdebug(__func__, "fido_cred_set_clientdata_hash: %s", | ||
525 | fido_strerr(r)); | ||
526 | goto out; | ||
527 | } | ||
528 | if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id), | ||
529 | "openssh", "openssh", NULL)) != FIDO_OK) { | ||
530 | skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r)); | ||
531 | goto out; | ||
532 | } | ||
533 | if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) { | ||
534 | skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r)); | ||
535 | goto out; | ||
536 | } | ||
537 | if ((dev = fido_dev_new()) == NULL) { | ||
538 | skdebug(__func__, "fido_dev_new failed"); | ||
539 | goto out; | ||
540 | } | ||
541 | if ((r = fido_dev_open(dev, device)) != FIDO_OK) { | ||
542 | skdebug(__func__, "fido_dev_open: %s", fido_strerr(r)); | ||
543 | goto out; | ||
544 | } | ||
545 | if ((r = fido_dev_make_cred(dev, cred, NULL)) != FIDO_OK) { | ||
546 | skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r)); | ||
547 | goto out; | ||
548 | } | ||
549 | if (fido_cred_x5c_ptr(cred) != NULL) { | ||
550 | if ((r = fido_cred_verify(cred)) != FIDO_OK) { | ||
551 | skdebug(__func__, "fido_cred_verify: %s", | ||
552 | fido_strerr(r)); | ||
553 | goto out; | ||
554 | } | ||
555 | } else { | ||
556 | skdebug(__func__, "self-attested credential"); | ||
557 | if ((r = fido_cred_verify_self(cred)) != FIDO_OK) { | ||
558 | skdebug(__func__, "fido_cred_verify_self: %s", | ||
559 | fido_strerr(r)); | ||
560 | goto out; | ||
561 | } | ||
562 | } | ||
563 | if ((response = calloc(1, sizeof(*response))) == NULL) { | ||
564 | skdebug(__func__, "calloc response failed"); | ||
565 | goto out; | ||
566 | } | ||
567 | if (pack_public_key(alg, cred, response) != 0) { | ||
568 | skdebug(__func__, "pack_public_key failed"); | ||
569 | goto out; | ||
570 | } | ||
571 | if ((ptr = fido_cred_id_ptr(cred)) != NULL) { | ||
572 | len = fido_cred_id_len(cred); | ||
573 | if ((response->key_handle = calloc(1, len)) == NULL) { | ||
574 | skdebug(__func__, "calloc key handle failed"); | ||
575 | goto out; | ||
576 | } | ||
577 | memcpy(response->key_handle, ptr, len); | ||
578 | response->key_handle_len = len; | ||
579 | } | ||
580 | if ((ptr = fido_cred_sig_ptr(cred)) != NULL) { | ||
581 | len = fido_cred_sig_len(cred); | ||
582 | if ((response->signature = calloc(1, len)) == NULL) { | ||
583 | skdebug(__func__, "calloc signature failed"); | ||
584 | goto out; | ||
585 | } | ||
586 | memcpy(response->signature, ptr, len); | ||
587 | response->signature_len = len; | ||
588 | } | ||
589 | if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) { | ||
590 | len = fido_cred_x5c_len(cred); | ||
591 | if ((response->attestation_cert = calloc(1, len)) == NULL) { | ||
592 | skdebug(__func__, "calloc attestation cert failed"); | ||
593 | goto out; | ||
594 | } | ||
595 | memcpy(response->attestation_cert, ptr, len); | ||
596 | response->attestation_cert_len = len; | ||
597 | } | ||
598 | *enroll_response = response; | ||
599 | response = NULL; | ||
600 | ret = 0; | ||
601 | out: | ||
602 | free(device); | ||
603 | if (response != NULL) { | ||
604 | free(response->public_key); | ||
605 | free(response->key_handle); | ||
606 | free(response->signature); | ||
607 | free(response->attestation_cert); | ||
608 | free(response); | ||
609 | } | ||
610 | if (dev != NULL) { | ||
611 | fido_dev_close(dev); | ||
612 | fido_dev_free(&dev); | ||
613 | } | ||
614 | if (cred != NULL) { | ||
615 | fido_cred_free(&cred); | ||
616 | } | ||
617 | return ret; | ||
618 | } | ||
619 | |||
620 | #ifdef WITH_OPENSSL | ||
621 | static int | ||
622 | pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response) | ||
623 | { | ||
624 | ECDSA_SIG *sig = NULL; | ||
625 | const BIGNUM *sig_r, *sig_s; | ||
626 | const unsigned char *cp; | ||
627 | size_t sig_len; | ||
628 | int ret = -1; | ||
629 | |||
630 | cp = fido_assert_sig_ptr(assert, 0); | ||
631 | sig_len = fido_assert_sig_len(assert, 0); | ||
632 | if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) { | ||
633 | skdebug(__func__, "d2i_ECDSA_SIG failed"); | ||
634 | goto out; | ||
635 | } | ||
636 | ECDSA_SIG_get0(sig, &sig_r, &sig_s); | ||
637 | response->sig_r_len = BN_num_bytes(sig_r); | ||
638 | response->sig_s_len = BN_num_bytes(sig_s); | ||
639 | if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL || | ||
640 | (response->sig_s = calloc(1, response->sig_s_len)) == NULL) { | ||
641 | skdebug(__func__, "calloc signature failed"); | ||
642 | goto out; | ||
643 | } | ||
644 | BN_bn2bin(sig_r, response->sig_r); | ||
645 | BN_bn2bin(sig_s, response->sig_s); | ||
646 | ret = 0; | ||
647 | out: | ||
648 | ECDSA_SIG_free(sig); | ||
649 | if (ret != 0) { | ||
650 | free(response->sig_r); | ||
651 | free(response->sig_s); | ||
652 | response->sig_r = NULL; | ||
653 | response->sig_s = NULL; | ||
654 | } | ||
655 | return ret; | ||
656 | } | ||
657 | #endif /* WITH_OPENSSL */ | ||
658 | |||
659 | static int | ||
660 | pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response) | ||
661 | { | ||
662 | const unsigned char *ptr; | ||
663 | size_t len; | ||
664 | int ret = -1; | ||
665 | |||
666 | ptr = fido_assert_sig_ptr(assert, 0); | ||
667 | len = fido_assert_sig_len(assert, 0); | ||
668 | if (len != 64) { | ||
669 | skdebug(__func__, "bad length %zu", len); | ||
670 | goto out; | ||
671 | } | ||
672 | response->sig_r_len = len; | ||
673 | if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) { | ||
674 | skdebug(__func__, "calloc signature failed"); | ||
675 | goto out; | ||
676 | } | ||
677 | memcpy(response->sig_r, ptr, len); | ||
678 | ret = 0; | ||
679 | out: | ||
680 | if (ret != 0) { | ||
681 | free(response->sig_r); | ||
682 | response->sig_r = NULL; | ||
683 | } | ||
684 | return ret; | ||
685 | } | ||
686 | |||
687 | static int | ||
688 | pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response) | ||
689 | { | ||
690 | switch(alg) { | ||
691 | #ifdef WITH_OPENSSL | ||
692 | case SK_ECDSA: | ||
693 | return pack_sig_ecdsa(assert, response); | ||
694 | #endif /* WITH_OPENSSL */ | ||
695 | case SK_ED25519: | ||
696 | return pack_sig_ed25519(assert, response); | ||
697 | default: | ||
698 | return -1; | ||
699 | } | ||
700 | } | ||
701 | |||
702 | int | ||
703 | sk_sign(int alg, const uint8_t *message, size_t message_len, | ||
704 | const char *application, | ||
705 | const uint8_t *key_handle, size_t key_handle_len, | ||
706 | uint8_t flags, struct sk_sign_response **sign_response) | ||
707 | { | ||
708 | fido_assert_t *assert = NULL; | ||
709 | fido_dev_t *dev = NULL; | ||
710 | struct sk_sign_response *response = NULL; | ||
711 | int ret = -1; | ||
712 | int r; | ||
713 | |||
714 | #ifdef SK_DEBUG | ||
715 | fido_init(FIDO_DEBUG); | ||
716 | #endif | ||
717 | |||
718 | if (sign_response == NULL) { | ||
719 | skdebug(__func__, "sign_response == NULL"); | ||
720 | goto out; | ||
721 | } | ||
722 | *sign_response = NULL; | ||
723 | if ((dev = find_device(application, key_handle, | ||
724 | key_handle_len)) == NULL) { | ||
725 | skdebug(__func__, "couldn't find device for key handle"); | ||
726 | goto out; | ||
727 | } | ||
728 | if ((assert = fido_assert_new()) == NULL) { | ||
729 | skdebug(__func__, "fido_assert_new failed"); | ||
730 | goto out; | ||
731 | } | ||
732 | if ((r = fido_assert_set_clientdata_hash(assert, message, | ||
733 | message_len)) != FIDO_OK) { | ||
734 | skdebug(__func__, "fido_assert_set_clientdata_hash: %s", | ||
735 | fido_strerr(r)); | ||
736 | goto out; | ||
737 | } | ||
738 | if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { | ||
739 | skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); | ||
740 | goto out; | ||
741 | } | ||
742 | if ((r = fido_assert_allow_cred(assert, key_handle, | ||
743 | key_handle_len)) != FIDO_OK) { | ||
744 | skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); | ||
745 | goto out; | ||
746 | } | ||
747 | if ((r = fido_assert_set_up(assert, | ||
748 | (flags & SK_USER_PRESENCE_REQD) ? | ||
749 | FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) { | ||
750 | skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r)); | ||
751 | goto out; | ||
752 | } | ||
753 | if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) { | ||
754 | skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); | ||
755 | goto out; | ||
756 | } | ||
757 | if ((response = calloc(1, sizeof(*response))) == NULL) { | ||
758 | skdebug(__func__, "calloc response failed"); | ||
759 | goto out; | ||
760 | } | ||
761 | response->flags = fido_assert_flags(assert, 0); | ||
762 | response->counter = fido_assert_sigcount(assert, 0); | ||
763 | if (pack_sig(alg, assert, response) != 0) { | ||
764 | skdebug(__func__, "pack_sig failed"); | ||
765 | goto out; | ||
766 | } | ||
767 | *sign_response = response; | ||
768 | response = NULL; | ||
769 | ret = 0; | ||
770 | out: | ||
771 | if (response != NULL) { | ||
772 | free(response->sig_r); | ||
773 | free(response->sig_s); | ||
774 | free(response); | ||
775 | } | ||
776 | if (dev != NULL) { | ||
777 | fido_dev_close(dev); | ||
778 | fido_dev_free(&dev); | ||
779 | } | ||
780 | if (assert != NULL) { | ||
781 | fido_assert_free(&assert); | ||
782 | } | ||
783 | return ret; | ||
784 | } | ||
diff --git a/tools/test.sh b/tools/test.sh new file mode 100755 index 0000000..8159a44 --- /dev/null +++ b/tools/test.sh | |||
@@ -0,0 +1,96 @@ | |||
1 | #!/bin/bash -e | ||
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 | if [[ "$#" -ne 1 ]]; then | ||
8 | echo "usage: test.sh device" 1>&2 | ||
9 | exit 1 | ||
10 | fi | ||
11 | |||
12 | read -p "This script will reset the authenticator at $1, permanently erasing "\ | ||
13 | "its credentials. Are you *SURE* you want to proceed (yes/no)? " | ||
14 | if [[ "${REPLY}" != "yes" ]]; then | ||
15 | exit 1 | ||
16 | fi | ||
17 | |||
18 | echo "Resetting authenticator... (tap to continue!)" | ||
19 | fido2-token -R $1 | ||
20 | |||
21 | CRED_PARAM="$(mktemp /tmp/cred_param.XXXXXXXX)" | ||
22 | ASSERT_PARAM="$(mktemp /tmp/assert_param.XXXXXXXX)" | ||
23 | ASSERT_PUBKEY="$(mktemp /tmp/assert_pubkey.XXXXXXXX)" | ||
24 | ES256_CRED="$(mktemp /tmp/es256_cred.XXXXXXX)" | ||
25 | ES256_CRED_R="$(mktemp /tmp/es256_cred_r.XXXXXXXX)" | ||
26 | |||
27 | cleanup() { | ||
28 | echo "Cleaning up..." | ||
29 | [[ "${CRED_PARAM}" != "" ]] && rm "${CRED_PARAM}" | ||
30 | [[ "${ASSERT_PARAM}" != "" ]] && rm "${ASSERT_PARAM}" | ||
31 | [[ "${ASSERT_PUBKEY}" != "" ]] && rm "${ASSERT_PUBKEY}" | ||
32 | [[ "${ES256_CRED}" != "" ]] && rm "${ES256_CRED}" | ||
33 | [[ "${ES256_CRED_R}" != "" ]] && rm "${ES256_CRED_R}" | ||
34 | } | ||
35 | |||
36 | trap cleanup EXIT | ||
37 | |||
38 | dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 > "${CRED_PARAM}" | ||
39 | echo "Boring Relying Party" >> "${CRED_PARAM}" | ||
40 | echo "Boring User Name" >> "${CRED_PARAM}" | ||
41 | dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 >> "${CRED_PARAM}" | ||
42 | echo "Credential parameters:" | ||
43 | cat "${CRED_PARAM}" | ||
44 | |||
45 | echo "Generating non-resident ES256 credential... (tap to continue!)" | ||
46 | fido2-cred -M -i "${CRED_PARAM}" $1 | fido2-cred -V | tee "${ES256_CRED}" | ||
47 | echo "Generating resident ES256 credential... (tap to continue!)" | ||
48 | fido2-cred -M -r -i "${CRED_PARAM}" $1 | fido2-cred -V | tee "${ES256_CRED_R}" | ||
49 | |||
50 | PIN1="$(dd if=/dev/urandom | tr -cd '[:print:]' | fold -w50 | head -1)" | ||
51 | PIN2="$(dd if=/dev/urandom | tr -cd '[:print:]' | fold -w50 | head -1)" | ||
52 | |||
53 | echo "Setting ${PIN1} as the PIN..." | ||
54 | echo -e "${PIN1}\n${PIN1}" | setsid -w fido2-token -S $1 | ||
55 | echo "Changing PIN from ${PIN1} to ${PIN2}..." | ||
56 | echo -e "${PIN1}\n${PIN2}\n${PIN2}" | setsid -w fido2-token -C $1 | ||
57 | echo "" | ||
58 | |||
59 | echo "Testing non-resident ES256 credential..." | ||
60 | echo "Getting assertion without user presence verification..." | ||
61 | dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 > "${ASSERT_PARAM}" | ||
62 | echo "Boring Relying Party" >> "${ASSERT_PARAM}" | ||
63 | head -1 "${ES256_CRED}" >> "${ASSERT_PARAM}" | ||
64 | tail -n +2 "${ES256_CRED}" > "${ASSERT_PUBKEY}" | ||
65 | echo "Assertion parameters:" | ||
66 | cat "${ASSERT_PARAM}" | ||
67 | fido2-assert -G -i "${ASSERT_PARAM}" $1 | fido2-assert -V "${ASSERT_PUBKEY}" | ||
68 | echo "Checking that the user presence bit is observed..." | ||
69 | ! fido2-assert -G -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}" | ||
70 | echo "Checking that the user verification bit is observed..." | ||
71 | ! fido2-assert -G -i "${ASSERT_PARAM}" $1 | fido2-assert -V -v "${ASSERT_PUBKEY}" | ||
72 | echo "Getting assertion _with_ user presence verification... (tap to continue!)" | ||
73 | fido2-assert -G -p -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}" | ||
74 | echo "Getting assertion _with_ user verification..." | ||
75 | echo -e "${PIN2}\n" | setsid -w fido2-assert -G -v -i "${ASSERT_PARAM}" $1 | \ | ||
76 | fido2-assert -V -v "${ASSERT_PUBKEY}" | ||
77 | echo "" | ||
78 | |||
79 | echo "Testing resident ES256 credential..." | ||
80 | echo "Getting assertion without user presence verification..." | ||
81 | dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 > "${ASSERT_PARAM}" | ||
82 | echo "Boring Relying Party" >> "${ASSERT_PARAM}" | ||
83 | tail -n +2 "${ES256_CRED_R}" > "${ASSERT_PUBKEY}" | ||
84 | echo "Assertion parameters:" | ||
85 | cat "${ASSERT_PARAM}" | ||
86 | fido2-assert -G -r -i "${ASSERT_PARAM}" $1 | fido2-assert -V "${ASSERT_PUBKEY}" | ||
87 | echo "Checking that the user presence bit is observed..." | ||
88 | ! fido2-assert -G -r -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}" | ||
89 | echo "Checking that the user verification bit is observed..." | ||
90 | ! fido2-assert -G -r -i "${ASSERT_PARAM}" $1 | fido2-assert -V -v "${ASSERT_PUBKEY}" | ||
91 | echo "Getting assertion _with_ user presence verification... (tap to continue!)" | ||
92 | fido2-assert -G -r -p -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}" | ||
93 | echo "Getting assertion _with_ user verification..." | ||
94 | echo -e "${PIN2}\n" | setsid -w fido2-assert -G -v -r -i "${ASSERT_PARAM}" $1 | \ | ||
95 | fido2-assert -V -v "${ASSERT_PUBKEY}" | ||
96 | echo "" | ||
diff --git a/tools/token.c b/tools/token.c new file mode 100644 index 0000000..b149208 --- /dev/null +++ b/tools/token.c | |||
@@ -0,0 +1,364 @@ | |||
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.h> | ||
8 | #include <stdbool.h> | ||
9 | #include <stdio.h> | ||
10 | #include <stdlib.h> | ||
11 | #include <string.h> | ||
12 | #ifdef HAVE_UNISTD_H | ||
13 | #include <unistd.h> | ||
14 | #endif | ||
15 | |||
16 | #include "../openbsd-compat/openbsd-compat.h" | ||
17 | #include "extern.h" | ||
18 | |||
19 | static void | ||
20 | format_flags(char *ret, size_t retlen, uint8_t flags) | ||
21 | { | ||
22 | memset(ret, 0, retlen); | ||
23 | |||
24 | if (flags & FIDO_CAP_WINK) { | ||
25 | if (strlcat(ret, "wink,", retlen) >= retlen) | ||
26 | goto toolong; | ||
27 | } else { | ||
28 | if (strlcat(ret, "nowink,", retlen) >= retlen) | ||
29 | goto toolong; | ||
30 | } | ||
31 | |||
32 | if (flags & FIDO_CAP_CBOR) { | ||
33 | if (strlcat(ret, " cbor,", retlen) >= retlen) | ||
34 | goto toolong; | ||
35 | } else { | ||
36 | if (strlcat(ret, " nocbor,", retlen) >= retlen) | ||
37 | goto toolong; | ||
38 | } | ||
39 | |||
40 | if (flags & FIDO_CAP_NMSG) { | ||
41 | if (strlcat(ret, " nomsg", retlen) >= retlen) | ||
42 | goto toolong; | ||
43 | } else { | ||
44 | if (strlcat(ret, " msg", retlen) >= retlen) | ||
45 | goto toolong; | ||
46 | } | ||
47 | |||
48 | return; | ||
49 | toolong: | ||
50 | strlcpy(ret, "toolong", retlen); | ||
51 | } | ||
52 | |||
53 | static void | ||
54 | print_attr(const fido_dev_t *dev) | ||
55 | { | ||
56 | char flags_txt[128]; | ||
57 | |||
58 | printf("proto: 0x%02x\n", fido_dev_protocol(dev)); | ||
59 | printf("major: 0x%02x\n", fido_dev_major(dev)); | ||
60 | printf("minor: 0x%02x\n", fido_dev_minor(dev)); | ||
61 | printf("build: 0x%02x\n", fido_dev_build(dev)); | ||
62 | |||
63 | format_flags(flags_txt, sizeof(flags_txt), fido_dev_flags(dev)); | ||
64 | printf("caps: 0x%02x (%s)\n", fido_dev_flags(dev), flags_txt); | ||
65 | } | ||
66 | |||
67 | static void | ||
68 | print_str_array(const char *label, char * const *sa, size_t len) | ||
69 | { | ||
70 | if (len == 0) | ||
71 | return; | ||
72 | |||
73 | printf("%s strings: ", label); | ||
74 | |||
75 | for (size_t i = 0; i < len; i++) | ||
76 | printf("%s%s", i > 0 ? ", " : "", sa[i]); | ||
77 | |||
78 | printf("\n"); | ||
79 | } | ||
80 | |||
81 | static void | ||
82 | print_opt_array(const char *label, char * const *name, const bool *value, | ||
83 | size_t len) | ||
84 | { | ||
85 | if (len == 0) | ||
86 | return; | ||
87 | |||
88 | printf("%s: ", label); | ||
89 | |||
90 | for (size_t i = 0; i < len; i++) | ||
91 | printf("%s%s%s", i > 0 ? ", " : "", | ||
92 | value[i] ? "" : "no", name[i]); | ||
93 | |||
94 | printf("\n"); | ||
95 | } | ||
96 | |||
97 | static void | ||
98 | print_aaguid(const unsigned char *buf, size_t buflen) | ||
99 | { | ||
100 | printf("aaguid: "); | ||
101 | |||
102 | while (buflen--) | ||
103 | printf("%02x", *buf++); | ||
104 | |||
105 | printf("\n"); | ||
106 | } | ||
107 | |||
108 | static void | ||
109 | print_maxmsgsiz(uint64_t maxmsgsiz) | ||
110 | { | ||
111 | printf("maxmsgsiz: %d\n", (int)maxmsgsiz); | ||
112 | } | ||
113 | |||
114 | static void | ||
115 | print_byte_array(const char *label, const uint8_t *ba, size_t len) | ||
116 | { | ||
117 | if (len == 0) | ||
118 | return; | ||
119 | |||
120 | printf("%s: ", label); | ||
121 | |||
122 | for (size_t i = 0; i < len; i++) | ||
123 | printf("%s%u", i > 0 ? ", " : "", (unsigned)ba[i]); | ||
124 | |||
125 | printf("\n"); | ||
126 | } | ||
127 | |||
128 | int | ||
129 | token_info(int argc, char **argv, char *path) | ||
130 | { | ||
131 | char *cred_id = NULL; | ||
132 | char *rp_id = NULL; | ||
133 | fido_cbor_info_t *ci = NULL; | ||
134 | fido_dev_t *dev = NULL; | ||
135 | int ch; | ||
136 | int credman = 0; | ||
137 | int r; | ||
138 | int retrycnt; | ||
139 | |||
140 | optind = 1; | ||
141 | |||
142 | while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { | ||
143 | switch (ch) { | ||
144 | case 'c': | ||
145 | credman = 1; | ||
146 | break; | ||
147 | case 'i': | ||
148 | cred_id = optarg; | ||
149 | break; | ||
150 | case 'k': | ||
151 | rp_id = optarg; | ||
152 | break; | ||
153 | default: | ||
154 | break; /* ignore */ | ||
155 | } | ||
156 | } | ||
157 | |||
158 | if (path == NULL || (credman && (cred_id != NULL || rp_id != NULL))) | ||
159 | usage(); | ||
160 | |||
161 | dev = open_dev(path); | ||
162 | |||
163 | if (credman) | ||
164 | return (credman_get_metadata(dev, path)); | ||
165 | if (cred_id && rp_id) | ||
166 | return (credman_print_rk(dev, path, rp_id, cred_id)); | ||
167 | if (cred_id || rp_id) | ||
168 | usage(); | ||
169 | |||
170 | print_attr(dev); | ||
171 | |||
172 | if (fido_dev_is_fido2(dev) == false) | ||
173 | goto end; | ||
174 | if ((ci = fido_cbor_info_new()) == NULL) | ||
175 | errx(1, "fido_cbor_info_new"); | ||
176 | if ((r = fido_dev_get_cbor_info(dev, ci)) != FIDO_OK) | ||
177 | errx(1, "fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r); | ||
178 | |||
179 | /* print supported protocol versions */ | ||
180 | print_str_array("version", fido_cbor_info_versions_ptr(ci), | ||
181 | fido_cbor_info_versions_len(ci)); | ||
182 | |||
183 | /* print supported extensions */ | ||
184 | print_str_array("extension", fido_cbor_info_extensions_ptr(ci), | ||
185 | fido_cbor_info_extensions_len(ci)); | ||
186 | |||
187 | /* print aaguid */ | ||
188 | print_aaguid(fido_cbor_info_aaguid_ptr(ci), | ||
189 | fido_cbor_info_aaguid_len(ci)); | ||
190 | |||
191 | /* print supported options */ | ||
192 | print_opt_array("options", fido_cbor_info_options_name_ptr(ci), | ||
193 | fido_cbor_info_options_value_ptr(ci), | ||
194 | fido_cbor_info_options_len(ci)); | ||
195 | |||
196 | /* print maximum message size */ | ||
197 | print_maxmsgsiz(fido_cbor_info_maxmsgsiz(ci)); | ||
198 | |||
199 | /* print supported pin protocols */ | ||
200 | print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci), | ||
201 | fido_cbor_info_protocols_len(ci)); | ||
202 | |||
203 | if ((r = fido_dev_get_retry_count(dev, &retrycnt)) != FIDO_OK) | ||
204 | printf("pin retries: undefined\n"); | ||
205 | else | ||
206 | printf("pin retries: %d\n", retrycnt); | ||
207 | |||
208 | bio_info(dev); | ||
209 | |||
210 | fido_cbor_info_free(&ci); | ||
211 | end: | ||
212 | fido_dev_close(dev); | ||
213 | fido_dev_free(&dev); | ||
214 | |||
215 | exit(0); | ||
216 | } | ||
217 | |||
218 | int | ||
219 | token_reset(char *path) | ||
220 | { | ||
221 | fido_dev_t *dev = NULL; | ||
222 | int r; | ||
223 | |||
224 | if (path == NULL) | ||
225 | usage(); | ||
226 | |||
227 | dev = open_dev(path); | ||
228 | if ((r = fido_dev_reset(dev)) != FIDO_OK) | ||
229 | errx(1, "fido_dev_reset: %s", fido_strerr(r)); | ||
230 | |||
231 | fido_dev_close(dev); | ||
232 | fido_dev_free(&dev); | ||
233 | |||
234 | exit(0); | ||
235 | } | ||
236 | |||
237 | int | ||
238 | token_set(int argc, char **argv, char *path) | ||
239 | { | ||
240 | char *id = NULL; | ||
241 | char *name = NULL; | ||
242 | int ch; | ||
243 | int enroll = 0; | ||
244 | |||
245 | optind = 1; | ||
246 | |||
247 | while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { | ||
248 | switch (ch) { | ||
249 | case 'e': | ||
250 | enroll = 1; | ||
251 | break; | ||
252 | case 'i': | ||
253 | id = optarg; | ||
254 | break; | ||
255 | case 'n': | ||
256 | name = optarg; | ||
257 | break; | ||
258 | default: | ||
259 | break; /* ignore */ | ||
260 | } | ||
261 | } | ||
262 | |||
263 | if (enroll) { | ||
264 | if (id && name) | ||
265 | return (bio_set_name(path, id, name)); | ||
266 | if (!id && !name) | ||
267 | return (bio_enroll(path)); | ||
268 | usage(); | ||
269 | } | ||
270 | |||
271 | return (pin_set(path)); | ||
272 | } | ||
273 | |||
274 | int | ||
275 | token_list(int argc, char **argv, char *path) | ||
276 | { | ||
277 | fido_dev_info_t *devlist; | ||
278 | size_t ndevs; | ||
279 | const char *rp_id = NULL; | ||
280 | int enrolls = 0; | ||
281 | int keys = 0; | ||
282 | int rplist = 0; | ||
283 | int ch; | ||
284 | int r; | ||
285 | |||
286 | optind = 1; | ||
287 | |||
288 | while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { | ||
289 | switch (ch) { | ||
290 | case 'e': | ||
291 | enrolls = 1; | ||
292 | break; | ||
293 | case 'k': | ||
294 | keys = 1; | ||
295 | rp_id = optarg; | ||
296 | break; | ||
297 | case 'r': | ||
298 | rplist = 1; | ||
299 | break; | ||
300 | default: | ||
301 | break; /* ignore */ | ||
302 | } | ||
303 | } | ||
304 | |||
305 | if (enrolls) | ||
306 | return (bio_list(path)); | ||
307 | if (keys) | ||
308 | return (credman_list_rk(path, rp_id)); | ||
309 | if (rplist) | ||
310 | return (credman_list_rp(path)); | ||
311 | |||
312 | if ((devlist = fido_dev_info_new(64)) == NULL) | ||
313 | errx(1, "fido_dev_info_new"); | ||
314 | if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK) | ||
315 | errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r); | ||
316 | |||
317 | for (size_t i = 0; i < ndevs; i++) { | ||
318 | const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); | ||
319 | printf("%s: vendor=0x%04x, product=0x%04x (%s %s)\n", | ||
320 | fido_dev_info_path(di), | ||
321 | (uint16_t)fido_dev_info_vendor(di), | ||
322 | (uint16_t)fido_dev_info_product(di), | ||
323 | fido_dev_info_manufacturer_string(di), | ||
324 | fido_dev_info_product_string(di)); | ||
325 | } | ||
326 | |||
327 | fido_dev_info_free(&devlist, ndevs); | ||
328 | |||
329 | exit(0); | ||
330 | } | ||
331 | |||
332 | int | ||
333 | token_delete(int argc, char **argv, char *path) | ||
334 | { | ||
335 | char *id = NULL; | ||
336 | fido_dev_t *dev = NULL; | ||
337 | int ch; | ||
338 | int enroll = 0; | ||
339 | |||
340 | optind = 1; | ||
341 | |||
342 | while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { | ||
343 | switch (ch) { | ||
344 | case 'e': | ||
345 | enroll = 1; | ||
346 | break; | ||
347 | case 'i': | ||
348 | id = optarg; | ||
349 | break; | ||
350 | default: | ||
351 | break; /* ignore */ | ||
352 | } | ||
353 | } | ||
354 | |||
355 | if (path == NULL || id == NULL) | ||
356 | usage(); | ||
357 | |||
358 | dev = open_dev(path); | ||
359 | |||
360 | if (id && !enroll) | ||
361 | return (credman_delete_rk(dev, path, id)); | ||
362 | |||
363 | return (bio_delete(dev, path, id)); | ||
364 | } | ||
diff --git a/tools/util.c b/tools/util.c new file mode 100644 index 0000000..de70388 --- /dev/null +++ b/tools/util.c | |||
@@ -0,0 +1,364 @@ | |||
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 <openssl/ec.h> | ||
11 | #include <openssl/evp.h> | ||
12 | #include <openssl/pem.h> | ||
13 | |||
14 | #include <fido.h> | ||
15 | #include <fido/es256.h> | ||
16 | #include <fido/rs256.h> | ||
17 | #include <fido/eddsa.h> | ||
18 | |||
19 | #include <fcntl.h> | ||
20 | #include <stdint.h> | ||
21 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <string.h> | ||
24 | |||
25 | #include "../openbsd-compat/openbsd-compat.h" | ||
26 | #ifdef _MSC_VER | ||
27 | #include "../openbsd-compat/posix_win.h" | ||
28 | #endif | ||
29 | |||
30 | #include "extern.h" | ||
31 | |||
32 | void | ||
33 | read_pin(const char *path, char *buf, size_t len) | ||
34 | { | ||
35 | char prompt[1024]; | ||
36 | int r; | ||
37 | |||
38 | r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", path); | ||
39 | if (r < 0 || (size_t)r >= sizeof(prompt)) | ||
40 | errx(1, "snprintf"); | ||
41 | if (!readpassphrase(prompt, buf, len, RPP_ECHO_OFF)) | ||
42 | errx(1, "readpassphrase"); | ||
43 | } | ||
44 | |||
45 | FILE * | ||
46 | open_write(const char *file) | ||
47 | { | ||
48 | int fd; | ||
49 | FILE *f; | ||
50 | |||
51 | if (file == NULL || strcmp(file, "-") == 0) | ||
52 | return (stdout); | ||
53 | if ((fd = open(file, O_WRONLY | O_CREAT, 0600)) < 0) | ||
54 | err(1, "open %s", file); | ||
55 | if ((f = fdopen(fd, "w")) == NULL) | ||
56 | err(1, "fdopen %s", file); | ||
57 | |||
58 | return (f); | ||
59 | } | ||
60 | |||
61 | FILE * | ||
62 | open_read(const char *file) | ||
63 | { | ||
64 | int fd; | ||
65 | FILE *f; | ||
66 | |||
67 | if (file == NULL || strcmp(file, "-") == 0) { | ||
68 | #ifdef FIDO_FUZZ | ||
69 | setvbuf(stdin, NULL, _IONBF, 0); | ||
70 | #endif | ||
71 | return (stdin); | ||
72 | } | ||
73 | if ((fd = open(file, O_RDONLY)) < 0) | ||
74 | err(1, "open %s", file); | ||
75 | if ((f = fdopen(fd, "r")) == NULL) | ||
76 | err(1, "fdopen %s", file); | ||
77 | |||
78 | return (f); | ||
79 | } | ||
80 | |||
81 | void | ||
82 | xxd(const void *buf, size_t count) | ||
83 | { | ||
84 | const uint8_t *ptr = buf; | ||
85 | size_t i; | ||
86 | |||
87 | fprintf(stderr, " "); | ||
88 | |||
89 | for (i = 0; i < count; i++) { | ||
90 | fprintf(stderr, "%02x ", *ptr++); | ||
91 | if ((i + 1) % 16 == 0 && i + 1 < count) | ||
92 | fprintf(stderr, "\n "); | ||
93 | } | ||
94 | |||
95 | fprintf(stderr, "\n"); | ||
96 | fflush(stderr); | ||
97 | } | ||
98 | |||
99 | int | ||
100 | string_read(FILE *f, char **out) | ||
101 | { | ||
102 | char *line = NULL; | ||
103 | size_t linesize = 0; | ||
104 | ssize_t n; | ||
105 | |||
106 | *out = NULL; | ||
107 | |||
108 | if ((n = getline(&line, &linesize, f)) <= 0 || | ||
109 | (size_t)n != strlen(line)) { | ||
110 | free(line); | ||
111 | return (-1); | ||
112 | } | ||
113 | |||
114 | line[n - 1] = '\0'; /* trim \n */ | ||
115 | *out = line; | ||
116 | |||
117 | return (0); | ||
118 | } | ||
119 | |||
120 | fido_dev_t * | ||
121 | open_dev(const char *path) | ||
122 | { | ||
123 | fido_dev_t *dev; | ||
124 | int r; | ||
125 | |||
126 | if ((dev = fido_dev_new()) == NULL) | ||
127 | errx(1, "fido_dev_new"); | ||
128 | |||
129 | r = fido_dev_open(dev, path); | ||
130 | if (r != FIDO_OK) | ||
131 | errx(1, "fido_dev_open %s: %s", path, fido_strerr(r)); | ||
132 | |||
133 | return (dev); | ||
134 | } | ||
135 | |||
136 | EC_KEY * | ||
137 | read_ec_pubkey(const char *path) | ||
138 | { | ||
139 | FILE *fp = NULL; | ||
140 | EVP_PKEY *pkey = NULL; | ||
141 | EC_KEY *ec = NULL; | ||
142 | |||
143 | if ((fp = fopen(path, "r")) == NULL) { | ||
144 | warn("fopen"); | ||
145 | goto fail; | ||
146 | } | ||
147 | |||
148 | if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { | ||
149 | warnx("PEM_read_PUBKEY"); | ||
150 | goto fail; | ||
151 | } | ||
152 | if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) { | ||
153 | warnx("EVP_PKEY_get1_EC_KEY"); | ||
154 | goto fail; | ||
155 | } | ||
156 | |||
157 | fail: | ||
158 | if (fp) { | ||
159 | fclose(fp); | ||
160 | } | ||
161 | if (pkey) { | ||
162 | EVP_PKEY_free(pkey); | ||
163 | } | ||
164 | |||
165 | return (ec); | ||
166 | } | ||
167 | |||
168 | int | ||
169 | write_ec_pubkey(FILE *f, const void *ptr, size_t len) | ||
170 | { | ||
171 | EVP_PKEY *pkey = NULL; | ||
172 | es256_pk_t *pk = NULL; | ||
173 | int ok = -1; | ||
174 | |||
175 | if ((pk = es256_pk_new()) == NULL) { | ||
176 | warnx("es256_pk_new"); | ||
177 | goto fail; | ||
178 | } | ||
179 | |||
180 | if (es256_pk_from_ptr(pk, ptr, len) != FIDO_OK) { | ||
181 | warnx("es256_pk_from_ptr"); | ||
182 | goto fail; | ||
183 | } | ||
184 | |||
185 | if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) { | ||
186 | warnx("es256_pk_to_EVP_PKEY"); | ||
187 | goto fail; | ||
188 | } | ||
189 | |||
190 | if (PEM_write_PUBKEY(f, pkey) == 0) { | ||
191 | warnx("PEM_write_PUBKEY"); | ||
192 | goto fail; | ||
193 | } | ||
194 | |||
195 | ok = 0; | ||
196 | fail: | ||
197 | es256_pk_free(&pk); | ||
198 | |||
199 | if (pkey != NULL) { | ||
200 | EVP_PKEY_free(pkey); | ||
201 | } | ||
202 | |||
203 | return (ok); | ||
204 | } | ||
205 | |||
206 | RSA * | ||
207 | read_rsa_pubkey(const char *path) | ||
208 | { | ||
209 | FILE *fp = NULL; | ||
210 | EVP_PKEY *pkey = NULL; | ||
211 | RSA *rsa = NULL; | ||
212 | |||
213 | if ((fp = fopen(path, "r")) == NULL) { | ||
214 | warn("fopen"); | ||
215 | goto fail; | ||
216 | } | ||
217 | |||
218 | if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { | ||
219 | warnx("PEM_read_PUBKEY"); | ||
220 | goto fail; | ||
221 | } | ||
222 | if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) { | ||
223 | warnx("EVP_PKEY_get1_RSA"); | ||
224 | goto fail; | ||
225 | } | ||
226 | |||
227 | fail: | ||
228 | if (fp) { | ||
229 | fclose(fp); | ||
230 | } | ||
231 | if (pkey) { | ||
232 | EVP_PKEY_free(pkey); | ||
233 | } | ||
234 | |||
235 | return (rsa); | ||
236 | } | ||
237 | |||
238 | int | ||
239 | write_rsa_pubkey(FILE *f, const void *ptr, size_t len) | ||
240 | { | ||
241 | EVP_PKEY *pkey = NULL; | ||
242 | rs256_pk_t *pk = NULL; | ||
243 | int ok = -1; | ||
244 | |||
245 | if ((pk = rs256_pk_new()) == NULL) { | ||
246 | warnx("rs256_pk_new"); | ||
247 | goto fail; | ||
248 | } | ||
249 | |||
250 | if (rs256_pk_from_ptr(pk, ptr, len) != FIDO_OK) { | ||
251 | warnx("rs256_pk_from_ptr"); | ||
252 | goto fail; | ||
253 | } | ||
254 | |||
255 | if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) { | ||
256 | warnx("rs256_pk_to_EVP_PKEY"); | ||
257 | goto fail; | ||
258 | } | ||
259 | |||
260 | if (PEM_write_PUBKEY(f, pkey) == 0) { | ||
261 | warnx("PEM_write_PUBKEY"); | ||
262 | goto fail; | ||
263 | } | ||
264 | |||
265 | ok = 0; | ||
266 | fail: | ||
267 | rs256_pk_free(&pk); | ||
268 | |||
269 | if (pkey != NULL) { | ||
270 | EVP_PKEY_free(pkey); | ||
271 | } | ||
272 | |||
273 | return (ok); | ||
274 | } | ||
275 | |||
276 | EVP_PKEY * | ||
277 | read_eddsa_pubkey(const char *path) | ||
278 | { | ||
279 | FILE *fp = NULL; | ||
280 | EVP_PKEY *pkey = NULL; | ||
281 | |||
282 | if ((fp = fopen(path, "r")) == NULL) { | ||
283 | warn("fopen"); | ||
284 | goto fail; | ||
285 | } | ||
286 | |||
287 | if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { | ||
288 | warnx("PEM_read_PUBKEY"); | ||
289 | goto fail; | ||
290 | } | ||
291 | |||
292 | fail: | ||
293 | if (fp) { | ||
294 | fclose(fp); | ||
295 | } | ||
296 | |||
297 | return (pkey); | ||
298 | } | ||
299 | |||
300 | int | ||
301 | write_eddsa_pubkey(FILE *f, const void *ptr, size_t len) | ||
302 | { | ||
303 | EVP_PKEY *pkey = NULL; | ||
304 | eddsa_pk_t *pk = NULL; | ||
305 | int ok = -1; | ||
306 | |||
307 | if ((pk = eddsa_pk_new()) == NULL) { | ||
308 | warnx("eddsa_pk_new"); | ||
309 | goto fail; | ||
310 | } | ||
311 | |||
312 | if (eddsa_pk_from_ptr(pk, ptr, len) != FIDO_OK) { | ||
313 | warnx("eddsa_pk_from_ptr"); | ||
314 | goto fail; | ||
315 | } | ||
316 | |||
317 | if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) { | ||
318 | warnx("eddsa_pk_to_EVP_PKEY"); | ||
319 | goto fail; | ||
320 | } | ||
321 | |||
322 | if (PEM_write_PUBKEY(f, pkey) == 0) { | ||
323 | warnx("PEM_write_PUBKEY"); | ||
324 | goto fail; | ||
325 | } | ||
326 | |||
327 | ok = 0; | ||
328 | fail: | ||
329 | eddsa_pk_free(&pk); | ||
330 | |||
331 | if (pkey != NULL) { | ||
332 | EVP_PKEY_free(pkey); | ||
333 | } | ||
334 | |||
335 | return (ok); | ||
336 | } | ||
337 | |||
338 | void | ||
339 | print_cred(FILE *out_f, int type, const fido_cred_t *cred) | ||
340 | { | ||
341 | char *id; | ||
342 | int r; | ||
343 | |||
344 | r = base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred), &id); | ||
345 | if (r < 0) | ||
346 | errx(1, "output error"); | ||
347 | |||
348 | fprintf(out_f, "%s\n", id); | ||
349 | |||
350 | if (type == COSE_ES256) { | ||
351 | write_ec_pubkey(out_f, fido_cred_pubkey_ptr(cred), | ||
352 | fido_cred_pubkey_len(cred)); | ||
353 | } else if (type == COSE_RS256) { | ||
354 | write_rsa_pubkey(out_f, fido_cred_pubkey_ptr(cred), | ||
355 | fido_cred_pubkey_len(cred)); | ||
356 | } else if (type == COSE_EDDSA) { | ||
357 | write_eddsa_pubkey(out_f, fido_cred_pubkey_ptr(cred), | ||
358 | fido_cred_pubkey_len(cred)); | ||
359 | } else { | ||
360 | errx(1, "print_cred: unknown type"); | ||
361 | } | ||
362 | |||
363 | free(id); | ||
364 | } | ||