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 /examples |
Import upstream release 1.3.0
Closes: #951184
Diffstat (limited to 'examples')
-rw-r--r-- | examples/CMakeLists.txt | 44 | ||||
-rw-r--r-- | examples/README.adoc | 81 | ||||
-rw-r--r-- | examples/assert.c | 329 | ||||
-rw-r--r-- | examples/cred.c | 303 | ||||
-rw-r--r-- | examples/extern.h | 32 | ||||
-rw-r--r-- | examples/info.c | 216 | ||||
-rw-r--r-- | examples/manifest.c | 45 | ||||
-rw-r--r-- | examples/reset.c | 64 | ||||
-rw-r--r-- | examples/retries.c | 52 | ||||
-rw-r--r-- | examples/setpin.c | 59 | ||||
-rw-r--r-- | examples/util.c | 415 |
11 files changed, 1640 insertions, 0 deletions
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..957311e --- /dev/null +++ b/examples/CMakeLists.txt | |||
@@ -0,0 +1,44 @@ | |||
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/getopt_long.c | ||
7 | ../openbsd-compat/strlcat.c | ||
8 | ../openbsd-compat/strlcpy.c | ||
9 | ) | ||
10 | |||
11 | if(WIN32) | ||
12 | list(APPEND COMPAT_SOURCES ../openbsd-compat/posix_win.c) | ||
13 | endif() | ||
14 | |||
15 | # drop -rdynamic | ||
16 | set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") | ||
17 | |||
18 | # manifest | ||
19 | add_executable(manifest manifest.c ${COMPAT_SOURCES}) | ||
20 | target_link_libraries(manifest fido2) | ||
21 | |||
22 | # info | ||
23 | add_executable(info info.c ${COMPAT_SOURCES}) | ||
24 | target_link_libraries(info fido2) | ||
25 | |||
26 | # reset | ||
27 | add_executable(reset reset.c util.c ${COMPAT_SOURCES}) | ||
28 | target_link_libraries(reset fido2) | ||
29 | |||
30 | # cred | ||
31 | add_executable(cred cred.c util.c ${COMPAT_SOURCES}) | ||
32 | target_link_libraries(cred fido2) | ||
33 | |||
34 | # assert | ||
35 | add_executable(assert assert.c util.c ${COMPAT_SOURCES}) | ||
36 | target_link_libraries(assert fido2) | ||
37 | |||
38 | # setpin | ||
39 | add_executable(setpin setpin.c ${COMPAT_SOURCES}) | ||
40 | target_link_libraries(setpin fido2) | ||
41 | |||
42 | # retries | ||
43 | add_executable(retries retries.c ${COMPAT_SOURCES}) | ||
44 | target_link_libraries(retries fido2) | ||
diff --git a/examples/README.adoc b/examples/README.adoc new file mode 100644 index 0000000..091c6bc --- /dev/null +++ b/examples/README.adoc | |||
@@ -0,0 +1,81 @@ | |||
1 | = Examples | ||
2 | |||
3 | === Definitions | ||
4 | |||
5 | The following definitions are used in the description below: | ||
6 | |||
7 | - <device> | ||
8 | |||
9 | The file system path or subsystem-specific identification string of a | ||
10 | FIDO device. | ||
11 | |||
12 | - <pin>, [oldpin] | ||
13 | |||
14 | Strings passed directly in the executed command's argument vector. | ||
15 | |||
16 | - <cred_id> | ||
17 | |||
18 | The file system path of a file containing a FIDO credential ID in | ||
19 | binary representation. | ||
20 | |||
21 | - <pubkey> | ||
22 | |||
23 | The file system path of a file containing a NIST P-256 public key in | ||
24 | PEM format. | ||
25 | |||
26 | === Description | ||
27 | |||
28 | The following examples are provided: | ||
29 | |||
30 | - manifest | ||
31 | |||
32 | Prints a list of configured FIDO devices. | ||
33 | |||
34 | - info <device> | ||
35 | |||
36 | Prints information about <device>. | ||
37 | |||
38 | - reset <device> | ||
39 | |||
40 | Performs a factory reset on <device>. | ||
41 | |||
42 | - setpin <pin> [oldpin] <device> | ||
43 | |||
44 | Configures <pin> as the new PIN of <device>. If [oldpin] is provided, | ||
45 | the device's PIN is changed from [oldpin] to <pin>. | ||
46 | |||
47 | - cred [-t ecdsa|rsa|eddsa] [-k pubkey] [-ei cred_id] [-P pin] [-T seconds] | ||
48 | [-hruv] <device> | ||
49 | |||
50 | Creates a new credential on <device> and verify that the credential | ||
51 | was signed by the authenticator. The device's attestation certificate | ||
52 | is not verified. If option -k is specified, the credential's public | ||
53 | key is stored in <pubkey>. If option -i is specified, the credential | ||
54 | ID is stored in <cred_id>. The -e option may be used to add <cred_id> | ||
55 | to the list of excluded credentials. If option -h is specified, | ||
56 | the hmac-secret FIDO2 extension is enabled on the generated | ||
57 | credential. If option -r is specified, the generated credential | ||
58 | will involve a resident key. User verification may be requested | ||
59 | through the -v option. If option -u is specified, the credential | ||
60 | is generated using U2F (CTAP1) instead of FIDO2 (CTAP2) commands. | ||
61 | The -T option may be used to enforce a timeout of <seconds>. | ||
62 | |||
63 | - assert [-t ecdsa|rsa|eddsa] [-a cred_id] [-h hmac_secret] [-s hmac_salt] | ||
64 | [-P pin] [-T seconds] [-puv] <pubkey> <device> | ||
65 | |||
66 | Asks <device> for a FIDO2 assertion corresponding to [cred_id], | ||
67 | which may be omitted for resident keys. The obtained assertion | ||
68 | is verified using <pubkey>. The -p option requests that the user | ||
69 | be present. User verification may be requested through the -v | ||
70 | option. If option -u is specified, the assertion is generated using | ||
71 | U2F (CTAP1) instead of FIDO2 (CTAP2) commands. If option -s is | ||
72 | specified, a FIDO2 hmac-secret is requested from the authenticator, | ||
73 | and the contents of <hmac_salt> are used as the salt. If option -h | ||
74 | is specified, the resulting hmac-secret is stored in <hmac_secret>. | ||
75 | The -T option may be used to enforce a timeout of <seconds>. | ||
76 | |||
77 | - retries <device> | ||
78 | Get the number of PIN attempts left on <device> before lockout. | ||
79 | |||
80 | Debugging is possible through the use of the FIDO_DEBUG environment variable. | ||
81 | If set, libfido2 will produce a log of its transactions with the authenticator. | ||
diff --git a/examples/assert.c b/examples/assert.c new file mode 100644 index 0000000..a421a51 --- /dev/null +++ b/examples/assert.c | |||
@@ -0,0 +1,329 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2018 Yubico AB. All rights reserved. | ||
3 | * Use of this source code is governed by a BSD-style | ||
4 | * license that can be found in the LICENSE file. | ||
5 | */ | ||
6 | |||
7 | #include <openssl/ec.h> | ||
8 | |||
9 | #include <stdbool.h> | ||
10 | #include <stdio.h> | ||
11 | #include <stdlib.h> | ||
12 | #include <string.h> | ||
13 | #ifdef HAVE_UNISTD_H | ||
14 | #include <unistd.h> | ||
15 | #endif | ||
16 | |||
17 | #include "../openbsd-compat/openbsd-compat.h" | ||
18 | |||
19 | #include "fido.h" | ||
20 | #include "fido/es256.h" | ||
21 | #include "fido/rs256.h" | ||
22 | #include "fido/eddsa.h" | ||
23 | #include "extern.h" | ||
24 | |||
25 | #ifdef SIGNAL_EXAMPLE | ||
26 | extern volatile sig_atomic_t got_signal; | ||
27 | #endif | ||
28 | |||
29 | static const unsigned char cdh[32] = { | ||
30 | 0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7, | ||
31 | 0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56, | ||
32 | 0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52, | ||
33 | 0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76, | ||
34 | }; | ||
35 | |||
36 | static void | ||
37 | usage(void) | ||
38 | { | ||
39 | fprintf(stderr, "usage: assert [-t ecdsa|rsa|eddsa] [-a cred_id] " | ||
40 | "[-h hmac_secret] [-s hmac_salt] [-P pin] [-T seconds] [-puv] " | ||
41 | "<pubkey> <device>\n"); | ||
42 | exit(EXIT_FAILURE); | ||
43 | } | ||
44 | |||
45 | static void | ||
46 | verify_assert(int type, const unsigned char *authdata_ptr, size_t authdata_len, | ||
47 | const unsigned char *sig_ptr, size_t sig_len, bool up, bool uv, int ext, | ||
48 | const char *key) | ||
49 | { | ||
50 | fido_assert_t *assert = NULL; | ||
51 | EC_KEY *ec = NULL; | ||
52 | RSA *rsa = NULL; | ||
53 | EVP_PKEY *eddsa = NULL; | ||
54 | es256_pk_t *es256_pk = NULL; | ||
55 | rs256_pk_t *rs256_pk = NULL; | ||
56 | eddsa_pk_t *eddsa_pk = NULL; | ||
57 | void *pk; | ||
58 | int r; | ||
59 | |||
60 | /* credential pubkey */ | ||
61 | switch (type) { | ||
62 | case COSE_ES256: | ||
63 | if ((ec = read_ec_pubkey(key)) == NULL) | ||
64 | errx(1, "read_ec_pubkey"); | ||
65 | |||
66 | if ((es256_pk = es256_pk_new()) == NULL) | ||
67 | errx(1, "es256_pk_new"); | ||
68 | |||
69 | if (es256_pk_from_EC_KEY(es256_pk, ec) != FIDO_OK) | ||
70 | errx(1, "es256_pk_from_EC_KEY"); | ||
71 | |||
72 | pk = es256_pk; | ||
73 | EC_KEY_free(ec); | ||
74 | ec = NULL; | ||
75 | |||
76 | break; | ||
77 | case COSE_RS256: | ||
78 | if ((rsa = read_rsa_pubkey(key)) == NULL) | ||
79 | errx(1, "read_rsa_pubkey"); | ||
80 | |||
81 | if ((rs256_pk = rs256_pk_new()) == NULL) | ||
82 | errx(1, "rs256_pk_new"); | ||
83 | |||
84 | if (rs256_pk_from_RSA(rs256_pk, rsa) != FIDO_OK) | ||
85 | errx(1, "rs256_pk_from_RSA"); | ||
86 | |||
87 | pk = rs256_pk; | ||
88 | RSA_free(rsa); | ||
89 | rsa = NULL; | ||
90 | |||
91 | break; | ||
92 | case COSE_EDDSA: | ||
93 | if ((eddsa = read_eddsa_pubkey(key)) == NULL) | ||
94 | errx(1, "read_eddsa_pubkey"); | ||
95 | |||
96 | if ((eddsa_pk = eddsa_pk_new()) == NULL) | ||
97 | errx(1, "eddsa_pk_new"); | ||
98 | |||
99 | if (eddsa_pk_from_EVP_PKEY(eddsa_pk, eddsa) != FIDO_OK) | ||
100 | errx(1, "eddsa_pk_from_EVP_PKEY"); | ||
101 | |||
102 | pk = eddsa_pk; | ||
103 | EVP_PKEY_free(eddsa); | ||
104 | eddsa = NULL; | ||
105 | |||
106 | break; | ||
107 | default: | ||
108 | errx(1, "unknown credential type %d", type); | ||
109 | } | ||
110 | |||
111 | if ((assert = fido_assert_new()) == NULL) | ||
112 | errx(1, "fido_assert_new"); | ||
113 | |||
114 | /* client data hash */ | ||
115 | r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh)); | ||
116 | if (r != FIDO_OK) | ||
117 | errx(1, "fido_assert_set_clientdata_hash: %s (0x%x)", | ||
118 | fido_strerr(r), r); | ||
119 | |||
120 | /* relying party */ | ||
121 | r = fido_assert_set_rp(assert, "localhost"); | ||
122 | if (r != FIDO_OK) | ||
123 | errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r); | ||
124 | |||
125 | /* authdata */ | ||
126 | r = fido_assert_set_count(assert, 1); | ||
127 | if (r != FIDO_OK) | ||
128 | errx(1, "fido_assert_set_count: %s (0x%x)", fido_strerr(r), r); | ||
129 | r = fido_assert_set_authdata(assert, 0, authdata_ptr, authdata_len); | ||
130 | if (r != FIDO_OK) | ||
131 | errx(1, "fido_assert_set_authdata: %s (0x%x)", fido_strerr(r), r); | ||
132 | |||
133 | /* extension */ | ||
134 | r = fido_assert_set_extensions(assert, ext); | ||
135 | if (r != FIDO_OK) | ||
136 | errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r), | ||
137 | r); | ||
138 | |||
139 | /* user presence */ | ||
140 | if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK) | ||
141 | errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r); | ||
142 | |||
143 | /* user verification */ | ||
144 | if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK) | ||
145 | errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r); | ||
146 | |||
147 | /* sig */ | ||
148 | r = fido_assert_set_sig(assert, 0, sig_ptr, sig_len); | ||
149 | if (r != FIDO_OK) | ||
150 | errx(1, "fido_assert_set_sig: %s (0x%x)", fido_strerr(r), r); | ||
151 | |||
152 | r = fido_assert_verify(assert, 0, type, pk); | ||
153 | if (r != FIDO_OK) | ||
154 | errx(1, "fido_assert_verify: %s (0x%x)", fido_strerr(r), r); | ||
155 | |||
156 | es256_pk_free(&es256_pk); | ||
157 | rs256_pk_free(&rs256_pk); | ||
158 | eddsa_pk_free(&eddsa_pk); | ||
159 | |||
160 | fido_assert_free(&assert); | ||
161 | } | ||
162 | |||
163 | int | ||
164 | main(int argc, char **argv) | ||
165 | { | ||
166 | bool up = false; | ||
167 | bool uv = false; | ||
168 | bool u2f = false; | ||
169 | fido_dev_t *dev = NULL; | ||
170 | fido_assert_t *assert = NULL; | ||
171 | const char *pin = NULL; | ||
172 | const char *hmac_out = NULL; | ||
173 | unsigned char *body = NULL; | ||
174 | long long seconds = 0; | ||
175 | size_t len; | ||
176 | int type = COSE_ES256; | ||
177 | int ext = 0; | ||
178 | int ch; | ||
179 | int r; | ||
180 | |||
181 | if ((assert = fido_assert_new()) == NULL) | ||
182 | errx(1, "fido_assert_new"); | ||
183 | |||
184 | while ((ch = getopt(argc, argv, "P:T:a:h:ps:t:uv")) != -1) { | ||
185 | switch (ch) { | ||
186 | case 'P': | ||
187 | pin = optarg; | ||
188 | break; | ||
189 | case 'T': | ||
190 | #ifndef SIGNAL_EXAMPLE | ||
191 | errx(1, "-T not supported"); | ||
192 | #endif | ||
193 | if (base10(optarg, &seconds) < 0) | ||
194 | errx(1, "base10: %s", optarg); | ||
195 | if (seconds <= 0 || seconds > 30) | ||
196 | errx(1, "-T: %s must be in (0,30]", optarg); | ||
197 | break; | ||
198 | case 'a': | ||
199 | if (read_blob(optarg, &body, &len) < 0) | ||
200 | errx(1, "read_blob: %s", optarg); | ||
201 | if ((r = fido_assert_allow_cred(assert, body, | ||
202 | len)) != FIDO_OK) | ||
203 | errx(1, "fido_assert_allow_cred: %s (0x%x)", | ||
204 | fido_strerr(r), r); | ||
205 | free(body); | ||
206 | body = NULL; | ||
207 | break; | ||
208 | case 'h': | ||
209 | hmac_out = optarg; | ||
210 | break; | ||
211 | case 'p': | ||
212 | up = true; | ||
213 | break; | ||
214 | case 's': | ||
215 | ext = FIDO_EXT_HMAC_SECRET; | ||
216 | if (read_blob(optarg, &body, &len) < 0) | ||
217 | errx(1, "read_blob: %s", optarg); | ||
218 | if ((r = fido_assert_set_hmac_salt(assert, body, | ||
219 | len)) != FIDO_OK) | ||
220 | errx(1, "fido_assert_set_hmac_salt: %s (0x%x)", | ||
221 | fido_strerr(r), r); | ||
222 | free(body); | ||
223 | body = NULL; | ||
224 | break; | ||
225 | case 't': | ||
226 | if (strcmp(optarg, "ecdsa") == 0) | ||
227 | type = COSE_ES256; | ||
228 | else if (strcmp(optarg, "rsa") == 0) | ||
229 | type = COSE_RS256; | ||
230 | else if (strcmp(optarg, "eddsa") == 0) | ||
231 | type = COSE_EDDSA; | ||
232 | else | ||
233 | errx(1, "unknown type %s", optarg); | ||
234 | break; | ||
235 | case 'u': | ||
236 | u2f = true; | ||
237 | break; | ||
238 | case 'v': | ||
239 | uv = true; | ||
240 | break; | ||
241 | default: | ||
242 | usage(); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | argc -= optind; | ||
247 | argv += optind; | ||
248 | |||
249 | if (argc != 2) | ||
250 | usage(); | ||
251 | |||
252 | fido_init(0); | ||
253 | |||
254 | if ((dev = fido_dev_new()) == NULL) | ||
255 | errx(1, "fido_dev_new"); | ||
256 | |||
257 | r = fido_dev_open(dev, argv[1]); | ||
258 | if (r != FIDO_OK) | ||
259 | errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r); | ||
260 | if (u2f) | ||
261 | fido_dev_force_u2f(dev); | ||
262 | |||
263 | /* client data hash */ | ||
264 | r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh)); | ||
265 | if (r != FIDO_OK) | ||
266 | errx(1, "fido_assert_set_clientdata_hash: %s (0x%x)", | ||
267 | fido_strerr(r), r); | ||
268 | |||
269 | /* relying party */ | ||
270 | r = fido_assert_set_rp(assert, "localhost"); | ||
271 | if (r != FIDO_OK) | ||
272 | errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r); | ||
273 | |||
274 | /* extensions */ | ||
275 | r = fido_assert_set_extensions(assert, ext); | ||
276 | if (r != FIDO_OK) | ||
277 | errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r), | ||
278 | r); | ||
279 | |||
280 | /* user presence */ | ||
281 | if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK) | ||
282 | errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r); | ||
283 | |||
284 | /* user verification */ | ||
285 | if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK) | ||
286 | errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r); | ||
287 | |||
288 | #ifdef SIGNAL_EXAMPLE | ||
289 | prepare_signal_handler(SIGINT); | ||
290 | if (seconds) { | ||
291 | prepare_signal_handler(SIGALRM); | ||
292 | alarm((unsigned)seconds); | ||
293 | } | ||
294 | #endif | ||
295 | |||
296 | r = fido_dev_get_assert(dev, assert, pin); | ||
297 | if (r != FIDO_OK) { | ||
298 | #ifdef SIGNAL_EXAMPLE | ||
299 | if (got_signal) | ||
300 | fido_dev_cancel(dev); | ||
301 | #endif | ||
302 | errx(1, "fido_dev_get_assert: %s (0x%x)", fido_strerr(r), r); | ||
303 | } | ||
304 | |||
305 | r = fido_dev_close(dev); | ||
306 | if (r != FIDO_OK) | ||
307 | errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r); | ||
308 | |||
309 | fido_dev_free(&dev); | ||
310 | |||
311 | if (fido_assert_count(assert) != 1) | ||
312 | errx(1, "fido_assert_count: %d signatures returned", | ||
313 | (int)fido_assert_count(assert)); | ||
314 | |||
315 | verify_assert(type, fido_assert_authdata_ptr(assert, 0), | ||
316 | fido_assert_authdata_len(assert, 0), fido_assert_sig_ptr(assert, 0), | ||
317 | fido_assert_sig_len(assert, 0), up, uv, ext, argv[0]); | ||
318 | |||
319 | if (hmac_out != NULL) { | ||
320 | /* extract the hmac secret */ | ||
321 | if (write_blob(hmac_out, fido_assert_hmac_secret_ptr(assert, 0), | ||
322 | fido_assert_hmac_secret_len(assert, 0)) < 0) | ||
323 | errx(1, "write_blob"); | ||
324 | } | ||
325 | |||
326 | fido_assert_free(&assert); | ||
327 | |||
328 | exit(0); | ||
329 | } | ||
diff --git a/examples/cred.c b/examples/cred.c new file mode 100644 index 0000000..e471f7e --- /dev/null +++ b/examples/cred.c | |||
@@ -0,0 +1,303 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2018 Yubico AB. All rights reserved. | ||
3 | * Use of this source code is governed by a BSD-style | ||
4 | * license that can be found in the LICENSE file. | ||
5 | */ | ||
6 | |||
7 | #include <openssl/ec.h> | ||
8 | #include <openssl/pem.h> | ||
9 | |||
10 | #include <errno.h> | ||
11 | #include <stdbool.h> | ||
12 | #include <stdio.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <string.h> | ||
15 | #ifdef HAVE_UNISTD_H | ||
16 | #include <unistd.h> | ||
17 | #endif | ||
18 | |||
19 | #include "../openbsd-compat/openbsd-compat.h" | ||
20 | |||
21 | #include "fido.h" | ||
22 | #include "extern.h" | ||
23 | |||
24 | #ifdef SIGNAL_EXAMPLE | ||
25 | extern volatile sig_atomic_t got_signal; | ||
26 | #endif | ||
27 | |||
28 | static const unsigned char cdh[32] = { | ||
29 | 0xf9, 0x64, 0x57, 0xe7, 0x2d, 0x97, 0xf6, 0xbb, | ||
30 | 0xdd, 0xd7, 0xfb, 0x06, 0x37, 0x62, 0xea, 0x26, | ||
31 | 0x20, 0x44, 0x8e, 0x69, 0x7c, 0x03, 0xf2, 0x31, | ||
32 | 0x2f, 0x99, 0xdc, 0xaf, 0x3e, 0x8a, 0x91, 0x6b, | ||
33 | }; | ||
34 | |||
35 | static const unsigned char user_id[32] = { | ||
36 | 0x78, 0x1c, 0x78, 0x60, 0xad, 0x88, 0xd2, 0x63, | ||
37 | 0x32, 0x62, 0x2a, 0xf1, 0x74, 0x5d, 0xed, 0xb2, | ||
38 | 0xe7, 0xa4, 0x2b, 0x44, 0x89, 0x29, 0x39, 0xc5, | ||
39 | 0x56, 0x64, 0x01, 0x27, 0x0d, 0xbb, 0xc4, 0x49, | ||
40 | }; | ||
41 | |||
42 | static void | ||
43 | usage(void) | ||
44 | { | ||
45 | fprintf(stderr, "usage: cred [-t ecdsa|rsa|eddsa] [-k pubkey] " | ||
46 | "[-ei cred_id] [-P pin] [-T seconds] [-hruv] <device>\n"); | ||
47 | exit(EXIT_FAILURE); | ||
48 | } | ||
49 | |||
50 | static void | ||
51 | verify_cred(int type, const char *fmt, const unsigned char *authdata_ptr, | ||
52 | size_t authdata_len, const unsigned char *x509_ptr, size_t x509_len, | ||
53 | const unsigned char *sig_ptr, size_t sig_len, bool rk, bool uv, int ext, | ||
54 | const char *key_out, const char *id_out) | ||
55 | { | ||
56 | fido_cred_t *cred; | ||
57 | int r; | ||
58 | |||
59 | if ((cred = fido_cred_new()) == NULL) | ||
60 | errx(1, "fido_cred_new"); | ||
61 | |||
62 | /* type */ | ||
63 | r = fido_cred_set_type(cred, type); | ||
64 | if (r != FIDO_OK) | ||
65 | errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r); | ||
66 | |||
67 | /* client data hash */ | ||
68 | r = fido_cred_set_clientdata_hash(cred, cdh, sizeof(cdh)); | ||
69 | if (r != FIDO_OK) | ||
70 | errx(1, "fido_cred_set_clientdata_hash: %s (0x%x)", | ||
71 | fido_strerr(r), r); | ||
72 | |||
73 | /* relying party */ | ||
74 | r = fido_cred_set_rp(cred, "localhost", "sweet home localhost"); | ||
75 | if (r != FIDO_OK) | ||
76 | errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r); | ||
77 | |||
78 | /* authdata */ | ||
79 | r = fido_cred_set_authdata(cred, authdata_ptr, authdata_len); | ||
80 | if (r != FIDO_OK) | ||
81 | errx(1, "fido_cred_set_authdata: %s (0x%x)", fido_strerr(r), r); | ||
82 | |||
83 | /* extensions */ | ||
84 | r = fido_cred_set_extensions(cred, ext); | ||
85 | if (r != FIDO_OK) | ||
86 | errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r); | ||
87 | |||
88 | /* resident key */ | ||
89 | if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK) | ||
90 | errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r); | ||
91 | |||
92 | /* user verification */ | ||
93 | if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) | ||
94 | errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r); | ||
95 | |||
96 | /* x509 */ | ||
97 | r = fido_cred_set_x509(cred, x509_ptr, x509_len); | ||
98 | if (r != FIDO_OK) | ||
99 | errx(1, "fido_cred_set_x509: %s (0x%x)", fido_strerr(r), r); | ||
100 | |||
101 | /* sig */ | ||
102 | r = fido_cred_set_sig(cred, sig_ptr, sig_len); | ||
103 | if (r != FIDO_OK) | ||
104 | errx(1, "fido_cred_set_sig: %s (0x%x)", fido_strerr(r), r); | ||
105 | |||
106 | /* fmt */ | ||
107 | r = fido_cred_set_fmt(cred, fmt); | ||
108 | if (r != FIDO_OK) | ||
109 | errx(1, "fido_cred_set_fmt: %s (0x%x)", fido_strerr(r), r); | ||
110 | |||
111 | r = fido_cred_verify(cred); | ||
112 | if (r != FIDO_OK) | ||
113 | errx(1, "fido_cred_verify: %s (0x%x)", fido_strerr(r), r); | ||
114 | |||
115 | if (key_out != NULL) { | ||
116 | /* extract the credential pubkey */ | ||
117 | if (type == COSE_ES256) { | ||
118 | if (write_ec_pubkey(key_out, fido_cred_pubkey_ptr(cred), | ||
119 | fido_cred_pubkey_len(cred)) < 0) | ||
120 | errx(1, "write_ec_pubkey"); | ||
121 | } else if (type == COSE_RS256) { | ||
122 | if (write_rsa_pubkey(key_out, fido_cred_pubkey_ptr(cred), | ||
123 | fido_cred_pubkey_len(cred)) < 0) | ||
124 | errx(1, "write_rsa_pubkey"); | ||
125 | } else if (type == COSE_EDDSA) { | ||
126 | if (write_eddsa_pubkey(key_out, fido_cred_pubkey_ptr(cred), | ||
127 | fido_cred_pubkey_len(cred)) < 0) | ||
128 | errx(1, "write_eddsa_pubkey"); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | if (id_out != NULL) { | ||
133 | /* extract the credential id */ | ||
134 | if (write_blob(id_out, fido_cred_id_ptr(cred), | ||
135 | fido_cred_id_len(cred)) < 0) | ||
136 | errx(1, "write_blob"); | ||
137 | } | ||
138 | |||
139 | fido_cred_free(&cred); | ||
140 | } | ||
141 | |||
142 | int | ||
143 | main(int argc, char **argv) | ||
144 | { | ||
145 | bool rk = false; | ||
146 | bool uv = false; | ||
147 | bool u2f = false; | ||
148 | fido_dev_t *dev; | ||
149 | fido_cred_t *cred = NULL; | ||
150 | const char *pin = NULL; | ||
151 | const char *key_out = NULL; | ||
152 | const char *id_out = NULL; | ||
153 | unsigned char *body = NULL; | ||
154 | long long seconds = 0; | ||
155 | size_t len; | ||
156 | int type = COSE_ES256; | ||
157 | int ext = 0; | ||
158 | int ch; | ||
159 | int r; | ||
160 | |||
161 | if ((cred = fido_cred_new()) == NULL) | ||
162 | errx(1, "fido_cred_new"); | ||
163 | |||
164 | while ((ch = getopt(argc, argv, "P:T:e:hi:k:rt:uv")) != -1) { | ||
165 | switch (ch) { | ||
166 | case 'P': | ||
167 | pin = optarg; | ||
168 | break; | ||
169 | case 'T': | ||
170 | #ifndef SIGNAL_EXAMPLE | ||
171 | errx(1, "-T not supported"); | ||
172 | #endif | ||
173 | if (base10(optarg, &seconds) < 0) | ||
174 | errx(1, "base10: %s", optarg); | ||
175 | if (seconds <= 0 || seconds > 30) | ||
176 | errx(1, "-T: %s must be in (0,30]", optarg); | ||
177 | break; | ||
178 | case 'e': | ||
179 | if (read_blob(optarg, &body, &len) < 0) | ||
180 | errx(1, "read_blob: %s", optarg); | ||
181 | r = fido_cred_exclude(cred, body, len); | ||
182 | if (r != FIDO_OK) | ||
183 | errx(1, "fido_cred_exclude: %s (0x%x)", | ||
184 | fido_strerr(r), r); | ||
185 | free(body); | ||
186 | body = NULL; | ||
187 | break; | ||
188 | case 'h': | ||
189 | ext = FIDO_EXT_HMAC_SECRET; | ||
190 | break; | ||
191 | case 'i': | ||
192 | id_out = optarg; | ||
193 | break; | ||
194 | case 'k': | ||
195 | key_out = optarg; | ||
196 | break; | ||
197 | case 'r': | ||
198 | rk = true; | ||
199 | break; | ||
200 | case 't': | ||
201 | if (strcmp(optarg, "ecdsa") == 0) | ||
202 | type = COSE_ES256; | ||
203 | else if (strcmp(optarg, "rsa") == 0) | ||
204 | type = COSE_RS256; | ||
205 | else if (strcmp(optarg, "eddsa") == 0) | ||
206 | type = COSE_EDDSA; | ||
207 | else | ||
208 | errx(1, "unknown type %s", optarg); | ||
209 | break; | ||
210 | case 'u': | ||
211 | u2f = true; | ||
212 | break; | ||
213 | case 'v': | ||
214 | uv = true; | ||
215 | break; | ||
216 | default: | ||
217 | usage(); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | argc -= optind; | ||
222 | argv += optind; | ||
223 | |||
224 | if (argc != 1) | ||
225 | usage(); | ||
226 | |||
227 | fido_init(0); | ||
228 | |||
229 | if ((dev = fido_dev_new()) == NULL) | ||
230 | errx(1, "fido_dev_new"); | ||
231 | |||
232 | if ((r = fido_dev_open(dev, argv[0])) != FIDO_OK) | ||
233 | errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r); | ||
234 | if (u2f) | ||
235 | fido_dev_force_u2f(dev); | ||
236 | |||
237 | /* type */ | ||
238 | r = fido_cred_set_type(cred, type); | ||
239 | if (r != FIDO_OK) | ||
240 | errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r); | ||
241 | |||
242 | /* client data hash */ | ||
243 | r = fido_cred_set_clientdata_hash(cred, cdh, sizeof(cdh)); | ||
244 | if (r != FIDO_OK) | ||
245 | errx(1, "fido_cred_set_clientdata_hash: %s (0x%x)", | ||
246 | fido_strerr(r), r); | ||
247 | |||
248 | /* relying party */ | ||
249 | r = fido_cred_set_rp(cred, "localhost", "sweet home localhost"); | ||
250 | if (r != FIDO_OK) | ||
251 | errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r); | ||
252 | |||
253 | /* user */ | ||
254 | r = fido_cred_set_user(cred, user_id, sizeof(user_id), "john smith", | ||
255 | "jsmith", NULL); | ||
256 | if (r != FIDO_OK) | ||
257 | errx(1, "fido_cred_set_user: %s (0x%x)", fido_strerr(r), r); | ||
258 | |||
259 | /* extensions */ | ||
260 | r = fido_cred_set_extensions(cred, ext); | ||
261 | if (r != FIDO_OK) | ||
262 | errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r); | ||
263 | |||
264 | /* resident key */ | ||
265 | if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK) | ||
266 | errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r); | ||
267 | |||
268 | /* user verification */ | ||
269 | if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) | ||
270 | errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r); | ||
271 | |||
272 | #ifdef SIGNAL_EXAMPLE | ||
273 | prepare_signal_handler(SIGINT); | ||
274 | if (seconds) { | ||
275 | prepare_signal_handler(SIGALRM); | ||
276 | alarm((unsigned)seconds); | ||
277 | } | ||
278 | #endif | ||
279 | |||
280 | r = fido_dev_make_cred(dev, cred, pin); | ||
281 | if (r != FIDO_OK) { | ||
282 | #ifdef SIGNAL_EXAMPLE | ||
283 | if (got_signal) | ||
284 | fido_dev_cancel(dev); | ||
285 | #endif | ||
286 | errx(1, "fido_makecred: %s (0x%x)", fido_strerr(r), r); | ||
287 | } | ||
288 | |||
289 | r = fido_dev_close(dev); | ||
290 | if (r != FIDO_OK) | ||
291 | errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r); | ||
292 | |||
293 | fido_dev_free(&dev); | ||
294 | |||
295 | verify_cred(type, fido_cred_fmt(cred), fido_cred_authdata_ptr(cred), | ||
296 | fido_cred_authdata_len(cred), fido_cred_x5c_ptr(cred), | ||
297 | fido_cred_x5c_len(cred), fido_cred_sig_ptr(cred), | ||
298 | fido_cred_sig_len(cred), rk, uv, ext, key_out, id_out); | ||
299 | |||
300 | fido_cred_free(&cred); | ||
301 | |||
302 | exit(0); | ||
303 | } | ||
diff --git a/examples/extern.h b/examples/extern.h new file mode 100644 index 0000000..578b8c4 --- /dev/null +++ b/examples/extern.h | |||
@@ -0,0 +1,32 @@ | |||
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 | #include <openssl/ec.h> | ||
11 | #include <openssl/evp.h> | ||
12 | #include <openssl/rsa.h> | ||
13 | |||
14 | #ifdef HAVE_SIGNAL_H | ||
15 | #include <signal.h> | ||
16 | #endif | ||
17 | |||
18 | /* util.c */ | ||
19 | EC_KEY *read_ec_pubkey(const char *); | ||
20 | RSA *read_rsa_pubkey(const char *); | ||
21 | EVP_PKEY *read_eddsa_pubkey(const char *); | ||
22 | int base10(const char *, long long *); | ||
23 | int read_blob(const char *, unsigned char **, size_t *); | ||
24 | int write_blob(const char *, const unsigned char *, size_t); | ||
25 | int write_ec_pubkey(const char *, const void *, size_t); | ||
26 | int write_rsa_pubkey(const char *, const void *, size_t); | ||
27 | int write_eddsa_pubkey(const char *, const void *, size_t); | ||
28 | #ifdef SIGNAL_EXAMPLE | ||
29 | void prepare_signal_handler(int); | ||
30 | #endif | ||
31 | |||
32 | #endif /* _EXTERN_H_ */ | ||
diff --git a/examples/info.c b/examples/info.c new file mode 100644 index 0000000..e79729c --- /dev/null +++ b/examples/info.c | |||
@@ -0,0 +1,216 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2018 Yubico AB. All rights reserved. | ||
3 | * Use of this source code is governed by a BSD-style | ||
4 | * license that can be found in the LICENSE file. | ||
5 | */ | ||
6 | |||
7 | #include <openssl/ec.h> | ||
8 | |||
9 | #include <stdbool.h> | ||
10 | #include <stdint.h> | ||
11 | #include <stdio.h> | ||
12 | #include <stdlib.h> | ||
13 | #include <string.h> | ||
14 | |||
15 | #include "../openbsd-compat/openbsd-compat.h" | ||
16 | |||
17 | #include "fido.h" | ||
18 | |||
19 | /* | ||
20 | * Pretty-print a device's capabilities flags and return the result. | ||
21 | */ | ||
22 | static void | ||
23 | format_flags(char *ret, size_t retlen, uint8_t flags) | ||
24 | { | ||
25 | memset(ret, 0, retlen); | ||
26 | |||
27 | if (flags & FIDO_CAP_WINK) { | ||
28 | if (strlcat(ret, "wink,", retlen) >= retlen) | ||
29 | goto toolong; | ||
30 | } else { | ||
31 | if (strlcat(ret, "nowink,", retlen) >= retlen) | ||
32 | goto toolong; | ||
33 | } | ||
34 | |||
35 | if (flags & FIDO_CAP_CBOR) { | ||
36 | if (strlcat(ret, " cbor,", retlen) >= retlen) | ||
37 | goto toolong; | ||
38 | } else { | ||
39 | if (strlcat(ret, " nocbor,", retlen) >= retlen) | ||
40 | goto toolong; | ||
41 | } | ||
42 | |||
43 | if (flags & FIDO_CAP_NMSG) { | ||
44 | if (strlcat(ret, " nomsg", retlen) >= retlen) | ||
45 | goto toolong; | ||
46 | } else { | ||
47 | if (strlcat(ret, " msg", retlen) >= retlen) | ||
48 | goto toolong; | ||
49 | } | ||
50 | |||
51 | return; | ||
52 | toolong: | ||
53 | strlcpy(ret, "toolong", retlen); | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Print a FIDO device's attributes on stdout. | ||
58 | */ | ||
59 | static void | ||
60 | print_attr(const fido_dev_t *dev) | ||
61 | { | ||
62 | char flags_txt[128]; | ||
63 | |||
64 | printf("proto: 0x%02x\n", fido_dev_protocol(dev)); | ||
65 | printf("major: 0x%02x\n", fido_dev_major(dev)); | ||
66 | printf("minor: 0x%02x\n", fido_dev_minor(dev)); | ||
67 | printf("build: 0x%02x\n", fido_dev_build(dev)); | ||
68 | |||
69 | format_flags(flags_txt, sizeof(flags_txt), fido_dev_flags(dev)); | ||
70 | printf("caps: 0x%02x (%s)\n", fido_dev_flags(dev), flags_txt); | ||
71 | } | ||
72 | |||
73 | /* | ||
74 | * Auxiliary function to print an array of strings on stdout. | ||
75 | */ | ||
76 | static void | ||
77 | print_str_array(const char *label, char * const *sa, size_t len) | ||
78 | { | ||
79 | if (len == 0) | ||
80 | return; | ||
81 | |||
82 | printf("%s strings: ", label); | ||
83 | |||
84 | for (size_t i = 0; i < len; i++) | ||
85 | printf("%s%s", i > 0 ? ", " : "", sa[i]); | ||
86 | |||
87 | printf("\n"); | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * Auxiliary function to print (char *, bool) pairs on stdout. | ||
92 | */ | ||
93 | static void | ||
94 | print_opt_array(const char *label, char * const *name, const bool *value, | ||
95 | size_t len) | ||
96 | { | ||
97 | if (len == 0) | ||
98 | return; | ||
99 | |||
100 | printf("%s: ", label); | ||
101 | |||
102 | for (size_t i = 0; i < len; i++) | ||
103 | printf("%s%s%s", i > 0 ? ", " : "", | ||
104 | value[i] ? "" : "no", name[i]); | ||
105 | |||
106 | printf("\n"); | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * Auxiliary function to print an authenticator's AAGUID on stdout. | ||
111 | */ | ||
112 | static void | ||
113 | print_aaguid(const unsigned char *buf, size_t buflen) | ||
114 | { | ||
115 | printf("aaguid: "); | ||
116 | |||
117 | while (buflen--) | ||
118 | printf("%02x", *buf++); | ||
119 | |||
120 | printf("\n"); | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * Auxiliary function to print an authenticator's maximum message size on | ||
125 | * stdout. | ||
126 | */ | ||
127 | static void | ||
128 | print_maxmsgsiz(uint64_t maxmsgsiz) | ||
129 | { | ||
130 | printf("maxmsgsiz: %d\n", (int)maxmsgsiz); | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * Auxiliary function to print an array of bytes on stdout. | ||
135 | */ | ||
136 | static void | ||
137 | print_byte_array(const char *label, const uint8_t *ba, size_t len) | ||
138 | { | ||
139 | if (len == 0) | ||
140 | return; | ||
141 | |||
142 | printf("%s: ", label); | ||
143 | |||
144 | for (size_t i = 0; i < len; i++) | ||
145 | printf("%s%u", i > 0 ? ", " : "", (unsigned)ba[i]); | ||
146 | |||
147 | printf("\n"); | ||
148 | } | ||
149 | |||
150 | static void | ||
151 | getinfo(const char *path) | ||
152 | { | ||
153 | fido_dev_t *dev; | ||
154 | fido_cbor_info_t *ci; | ||
155 | int r; | ||
156 | |||
157 | fido_init(0); | ||
158 | |||
159 | if ((dev = fido_dev_new()) == NULL) | ||
160 | errx(1, "fido_dev_new"); | ||
161 | if ((r = fido_dev_open(dev, path)) != FIDO_OK) | ||
162 | errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r); | ||
163 | |||
164 | print_attr(dev); | ||
165 | |||
166 | if (fido_dev_is_fido2(dev) == false) | ||
167 | goto end; | ||
168 | if ((ci = fido_cbor_info_new()) == NULL) | ||
169 | errx(1, "fido_cbor_info_new"); | ||
170 | if ((r = fido_dev_get_cbor_info(dev, ci)) != FIDO_OK) | ||
171 | errx(1, "fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r); | ||
172 | |||
173 | /* print supported protocol versions */ | ||
174 | print_str_array("version", fido_cbor_info_versions_ptr(ci), | ||
175 | fido_cbor_info_versions_len(ci)); | ||
176 | |||
177 | /* print supported extensions */ | ||
178 | print_str_array("extension", fido_cbor_info_extensions_ptr(ci), | ||
179 | fido_cbor_info_extensions_len(ci)); | ||
180 | |||
181 | /* print aaguid */ | ||
182 | print_aaguid(fido_cbor_info_aaguid_ptr(ci), | ||
183 | fido_cbor_info_aaguid_len(ci)); | ||
184 | |||
185 | /* print supported options */ | ||
186 | print_opt_array("options", fido_cbor_info_options_name_ptr(ci), | ||
187 | fido_cbor_info_options_value_ptr(ci), | ||
188 | fido_cbor_info_options_len(ci)); | ||
189 | |||
190 | /* print maximum message size */ | ||
191 | print_maxmsgsiz(fido_cbor_info_maxmsgsiz(ci)); | ||
192 | |||
193 | /* print supported pin protocols */ | ||
194 | print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci), | ||
195 | fido_cbor_info_protocols_len(ci)); | ||
196 | |||
197 | fido_cbor_info_free(&ci); | ||
198 | end: | ||
199 | if ((r = fido_dev_close(dev)) != FIDO_OK) | ||
200 | errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r); | ||
201 | |||
202 | fido_dev_free(&dev); | ||
203 | } | ||
204 | |||
205 | int | ||
206 | main(int argc, char **argv) | ||
207 | { | ||
208 | if (argc != 2) { | ||
209 | fprintf(stderr, "usage: info <device>\n"); | ||
210 | exit(EXIT_FAILURE); | ||
211 | } | ||
212 | |||
213 | getinfo(argv[1]); | ||
214 | |||
215 | exit(0); | ||
216 | } | ||
diff --git a/examples/manifest.c b/examples/manifest.c new file mode 100644 index 0000000..895447a --- /dev/null +++ b/examples/manifest.c | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2018 Yubico AB. All rights reserved. | ||
3 | * Use of this source code is governed by a BSD-style | ||
4 | * license that can be found in the LICENSE file. | ||
5 | */ | ||
6 | |||
7 | #include <openssl/ec.h> | ||
8 | |||
9 | #include <stdbool.h> | ||
10 | #include <stdio.h> | ||
11 | #include <stdlib.h> | ||
12 | |||
13 | #include "../openbsd-compat/openbsd-compat.h" | ||
14 | |||
15 | #include "fido.h" | ||
16 | |||
17 | int | ||
18 | main(void) | ||
19 | { | ||
20 | fido_dev_info_t *devlist; | ||
21 | size_t ndevs; | ||
22 | int r; | ||
23 | |||
24 | fido_init(0); | ||
25 | |||
26 | if ((devlist = fido_dev_info_new(64)) == NULL) | ||
27 | errx(1, "fido_dev_info_new"); | ||
28 | |||
29 | if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK) | ||
30 | errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r); | ||
31 | |||
32 | for (size_t i = 0; i < ndevs; i++) { | ||
33 | const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); | ||
34 | printf("%s: vendor=0x%04x, product=0x%04x (%s %s)\n", | ||
35 | fido_dev_info_path(di), | ||
36 | (uint16_t)fido_dev_info_vendor(di), | ||
37 | (uint16_t)fido_dev_info_product(di), | ||
38 | fido_dev_info_manufacturer_string(di), | ||
39 | fido_dev_info_product_string(di)); | ||
40 | } | ||
41 | |||
42 | fido_dev_info_free(&devlist, ndevs); | ||
43 | |||
44 | exit(0); | ||
45 | } | ||
diff --git a/examples/reset.c b/examples/reset.c new file mode 100644 index 0000000..36a7de2 --- /dev/null +++ b/examples/reset.c | |||
@@ -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 | /* | ||
8 | * Perform a factory reset on a given authenticator. | ||
9 | */ | ||
10 | |||
11 | #include <openssl/ec.h> | ||
12 | |||
13 | #include <stdbool.h> | ||
14 | #include <stdint.h> | ||
15 | #include <stdio.h> | ||
16 | #include <stdlib.h> | ||
17 | |||
18 | #include "../openbsd-compat/openbsd-compat.h" | ||
19 | |||
20 | #include "fido.h" | ||
21 | #include "extern.h" | ||
22 | |||
23 | #ifdef SIGNAL_EXAMPLE | ||
24 | extern volatile sig_atomic_t got_signal; | ||
25 | #endif | ||
26 | |||
27 | int | ||
28 | main(int argc, char **argv) | ||
29 | { | ||
30 | fido_dev_t *dev; | ||
31 | int r; | ||
32 | |||
33 | if (argc != 2) { | ||
34 | fprintf(stderr, "usage: reset <device>\n"); | ||
35 | exit(EXIT_FAILURE); | ||
36 | } | ||
37 | |||
38 | fido_init(0); | ||
39 | |||
40 | if ((dev = fido_dev_new()) == NULL) | ||
41 | errx(1, "fido_dev_new"); | ||
42 | |||
43 | if ((r = fido_dev_open(dev, argv[1])) != FIDO_OK) | ||
44 | errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r); | ||
45 | |||
46 | #ifdef SIGNAL_EXAMPLE | ||
47 | prepare_signal_handler(SIGINT); | ||
48 | #endif | ||
49 | |||
50 | if ((r = fido_dev_reset(dev)) != FIDO_OK) { | ||
51 | #ifdef SIGNAL_EXAMPLE | ||
52 | if (got_signal) | ||
53 | fido_dev_cancel(dev); | ||
54 | #endif | ||
55 | errx(1, "fido_reset: %s (0x%x)", fido_strerr(r), r); | ||
56 | } | ||
57 | |||
58 | if ((r = fido_dev_close(dev)) != FIDO_OK) | ||
59 | errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r); | ||
60 | |||
61 | fido_dev_free(&dev); | ||
62 | |||
63 | exit(0); | ||
64 | } | ||
diff --git a/examples/retries.c b/examples/retries.c new file mode 100644 index 0000000..3ed7558 --- /dev/null +++ b/examples/retries.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 | * Get an authenticator's number of PIN attempts left. | ||
9 | */ | ||
10 | |||
11 | #include <openssl/ec.h> | ||
12 | |||
13 | #include <stdbool.h> | ||
14 | #include <stdio.h> | ||
15 | #include <stdlib.h> | ||
16 | |||
17 | #include "../openbsd-compat/openbsd-compat.h" | ||
18 | |||
19 | #include "fido.h" | ||
20 | |||
21 | int | ||
22 | main(int argc, char **argv) | ||
23 | { | ||
24 | fido_dev_t *dev; | ||
25 | int n; | ||
26 | int r; | ||
27 | |||
28 | if (argc != 2) { | ||
29 | fprintf(stderr, "usage: retries <device>\n"); | ||
30 | exit(EXIT_FAILURE); | ||
31 | } | ||
32 | |||
33 | fido_init(0); | ||
34 | |||
35 | if ((dev = fido_dev_new()) == NULL) | ||
36 | errx(1, "fido_dev_new"); | ||
37 | |||
38 | if ((r = fido_dev_open(dev, argv[1])) != FIDO_OK) | ||
39 | errx(1, "fido_open: %s (0x%x)", fido_strerr(r), r); | ||
40 | |||
41 | if ((r = fido_dev_get_retry_count(dev, &n)) != FIDO_OK) | ||
42 | errx(1, "fido_get_retries: %s (0x%x)", fido_strerr(r), r); | ||
43 | |||
44 | if ((r = fido_dev_close(dev)) != FIDO_OK) | ||
45 | errx(1, "fido_close: %s (0x%x)", fido_strerr(r), r); | ||
46 | |||
47 | fido_dev_free(&dev); | ||
48 | |||
49 | printf("%d\n", n); | ||
50 | |||
51 | exit(0); | ||
52 | } | ||
diff --git a/examples/setpin.c b/examples/setpin.c new file mode 100644 index 0000000..75d3d4a --- /dev/null +++ b/examples/setpin.c | |||
@@ -0,0 +1,59 @@ | |||
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 | * Configure a PIN on a given authenticator. | ||
9 | */ | ||
10 | |||
11 | #include <openssl/ec.h> | ||
12 | |||
13 | #include <stdbool.h> | ||
14 | #include <stdint.h> | ||
15 | #include <stdio.h> | ||
16 | #include <stdlib.h> | ||
17 | |||
18 | #include "../openbsd-compat/openbsd-compat.h" | ||
19 | |||
20 | #include "fido.h" | ||
21 | |||
22 | static void | ||
23 | setpin(const char *path, const char *pin, const char *oldpin) | ||
24 | { | ||
25 | fido_dev_t *dev; | ||
26 | int r; | ||
27 | |||
28 | fido_init(0); | ||
29 | |||
30 | if ((dev = fido_dev_new()) == NULL) | ||
31 | errx(1, "fido_dev_new"); | ||
32 | |||
33 | if ((r = fido_dev_open(dev, path)) != FIDO_OK) | ||
34 | errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r); | ||
35 | |||
36 | if ((r = fido_dev_set_pin(dev, pin, oldpin)) != FIDO_OK) | ||
37 | errx(1, "fido_setpin: %s (0x%x)", fido_strerr(r), r); | ||
38 | |||
39 | if ((r = fido_dev_close(dev)) != FIDO_OK) | ||
40 | errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r); | ||
41 | |||
42 | fido_dev_free(&dev); | ||
43 | } | ||
44 | |||
45 | int | ||
46 | main(int argc, char **argv) | ||
47 | { | ||
48 | if (argc < 3 || argc > 4) { | ||
49 | fprintf(stderr, "usage: setpin <pin> [oldpin] <device>\n"); | ||
50 | exit(EXIT_FAILURE); | ||
51 | } | ||
52 | |||
53 | if (argc == 3) | ||
54 | setpin(argv[2], argv[1], NULL); | ||
55 | else | ||
56 | setpin(argv[3], argv[1], argv[2]); | ||
57 | |||
58 | exit(0); | ||
59 | } | ||
diff --git a/examples/util.c b/examples/util.c new file mode 100644 index 0000000..2f6a845 --- /dev/null +++ b/examples/util.c | |||
@@ -0,0 +1,415 @@ | |||
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 <errno.h> | ||
15 | #include <fcntl.h> | ||
16 | #include <limits.h> | ||
17 | #include <stdbool.h> | ||
18 | #include <stdlib.h> | ||
19 | #include <string.h> | ||
20 | #ifdef HAVE_SIGNAL_H | ||
21 | #include <signal.h> | ||
22 | #endif | ||
23 | #ifdef HAVE_UNISTD_H | ||
24 | #include <unistd.h> | ||
25 | #endif | ||
26 | #ifdef _MSC_VER | ||
27 | #include "../openbsd-compat/posix_win.h" | ||
28 | #endif | ||
29 | |||
30 | #include "../openbsd-compat/openbsd-compat.h" | ||
31 | |||
32 | #include "fido.h" | ||
33 | #include "fido/es256.h" | ||
34 | #include "fido/rs256.h" | ||
35 | #include "fido/eddsa.h" | ||
36 | #include "extern.h" | ||
37 | |||
38 | #ifdef SIGNAL_EXAMPLE | ||
39 | volatile sig_atomic_t got_signal = 0; | ||
40 | |||
41 | static void | ||
42 | signal_handler(int signo) | ||
43 | { | ||
44 | (void)signo; | ||
45 | got_signal = 1; | ||
46 | } | ||
47 | |||
48 | void | ||
49 | prepare_signal_handler(int signo) | ||
50 | { | ||
51 | struct sigaction sa; | ||
52 | |||
53 | memset(&sa, 0, sizeof(sa)); | ||
54 | |||
55 | sigemptyset(&sa.sa_mask); | ||
56 | sa.sa_handler = signal_handler; | ||
57 | |||
58 | if (sigaction(signo, &sa, NULL) < 0) | ||
59 | err(1, "sigaction"); | ||
60 | } | ||
61 | #endif | ||
62 | |||
63 | int | ||
64 | base10(const char *str, long long *ll) | ||
65 | { | ||
66 | char *ep; | ||
67 | |||
68 | *ll = strtoll(str, &ep, 10); | ||
69 | if (str == ep || *ep != '\0') | ||
70 | return (-1); | ||
71 | else if (*ll == LLONG_MIN && errno == ERANGE) | ||
72 | return (-1); | ||
73 | else if (*ll == LLONG_MAX && errno == ERANGE) | ||
74 | return (-1); | ||
75 | |||
76 | return (0); | ||
77 | } | ||
78 | |||
79 | int | ||
80 | write_blob(const char *path, const unsigned char *ptr, size_t len) | ||
81 | { | ||
82 | int fd, ok = -1; | ||
83 | ssize_t n; | ||
84 | |||
85 | if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) { | ||
86 | warn("open %s", path); | ||
87 | goto fail; | ||
88 | } | ||
89 | |||
90 | if ((n = write(fd, ptr, len)) < 0) { | ||
91 | warn("write"); | ||
92 | goto fail; | ||
93 | } | ||
94 | if ((size_t)n != len) { | ||
95 | warnx("write"); | ||
96 | goto fail; | ||
97 | } | ||
98 | |||
99 | ok = 0; | ||
100 | fail: | ||
101 | if (fd != -1) { | ||
102 | close(fd); | ||
103 | } | ||
104 | |||
105 | return (ok); | ||
106 | } | ||
107 | |||
108 | int | ||
109 | read_blob(const char *path, unsigned char **ptr, size_t *len) | ||
110 | { | ||
111 | int fd, ok = -1; | ||
112 | struct stat st; | ||
113 | ssize_t n; | ||
114 | |||
115 | *ptr = NULL; | ||
116 | *len = 0; | ||
117 | |||
118 | if ((fd = open(path, O_RDONLY)) < 0) { | ||
119 | warn("open %s", path); | ||
120 | goto fail; | ||
121 | } | ||
122 | if (fstat(fd, &st) < 0) { | ||
123 | warn("stat %s", path); | ||
124 | goto fail; | ||
125 | } | ||
126 | if (st.st_size < 0) { | ||
127 | warnx("stat %s: invalid size", path); | ||
128 | goto fail; | ||
129 | } | ||
130 | *len = (size_t)st.st_size; | ||
131 | if ((*ptr = malloc(*len)) == NULL) { | ||
132 | warn("malloc"); | ||
133 | goto fail; | ||
134 | } | ||
135 | if ((n = read(fd, *ptr, *len)) < 0) { | ||
136 | warn("read"); | ||
137 | goto fail; | ||
138 | } | ||
139 | if ((size_t)n != *len) { | ||
140 | warnx("read"); | ||
141 | goto fail; | ||
142 | } | ||
143 | |||
144 | ok = 0; | ||
145 | fail: | ||
146 | if (fd != -1) { | ||
147 | close(fd); | ||
148 | } | ||
149 | if (ok < 0) { | ||
150 | free(*ptr); | ||
151 | *ptr = NULL; | ||
152 | *len = 0; | ||
153 | } | ||
154 | |||
155 | return (ok); | ||
156 | } | ||
157 | |||
158 | EC_KEY * | ||
159 | read_ec_pubkey(const char *path) | ||
160 | { | ||
161 | FILE *fp = NULL; | ||
162 | EVP_PKEY *pkey = NULL; | ||
163 | EC_KEY *ec = NULL; | ||
164 | |||
165 | if ((fp = fopen(path, "r")) == NULL) { | ||
166 | warn("fopen"); | ||
167 | goto fail; | ||
168 | } | ||
169 | |||
170 | if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { | ||
171 | warnx("PEM_read_PUBKEY"); | ||
172 | goto fail; | ||
173 | } | ||
174 | if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) { | ||
175 | warnx("EVP_PKEY_get1_EC_KEY"); | ||
176 | goto fail; | ||
177 | } | ||
178 | |||
179 | fail: | ||
180 | if (fp != NULL) { | ||
181 | fclose(fp); | ||
182 | } | ||
183 | if (pkey != NULL) { | ||
184 | EVP_PKEY_free(pkey); | ||
185 | } | ||
186 | |||
187 | return (ec); | ||
188 | } | ||
189 | |||
190 | int | ||
191 | write_ec_pubkey(const char *path, const void *ptr, size_t len) | ||
192 | { | ||
193 | FILE *fp = NULL; | ||
194 | EVP_PKEY *pkey = NULL; | ||
195 | es256_pk_t *pk = NULL; | ||
196 | int fd = -1; | ||
197 | int ok = -1; | ||
198 | |||
199 | if ((pk = es256_pk_new()) == NULL) { | ||
200 | warnx("es256_pk_new"); | ||
201 | goto fail; | ||
202 | } | ||
203 | |||
204 | if (es256_pk_from_ptr(pk, ptr, len) != FIDO_OK) { | ||
205 | warnx("es256_pk_from_ptr"); | ||
206 | goto fail; | ||
207 | } | ||
208 | |||
209 | if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) { | ||
210 | warn("open %s", path); | ||
211 | goto fail; | ||
212 | } | ||
213 | |||
214 | if ((fp = fdopen(fd, "w")) == NULL) { | ||
215 | warn("fdopen"); | ||
216 | goto fail; | ||
217 | } | ||
218 | fd = -1; /* owned by fp now */ | ||
219 | |||
220 | if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) { | ||
221 | warnx("es256_pk_to_EVP_PKEY"); | ||
222 | goto fail; | ||
223 | } | ||
224 | |||
225 | if (PEM_write_PUBKEY(fp, pkey) == 0) { | ||
226 | warnx("PEM_write_PUBKEY"); | ||
227 | goto fail; | ||
228 | } | ||
229 | |||
230 | ok = 0; | ||
231 | fail: | ||
232 | es256_pk_free(&pk); | ||
233 | |||
234 | if (fp != NULL) { | ||
235 | fclose(fp); | ||
236 | } | ||
237 | if (fd != -1) { | ||
238 | close(fd); | ||
239 | } | ||
240 | if (pkey != NULL) { | ||
241 | EVP_PKEY_free(pkey); | ||
242 | } | ||
243 | |||
244 | return (ok); | ||
245 | } | ||
246 | |||
247 | RSA * | ||
248 | read_rsa_pubkey(const char *path) | ||
249 | { | ||
250 | FILE *fp = NULL; | ||
251 | EVP_PKEY *pkey = NULL; | ||
252 | RSA *rsa = NULL; | ||
253 | |||
254 | if ((fp = fopen(path, "r")) == NULL) { | ||
255 | warn("fopen"); | ||
256 | goto fail; | ||
257 | } | ||
258 | |||
259 | if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { | ||
260 | warnx("PEM_read_PUBKEY"); | ||
261 | goto fail; | ||
262 | } | ||
263 | if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) { | ||
264 | warnx("EVP_PKEY_get1_RSA"); | ||
265 | goto fail; | ||
266 | } | ||
267 | |||
268 | fail: | ||
269 | if (fp != NULL) { | ||
270 | fclose(fp); | ||
271 | } | ||
272 | if (pkey != NULL) { | ||
273 | EVP_PKEY_free(pkey); | ||
274 | } | ||
275 | |||
276 | return (rsa); | ||
277 | } | ||
278 | |||
279 | int | ||
280 | write_rsa_pubkey(const char *path, const void *ptr, size_t len) | ||
281 | { | ||
282 | FILE *fp = NULL; | ||
283 | EVP_PKEY *pkey = NULL; | ||
284 | rs256_pk_t *pk = NULL; | ||
285 | int fd = -1; | ||
286 | int ok = -1; | ||
287 | |||
288 | if ((pk = rs256_pk_new()) == NULL) { | ||
289 | warnx("rs256_pk_new"); | ||
290 | goto fail; | ||
291 | } | ||
292 | |||
293 | if (rs256_pk_from_ptr(pk, ptr, len) != FIDO_OK) { | ||
294 | warnx("rs256_pk_from_ptr"); | ||
295 | goto fail; | ||
296 | } | ||
297 | |||
298 | if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) { | ||
299 | warn("open %s", path); | ||
300 | goto fail; | ||
301 | } | ||
302 | |||
303 | if ((fp = fdopen(fd, "w")) == NULL) { | ||
304 | warn("fdopen"); | ||
305 | goto fail; | ||
306 | } | ||
307 | fd = -1; /* owned by fp now */ | ||
308 | |||
309 | if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) { | ||
310 | warnx("rs256_pk_to_EVP_PKEY"); | ||
311 | goto fail; | ||
312 | } | ||
313 | |||
314 | if (PEM_write_PUBKEY(fp, pkey) == 0) { | ||
315 | warnx("PEM_write_PUBKEY"); | ||
316 | goto fail; | ||
317 | } | ||
318 | |||
319 | ok = 0; | ||
320 | fail: | ||
321 | rs256_pk_free(&pk); | ||
322 | |||
323 | if (fp != NULL) { | ||
324 | fclose(fp); | ||
325 | } | ||
326 | if (fd != -1) { | ||
327 | close(fd); | ||
328 | } | ||
329 | if (pkey != NULL) { | ||
330 | EVP_PKEY_free(pkey); | ||
331 | } | ||
332 | |||
333 | return (ok); | ||
334 | } | ||
335 | |||
336 | EVP_PKEY * | ||
337 | read_eddsa_pubkey(const char *path) | ||
338 | { | ||
339 | FILE *fp = NULL; | ||
340 | EVP_PKEY *pkey = NULL; | ||
341 | |||
342 | if ((fp = fopen(path, "r")) == NULL) { | ||
343 | warn("fopen"); | ||
344 | goto fail; | ||
345 | } | ||
346 | |||
347 | if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { | ||
348 | warnx("PEM_read_PUBKEY"); | ||
349 | goto fail; | ||
350 | } | ||
351 | |||
352 | fail: | ||
353 | if (fp) { | ||
354 | fclose(fp); | ||
355 | } | ||
356 | |||
357 | return (pkey); | ||
358 | } | ||
359 | |||
360 | int | ||
361 | write_eddsa_pubkey(const char *path, const void *ptr, size_t len) | ||
362 | { | ||
363 | FILE *fp = NULL; | ||
364 | EVP_PKEY *pkey = NULL; | ||
365 | eddsa_pk_t *pk = NULL; | ||
366 | int fd = -1; | ||
367 | int ok = -1; | ||
368 | |||
369 | if ((pk = eddsa_pk_new()) == NULL) { | ||
370 | warnx("eddsa_pk_new"); | ||
371 | goto fail; | ||
372 | } | ||
373 | |||
374 | if (eddsa_pk_from_ptr(pk, ptr, len) != FIDO_OK) { | ||
375 | warnx("eddsa_pk_from_ptr"); | ||
376 | goto fail; | ||
377 | } | ||
378 | |||
379 | if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) { | ||
380 | warn("open %s", path); | ||
381 | goto fail; | ||
382 | } | ||
383 | |||
384 | if ((fp = fdopen(fd, "w")) == NULL) { | ||
385 | warn("fdopen"); | ||
386 | goto fail; | ||
387 | } | ||
388 | fd = -1; /* owned by fp now */ | ||
389 | |||
390 | if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) { | ||
391 | warnx("eddsa_pk_to_EVP_PKEY"); | ||
392 | goto fail; | ||
393 | } | ||
394 | |||
395 | if (PEM_write_PUBKEY(fp, pkey) == 0) { | ||
396 | warnx("PEM_write_PUBKEY"); | ||
397 | goto fail; | ||
398 | } | ||
399 | |||
400 | ok = 0; | ||
401 | fail: | ||
402 | eddsa_pk_free(&pk); | ||
403 | |||
404 | if (fp != NULL) { | ||
405 | fclose(fp); | ||
406 | } | ||
407 | if (fd != -1) { | ||
408 | close(fd); | ||
409 | } | ||
410 | if (pkey != NULL) { | ||
411 | EVP_PKEY_free(pkey); | ||
412 | } | ||
413 | |||
414 | return (ok); | ||
415 | } | ||