1 /*
2 * Copyright (c) 2018-2023 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 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8 #include <errno.h>
9 #include <fido.h>
10 #include <stdbool.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #ifdef HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17
18 #include "../openbsd-compat/openbsd-compat.h"
19 #include "extern.h"
20
21 static const unsigned char cd[32] = {
22 0xf9, 0x64, 0x57, 0xe7, 0x2d, 0x97, 0xf6, 0xbb,
23 0xdd, 0xd7, 0xfb, 0x06, 0x37, 0x62, 0xea, 0x26,
24 0x20, 0x44, 0x8e, 0x69, 0x7c, 0x03, 0xf2, 0x31,
25 0x2f, 0x99, 0xdc, 0xaf, 0x3e, 0x8a, 0x91, 0x6b,
26 };
27
28 static const unsigned char user_id[32] = {
29 0x78, 0x1c, 0x78, 0x60, 0xad, 0x88, 0xd2, 0x63,
30 0x32, 0x62, 0x2a, 0xf1, 0x74, 0x5d, 0xed, 0xb2,
31 0xe7, 0xa4, 0x2b, 0x44, 0x89, 0x29, 0x39, 0xc5,
32 0x56, 0x64, 0x01, 0x27, 0x0d, 0xbb, 0xc4, 0x49,
33 };
34
35 static void
usage(void)36 usage(void)
37 {
38 fprintf(stderr, "usage: cred [-t es256|es384|rs256|eddsa] [-k pubkey] "
39 "[-ei cred_id] [-P pin] [-T seconds] [-b blobkey] [-c cred_protect] [-hruv] "
40 "<device>\n");
41 exit(EXIT_FAILURE);
42 }
43
44 static void
verify_cred(int type,const char * fmt,const unsigned char * authdata_ptr,size_t authdata_len,const unsigned char * attstmt_ptr,size_t attstmt_len,bool rk,bool uv,int ext,int cred_protect,const char * key_out,const char * id_out)45 verify_cred(int type, const char *fmt, const unsigned char *authdata_ptr,
46 size_t authdata_len, const unsigned char *attstmt_ptr, size_t attstmt_len,
47 bool rk, bool uv, int ext, int cred_protect, const char *key_out,
48 const char *id_out)
49 {
50 fido_cred_t *cred;
51 int r;
52
53 if ((cred = fido_cred_new()) == NULL)
54 errx(1, "fido_cred_new");
55
56 /* type */
57 r = fido_cred_set_type(cred, type);
58 if (r != FIDO_OK)
59 errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r);
60
61 /* client data */
62 r = fido_cred_set_clientdata(cred, cd, sizeof(cd));
63 if (r != FIDO_OK)
64 errx(1, "fido_cred_set_clientdata: %s (0x%x)", fido_strerr(r), r);
65
66 /* relying party */
67 r = fido_cred_set_rp(cred, "localhost", "sweet home localhost");
68 if (r != FIDO_OK)
69 errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r);
70
71 /* authdata */
72 r = fido_cred_set_authdata(cred, authdata_ptr, authdata_len);
73 if (r != FIDO_OK)
74 errx(1, "fido_cred_set_authdata: %s (0x%x)", fido_strerr(r), r);
75
76 /* extensions */
77 r = fido_cred_set_extensions(cred, ext);
78 if (r != FIDO_OK)
79 errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r);
80
81 /* resident key */
82 if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
83 errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r);
84
85 /* user verification */
86 if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
87 errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r);
88
89 /* credProt */
90 if (cred_protect != 0 && (r = fido_cred_set_prot(cred,
91 cred_protect)) != FIDO_OK)
92 errx(1, "fido_cred_set_prot: %s (0x%x)", fido_strerr(r), r);
93
94 /* fmt */
95 r = fido_cred_set_fmt(cred, fmt);
96 if (r != FIDO_OK)
97 errx(1, "fido_cred_set_fmt: %s (0x%x)", fido_strerr(r), r);
98
99 if (!strcmp(fido_cred_fmt(cred), "none")) {
100 warnx("no attestation data, skipping credential verification");
101 goto out;
102 }
103
104 /* attestation statement */
105 r = fido_cred_set_attstmt(cred, attstmt_ptr, attstmt_len);
106 if (r != FIDO_OK)
107 errx(1, "fido_cred_set_attstmt: %s (0x%x)", fido_strerr(r), r);
108
109 r = fido_cred_verify(cred);
110 if (r != FIDO_OK)
111 errx(1, "fido_cred_verify: %s (0x%x)", fido_strerr(r), r);
112
113 out:
114 if (key_out != NULL) {
115 /* extract the credential pubkey */
116 if (type == COSE_ES256) {
117 if (write_es256_pubkey(key_out,
118 fido_cred_pubkey_ptr(cred),
119 fido_cred_pubkey_len(cred)) < 0)
120 errx(1, "write_es256_pubkey");
121 } else if (type == COSE_ES384) {
122 if (write_es384_pubkey(key_out,
123 fido_cred_pubkey_ptr(cred),
124 fido_cred_pubkey_len(cred)) < 0)
125 errx(1, "write_es384_pubkey");
126 } else if (type == COSE_RS256) {
127 if (write_rs256_pubkey(key_out,
128 fido_cred_pubkey_ptr(cred),
129 fido_cred_pubkey_len(cred)) < 0)
130 errx(1, "write_rs256_pubkey");
131 } else if (type == COSE_EDDSA) {
132 if (write_eddsa_pubkey(key_out,
133 fido_cred_pubkey_ptr(cred),
134 fido_cred_pubkey_len(cred)) < 0)
135 errx(1, "write_eddsa_pubkey");
136 }
137 }
138
139 if (id_out != NULL) {
140 /* extract the credential id */
141 if (write_blob(id_out, fido_cred_id_ptr(cred),
142 fido_cred_id_len(cred)) < 0)
143 errx(1, "write_blob");
144 }
145
146 fido_cred_free(&cred);
147 }
148
149 int
main(int argc,char ** argv)150 main(int argc, char **argv)
151 {
152 bool rk = false;
153 bool uv = false;
154 bool u2f = false;
155 fido_dev_t *dev;
156 fido_cred_t *cred = NULL;
157 const char *pin = NULL;
158 const char *blobkey_out = NULL;
159 const char *key_out = NULL;
160 const char *id_out = NULL;
161 unsigned char *body = NULL;
162 long long ms = 0;
163 size_t len;
164 int type = COSE_ES256;
165 int ext = 0;
166 int ch;
167 int r;
168 long long cred_protect = 0;
169
170 if ((cred = fido_cred_new()) == NULL)
171 errx(1, "fido_cred_new");
172
173 while ((ch = getopt(argc, argv, "P:T:b:e:hi:k:rt:uvc:")) != -1) {
174 switch (ch) {
175 case 'P':
176 pin = optarg;
177 break;
178 case 'T':
179 if (base10(optarg, &ms) < 0)
180 errx(1, "base10: %s", optarg);
181 if (ms <= 0 || ms > 30)
182 errx(1, "-T: %s must be in (0,30]", optarg);
183 ms *= 1000; /* seconds to milliseconds */
184 break;
185 case 'b':
186 ext |= FIDO_EXT_LARGEBLOB_KEY;
187 blobkey_out = optarg;
188 break;
189 case 'e':
190 if (read_blob(optarg, &body, &len) < 0)
191 errx(1, "read_blob: %s", optarg);
192 r = fido_cred_exclude(cred, body, len);
193 if (r != FIDO_OK)
194 errx(1, "fido_cred_exclude: %s (0x%x)",
195 fido_strerr(r), r);
196 free(body);
197 body = NULL;
198 break;
199 case 'h':
200 ext |= FIDO_EXT_HMAC_SECRET;
201 break;
202 case 'c':
203 if (base10(optarg, &cred_protect) < 0)
204 errx(1, "base10: %s", optarg);
205 if (cred_protect <= 0 || cred_protect > 3)
206 errx(1, "-c: %s must be in (1,3)", optarg);
207 ext |= FIDO_EXT_CRED_PROTECT;
208 break;
209 case 'i':
210 id_out = optarg;
211 break;
212 case 'k':
213 key_out = optarg;
214 break;
215 case 'r':
216 rk = true;
217 break;
218 case 't':
219 if (strcmp(optarg, "es256") == 0)
220 type = COSE_ES256;
221 else if (strcmp(optarg, "es384") == 0)
222 type = COSE_ES384;
223 else if (strcmp(optarg, "rs256") == 0)
224 type = COSE_RS256;
225 else if (strcmp(optarg, "eddsa") == 0)
226 type = COSE_EDDSA;
227 else
228 errx(1, "unknown type %s", optarg);
229 break;
230 case 'u':
231 u2f = true;
232 break;
233 case 'v':
234 uv = true;
235 break;
236 default:
237 usage();
238 }
239 }
240
241 argc -= optind;
242 argv += optind;
243
244 if (argc != 1)
245 usage();
246
247 fido_init(0);
248
249 if ((dev = fido_dev_new()) == NULL)
250 errx(1, "fido_dev_new");
251
252 r = fido_dev_open(dev, argv[0]);
253 if (r != FIDO_OK)
254 errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
255 if (u2f)
256 fido_dev_force_u2f(dev);
257
258 /* type */
259 r = fido_cred_set_type(cred, type);
260 if (r != FIDO_OK)
261 errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r);
262
263 /* client data */
264 r = fido_cred_set_clientdata(cred, cd, sizeof(cd));
265 if (r != FIDO_OK)
266 errx(1, "fido_cred_set_clientdata: %s (0x%x)", fido_strerr(r), r);
267
268 /* relying party */
269 r = fido_cred_set_rp(cred, "localhost", "sweet home localhost");
270 if (r != FIDO_OK)
271 errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r);
272
273 /* user */
274 r = fido_cred_set_user(cred, user_id, sizeof(user_id), "john smith",
275 "jsmith", NULL);
276 if (r != FIDO_OK)
277 errx(1, "fido_cred_set_user: %s (0x%x)", fido_strerr(r), r);
278
279 /* extensions */
280 r = fido_cred_set_extensions(cred, ext);
281 if (r != FIDO_OK)
282 errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r);
283
284 /* resident key */
285 if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
286 errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r);
287
288 /* user verification */
289 if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
290 errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r);
291
292 /* credProt */
293 if (cred_protect != 0 && (r = fido_cred_set_prot(cred,
294 (int)cred_protect)) != FIDO_OK)
295 errx(1, "fido_cred_set_prot: %s (0x%x)", fido_strerr(r), r);
296
297 /* timeout */
298 if (ms != 0 && (r = fido_dev_set_timeout(dev, (int)ms)) != FIDO_OK)
299 errx(1, "fido_dev_set_timeout: %s (0x%x)", fido_strerr(r), r);
300
301 if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) {
302 fido_dev_cancel(dev);
303 errx(1, "fido_makecred: %s (0x%x)", fido_strerr(r), r);
304 }
305
306 r = fido_dev_close(dev);
307 if (r != FIDO_OK)
308 errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
309
310 fido_dev_free(&dev);
311
312 /* when verifying, pin implies uv */
313 if (pin)
314 uv = true;
315
316 verify_cred(type, fido_cred_fmt(cred), fido_cred_authdata_ptr(cred),
317 fido_cred_authdata_len(cred), fido_cred_attstmt_ptr(cred),
318 fido_cred_attstmt_len(cred), rk, uv, ext, fido_cred_prot(cred),
319 key_out, id_out);
320
321 if (blobkey_out != NULL) {
322 /* extract the "largeBlob" key */
323 if (write_blob(blobkey_out, fido_cred_largeblob_key_ptr(cred),
324 fido_cred_largeblob_key_len(cred)) < 0)
325 errx(1, "write_blob");
326 }
327
328 fido_cred_free(&cred);
329
330 exit(0);
331 }
332