summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authornicoo <nicoo@debian.org>2020-02-12 13:42:22 +0100
committerNicolas Braud-Santoni <nicolas@braud-santoni.eu>2020-02-12 13:42:22 +0100
commitc79050aa44b8836d836c5dd22a383a073c28b74b (patch)
tree7bcca9fabd7718bf87ca600a6594f57b76d8de7d /tools
Import upstream release 1.3.0
Closes: #951184
Diffstat (limited to 'tools')
-rw-r--r--tools/CMakeLists.txt65
-rw-r--r--tools/assert_get.c233
-rw-r--r--tools/assert_verify.c201
-rw-r--r--tools/base64.c135
-rw-r--r--tools/bio.c270
-rw-r--r--tools/cred_make.c221
-rw-r--r--tools/cred_verify.c177
-rw-r--r--tools/credman.c237
-rw-r--r--tools/extern.h64
-rw-r--r--tools/fido2-assert.c54
-rw-r--r--tools/fido2-cred.c52
-rw-r--r--tools/fido2-token.c95
-rw-r--r--tools/pin.c146
-rw-r--r--tools/sk-libfido2.c784
-rwxr-xr-xtools/test.sh96
-rw-r--r--tools/token.c364
-rw-r--r--tools/util.c364
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
5list(APPEND COMPAT_SOURCES
6 ../openbsd-compat/explicit_bzero.c
7 ../openbsd-compat/strlcpy.c
8 ../openbsd-compat/strlcat.c
9)
10
11if(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 )
19else()
20 list(APPEND COMPAT_SOURCES ../openbsd-compat/readpassphrase.c)
21endif()
22
23add_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
32add_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
41add_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
52add_library(sk-libfido2 MODULE sk-libfido2.c)
53set_target_properties(sk-libfido2 PROPERTIES
54 COMPILE_FLAGS "-DSK_STANDALONE -DWITH_OPENSSL"
55 OUTPUT_NAME sk-libfido2
56)
57
58target_link_libraries(fido2-cred ${CRYPTO_LIBRARIES} fido2_shared)
59target_link_libraries(fido2-assert ${CRYPTO_LIBRARIES} fido2_shared)
60target_link_libraries(fido2-token ${CRYPTO_LIBRARIES} fido2_shared)
61target_link_libraries(sk-libfido2 ${CRYPTO_LIBRARIES} fido2_shared)
62
63install(TARGETS fido2-cred fido2-assert fido2-token
64 DESTINATION ${CMAKE_INSTALL_BINDIR})
65install(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
18static fido_assert_t *
19prepare_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
91static void
92print_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
134int
135assert_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
23static fido_assert_t *
24prepare_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
90static void *
91load_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
136int
137assert_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
18int
19base64_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
57fail:
58 BIO_free(bio_b64);
59 BIO_free(bio_mem);
60
61 return (ok);
62}
63
64int
65base64_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
98fail:
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
111int
112base64_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
21static void
22print_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
39int
40bio_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
69int
70bio_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
109static const char *
110plural(uint8_t n)
111{
112 if (n == 1)
113 return "";
114 return "s";
115}
116
117static const char *
118enroll_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
156int
157bio_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
204int
205bio_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
239static const char *
240type_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
252void
253bio_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
18static fido_cred_t *
19prepare_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
80static void
81print_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
120int
121cred_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
18static fido_cred_t *
19prepare_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
102int
103cred_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
21int
22credman_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
50static void
51print_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
66int
67credman_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
97static void
98print_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
136int
137credman_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
166int
167credman_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
201out:
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
212int
213credman_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
10struct 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
25EC_KEY *read_ec_pubkey(const char *);
26fido_dev_t *open_dev(const char *);
27FILE *open_read(const char *);
28FILE *open_write(const char *);
29int assert_get(int, char **);
30int assert_verify(int, char **);
31int base64_decode(char *, void **, size_t *);
32int base64_encode(const void *, size_t, char **);
33int base64_read(FILE *, struct blob *);
34int bio_delete(fido_dev_t *, char *, char *);
35int bio_enroll(char *);
36void bio_info(fido_dev_t *);
37int bio_list(char *);
38int bio_set_name(char *, char *, char *);
39int cred_make(int, char **);
40int cred_verify(int, char **);
41int credman_delete_rk(fido_dev_t *, const char *, char *);
42int credman_get_metadata(fido_dev_t *, const char *);
43int credman_list_rk(char *, const char *);
44int credman_list_rp(char *);
45int credman_print_rk(fido_dev_t *, const char *, char *, char *);
46int pin_change(char *);
47int pin_set(char *);
48int string_read(FILE *, char **);
49int token_delete(int, char **, char *);
50int token_info(int, char **, char *);
51int token_list(int, char **, char *);
52int token_reset(char *);
53int token_set(int, char **, char *);
54int write_ec_pubkey(FILE *, const void *, size_t);
55int write_rsa_pubkey(FILE *, const void *, size_t);
56RSA *read_rsa_pubkey(const char *);
57EVP_PKEY *read_eddsa_pubkey(const char *);
58int write_eddsa_pubkey(FILE *, const void *, size_t);
59void print_cred(FILE *, int, const fido_cred_t *);
60void read_pin(const char *, char *, size_t);
61void usage(void);
62void 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
27void
28usage(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
38int
39main(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
25void
26usage(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
36int
37main(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
15static int action;
16
17void
18usage(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
32static void
33setaction(int ch)
34{
35 if (action)
36 usage();
37 action = ch;
38}
39
40int
41main(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
19int
20pin_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;
70out:
71 explicit_bzero(pin1, sizeof(pin1));
72 explicit_bzero(pin2, sizeof(pin2));
73
74 exit(status);
75}
76
77int
78pin_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;
140out:
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
73struct 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
84struct 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 */
101uint32_t sk_api_version(void);
102
103/* Enroll a U2F key (private key generation) */
104int 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 */
109int 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
114static void skdebug(const char *func, const char *fmt, ...)
115 __attribute__((__format__ (printf, 2, 3)));
116
117static void
118skdebug(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
143uint32_t
144sk_api_version(void)
145{
146 return SK_VERSION_MAJOR;
147}
148
149/* Select the first identified FIDO device attached to the system */
150static char *
151pick_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)
183static int
184get_random_challenge(uint8_t *ptr, size_t len)
185{
186 arc4random_buf(ptr, len);
187
188 return 0;
189}
190#elif defined(HAVE_GETENTROPY)
191static int
192get_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)
202static int
203get_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)
224static int
225get_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. */
241static int
242try_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 */
290static fido_dev_t *
291find_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 */
354static int
355pack_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
424static int
425pack_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
455static int
456pack_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
470int
471sk_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
621static int
622pack_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
659static int
660pack_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
687static int
688pack_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
702int
703sk_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
7if [[ "$#" -ne 1 ]]; then
8 echo "usage: test.sh device" 1>&2
9 exit 1
10fi
11
12read -p "This script will reset the authenticator at $1, permanently erasing "\
13"its credentials. Are you *SURE* you want to proceed (yes/no)? "
14if [[ "${REPLY}" != "yes" ]]; then
15 exit 1
16fi
17
18echo "Resetting authenticator... (tap to continue!)"
19fido2-token -R $1
20
21CRED_PARAM="$(mktemp /tmp/cred_param.XXXXXXXX)"
22ASSERT_PARAM="$(mktemp /tmp/assert_param.XXXXXXXX)"
23ASSERT_PUBKEY="$(mktemp /tmp/assert_pubkey.XXXXXXXX)"
24ES256_CRED="$(mktemp /tmp/es256_cred.XXXXXXX)"
25ES256_CRED_R="$(mktemp /tmp/es256_cred_r.XXXXXXXX)"
26
27cleanup() {
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
36trap cleanup EXIT
37
38dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 > "${CRED_PARAM}"
39echo "Boring Relying Party" >> "${CRED_PARAM}"
40echo "Boring User Name" >> "${CRED_PARAM}"
41dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 >> "${CRED_PARAM}"
42echo "Credential parameters:"
43cat "${CRED_PARAM}"
44
45echo "Generating non-resident ES256 credential... (tap to continue!)"
46fido2-cred -M -i "${CRED_PARAM}" $1 | fido2-cred -V | tee "${ES256_CRED}"
47echo "Generating resident ES256 credential... (tap to continue!)"
48fido2-cred -M -r -i "${CRED_PARAM}" $1 | fido2-cred -V | tee "${ES256_CRED_R}"
49
50PIN1="$(dd if=/dev/urandom | tr -cd '[:print:]' | fold -w50 | head -1)"
51PIN2="$(dd if=/dev/urandom | tr -cd '[:print:]' | fold -w50 | head -1)"
52
53echo "Setting ${PIN1} as the PIN..."
54echo -e "${PIN1}\n${PIN1}" | setsid -w fido2-token -S $1
55echo "Changing PIN from ${PIN1} to ${PIN2}..."
56echo -e "${PIN1}\n${PIN2}\n${PIN2}" | setsid -w fido2-token -C $1
57echo ""
58
59echo "Testing non-resident ES256 credential..."
60echo "Getting assertion without user presence verification..."
61dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 > "${ASSERT_PARAM}"
62echo "Boring Relying Party" >> "${ASSERT_PARAM}"
63head -1 "${ES256_CRED}" >> "${ASSERT_PARAM}"
64tail -n +2 "${ES256_CRED}" > "${ASSERT_PUBKEY}"
65echo "Assertion parameters:"
66cat "${ASSERT_PARAM}"
67fido2-assert -G -i "${ASSERT_PARAM}" $1 | fido2-assert -V "${ASSERT_PUBKEY}"
68echo "Checking that the user presence bit is observed..."
69! fido2-assert -G -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}"
70echo "Checking that the user verification bit is observed..."
71! fido2-assert -G -i "${ASSERT_PARAM}" $1 | fido2-assert -V -v "${ASSERT_PUBKEY}"
72echo "Getting assertion _with_ user presence verification... (tap to continue!)"
73fido2-assert -G -p -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}"
74echo "Getting assertion _with_ user verification..."
75echo -e "${PIN2}\n" | setsid -w fido2-assert -G -v -i "${ASSERT_PARAM}" $1 | \
76 fido2-assert -V -v "${ASSERT_PUBKEY}"
77echo ""
78
79echo "Testing resident ES256 credential..."
80echo "Getting assertion without user presence verification..."
81dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 > "${ASSERT_PARAM}"
82echo "Boring Relying Party" >> "${ASSERT_PARAM}"
83tail -n +2 "${ES256_CRED_R}" > "${ASSERT_PUBKEY}"
84echo "Assertion parameters:"
85cat "${ASSERT_PARAM}"
86fido2-assert -G -r -i "${ASSERT_PARAM}" $1 | fido2-assert -V "${ASSERT_PUBKEY}"
87echo "Checking that the user presence bit is observed..."
88! fido2-assert -G -r -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}"
89echo "Checking that the user verification bit is observed..."
90! fido2-assert -G -r -i "${ASSERT_PARAM}" $1 | fido2-assert -V -v "${ASSERT_PUBKEY}"
91echo "Getting assertion _with_ user presence verification... (tap to continue!)"
92fido2-assert -G -r -p -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}"
93echo "Getting assertion _with_ user verification..."
94echo -e "${PIN2}\n" | setsid -w fido2-assert -G -v -r -i "${ASSERT_PARAM}" $1 | \
95 fido2-assert -V -v "${ASSERT_PUBKEY}"
96echo ""
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
19static void
20format_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;
49toolong:
50 strlcpy(ret, "toolong", retlen);
51}
52
53static void
54print_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
67static void
68print_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
81static void
82print_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
97static void
98print_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
108static void
109print_maxmsgsiz(uint64_t maxmsgsiz)
110{
111 printf("maxmsgsiz: %d\n", (int)maxmsgsiz);
112}
113
114static void
115print_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
128int
129token_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);
211end:
212 fido_dev_close(dev);
213 fido_dev_free(&dev);
214
215 exit(0);
216}
217
218int
219token_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
237int
238token_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
274int
275token_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
332int
333token_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
32void
33read_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
45FILE *
46open_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
61FILE *
62open_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
81void
82xxd(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
99int
100string_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
120fido_dev_t *
121open_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
136EC_KEY *
137read_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
157fail:
158 if (fp) {
159 fclose(fp);
160 }
161 if (pkey) {
162 EVP_PKEY_free(pkey);
163 }
164
165 return (ec);
166}
167
168int
169write_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;
196fail:
197 es256_pk_free(&pk);
198
199 if (pkey != NULL) {
200 EVP_PKEY_free(pkey);
201 }
202
203 return (ok);
204}
205
206RSA *
207read_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
227fail:
228 if (fp) {
229 fclose(fp);
230 }
231 if (pkey) {
232 EVP_PKEY_free(pkey);
233 }
234
235 return (rsa);
236}
237
238int
239write_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;
266fail:
267 rs256_pk_free(&pk);
268
269 if (pkey != NULL) {
270 EVP_PKEY_free(pkey);
271 }
272
273 return (ok);
274}
275
276EVP_PKEY *
277read_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
292fail:
293 if (fp) {
294 fclose(fp);
295 }
296
297 return (pkey);
298}
299
300int
301write_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;
328fail:
329 eddsa_pk_free(&pk);
330
331 if (pkey != NULL) {
332 EVP_PKEY_free(pkey);
333 }
334
335 return (ok);
336}
337
338void
339print_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}