1ba9bdd8bSchristos /*
2ba9bdd8bSchristos * Copyright (c) 2018 Yubico AB. All rights reserved.
3ba9bdd8bSchristos * Use of this source code is governed by a BSD-style
4ba9bdd8bSchristos * license that can be found in the LICENSE file.
5*2d40c451Schristos * SPDX-License-Identifier: BSD-2-Clause
6ba9bdd8bSchristos */
7ba9bdd8bSchristos
8ba9bdd8bSchristos #include <fido.h>
9ba9bdd8bSchristos #include <stdio.h>
10ba9bdd8bSchristos #include <stdlib.h>
11ba9bdd8bSchristos #include <string.h>
12ba9bdd8bSchristos #ifdef HAVE_UNISTD_H
13ba9bdd8bSchristos #include <unistd.h>
14ba9bdd8bSchristos #endif
15ba9bdd8bSchristos
16ba9bdd8bSchristos #include "../openbsd-compat/openbsd-compat.h"
17ba9bdd8bSchristos #include "extern.h"
18ba9bdd8bSchristos
191fc1e710Schristos struct toggle {
201fc1e710Schristos fido_opt_t up;
211fc1e710Schristos fido_opt_t uv;
221fc1e710Schristos fido_opt_t pin;
231fc1e710Schristos };
241fc1e710Schristos
251fc1e710Schristos static const char *
opt2str(fido_opt_t v)261fc1e710Schristos opt2str(fido_opt_t v)
271fc1e710Schristos {
281fc1e710Schristos switch (v) {
291fc1e710Schristos case FIDO_OPT_OMIT:
301fc1e710Schristos return "omit";
311fc1e710Schristos case FIDO_OPT_TRUE:
321fc1e710Schristos return "true";
331fc1e710Schristos case FIDO_OPT_FALSE:
341fc1e710Schristos return "false";
351fc1e710Schristos default:
361fc1e710Schristos return "unknown";
371fc1e710Schristos }
381fc1e710Schristos }
391fc1e710Schristos
401fc1e710Schristos static void
parse_toggle(const char * str,struct toggle * opt)411fc1e710Schristos parse_toggle(const char *str, struct toggle *opt)
421fc1e710Schristos {
431fc1e710Schristos fido_opt_t *k;
441fc1e710Schristos fido_opt_t v;
451fc1e710Schristos char *assignment;
461fc1e710Schristos char *key;
471fc1e710Schristos char *val;
481fc1e710Schristos
491fc1e710Schristos if ((assignment = strdup(str)) == NULL)
501fc1e710Schristos err(1, "strdup");
511fc1e710Schristos if ((val = strchr(assignment, '=')) == NULL)
521fc1e710Schristos errx(1, "invalid assignment '%s'", assignment);
531fc1e710Schristos
541fc1e710Schristos key = assignment;
551fc1e710Schristos *val++ = '\0';
561fc1e710Schristos
571fc1e710Schristos if (!strcmp(val, "true"))
581fc1e710Schristos v = FIDO_OPT_TRUE;
591fc1e710Schristos else if (!strcmp(val, "false"))
601fc1e710Schristos v = FIDO_OPT_FALSE;
611fc1e710Schristos else
621fc1e710Schristos errx(1, "unknown value '%s'", val);
631fc1e710Schristos
641fc1e710Schristos if (!strcmp(key, "up"))
651fc1e710Schristos k = &opt->up;
661fc1e710Schristos else if (!strcmp(key, "uv"))
671fc1e710Schristos k = &opt->uv;
681fc1e710Schristos else if (!strcmp(key, "pin"))
691fc1e710Schristos k = &opt->pin;
701fc1e710Schristos else
711fc1e710Schristos errx(1, "unknown key '%s'", key);
721fc1e710Schristos
731fc1e710Schristos free(assignment);
741fc1e710Schristos
751fc1e710Schristos *k = v;
761fc1e710Schristos }
771fc1e710Schristos
78ba9bdd8bSchristos static fido_assert_t *
prepare_assert(FILE * in_f,int flags,const struct toggle * opt)791fc1e710Schristos prepare_assert(FILE *in_f, int flags, const struct toggle *opt)
80ba9bdd8bSchristos {
81ba9bdd8bSchristos fido_assert_t *assert = NULL;
82ba9bdd8bSchristos struct blob cdh;
83ba9bdd8bSchristos struct blob id;
84ba9bdd8bSchristos struct blob hmac_salt;
85ba9bdd8bSchristos char *rpid = NULL;
86ba9bdd8bSchristos int r;
87ba9bdd8bSchristos
88ba9bdd8bSchristos memset(&cdh, 0, sizeof(cdh));
89ba9bdd8bSchristos memset(&id, 0, sizeof(id));
90ba9bdd8bSchristos memset(&hmac_salt, 0, sizeof(hmac_salt));
91ba9bdd8bSchristos
92ba9bdd8bSchristos r = base64_read(in_f, &cdh);
93ba9bdd8bSchristos r |= string_read(in_f, &rpid);
94ba9bdd8bSchristos if ((flags & FLAG_RK) == 0)
95ba9bdd8bSchristos r |= base64_read(in_f, &id);
96ba9bdd8bSchristos if (flags & FLAG_HMAC)
97ba9bdd8bSchristos r |= base64_read(in_f, &hmac_salt);
98ba9bdd8bSchristos if (r < 0)
99ba9bdd8bSchristos errx(1, "input error");
100ba9bdd8bSchristos
101ba9bdd8bSchristos if (flags & FLAG_DEBUG) {
102ba9bdd8bSchristos fprintf(stderr, "client data hash:\n");
103ba9bdd8bSchristos xxd(cdh.ptr, cdh.len);
104ba9bdd8bSchristos fprintf(stderr, "relying party id: %s\n", rpid);
105ba9bdd8bSchristos if ((flags & FLAG_RK) == 0) {
106ba9bdd8bSchristos fprintf(stderr, "credential id:\n");
107ba9bdd8bSchristos xxd(id.ptr, id.len);
108ba9bdd8bSchristos }
1091fc1e710Schristos fprintf(stderr, "up=%s\n", opt2str(opt->up));
1101fc1e710Schristos fprintf(stderr, "uv=%s\n", opt2str(opt->uv));
1111fc1e710Schristos fprintf(stderr, "pin=%s\n", opt2str(opt->pin));
112ba9bdd8bSchristos }
113ba9bdd8bSchristos
114ba9bdd8bSchristos if ((assert = fido_assert_new()) == NULL)
115ba9bdd8bSchristos errx(1, "fido_assert_new");
116ba9bdd8bSchristos
117ba9bdd8bSchristos if ((r = fido_assert_set_clientdata_hash(assert, cdh.ptr,
118ba9bdd8bSchristos cdh.len)) != FIDO_OK ||
119ba9bdd8bSchristos (r = fido_assert_set_rp(assert, rpid)) != FIDO_OK)
120ba9bdd8bSchristos errx(1, "fido_assert_set: %s", fido_strerr(r));
1211fc1e710Schristos if ((r = fido_assert_set_up(assert, opt->up)) != FIDO_OK)
122ba9bdd8bSchristos errx(1, "fido_assert_set_up: %s", fido_strerr(r));
1231fc1e710Schristos if ((r = fido_assert_set_uv(assert, opt->uv)) != FIDO_OK)
124ba9bdd8bSchristos errx(1, "fido_assert_set_uv: %s", fido_strerr(r));
1251fc1e710Schristos
126ba9bdd8bSchristos if (flags & FLAG_HMAC) {
127ba9bdd8bSchristos if ((r = fido_assert_set_extensions(assert,
128ba9bdd8bSchristos FIDO_EXT_HMAC_SECRET)) != FIDO_OK)
129ba9bdd8bSchristos errx(1, "fido_assert_set_extensions: %s",
130ba9bdd8bSchristos fido_strerr(r));
131ba9bdd8bSchristos if ((r = fido_assert_set_hmac_salt(assert, hmac_salt.ptr,
132ba9bdd8bSchristos hmac_salt.len)) != FIDO_OK)
133ba9bdd8bSchristos errx(1, "fido_assert_set_hmac_salt: %s",
134ba9bdd8bSchristos fido_strerr(r));
135ba9bdd8bSchristos }
13695dbdf32Schristos if (flags & FLAG_LARGEBLOB) {
13795dbdf32Schristos if ((r = fido_assert_set_extensions(assert,
13895dbdf32Schristos FIDO_EXT_LARGEBLOB_KEY)) != FIDO_OK)
13995dbdf32Schristos errx(1, "fido_assert_set_extensions: %s", fido_strerr(r));
14095dbdf32Schristos }
141ba9bdd8bSchristos if ((flags & FLAG_RK) == 0) {
142ba9bdd8bSchristos if ((r = fido_assert_allow_cred(assert, id.ptr,
143ba9bdd8bSchristos id.len)) != FIDO_OK)
144ba9bdd8bSchristos errx(1, "fido_assert_allow_cred: %s", fido_strerr(r));
145ba9bdd8bSchristos }
146ba9bdd8bSchristos
147ba9bdd8bSchristos free(hmac_salt.ptr);
148ba9bdd8bSchristos free(cdh.ptr);
149ba9bdd8bSchristos free(id.ptr);
150ba9bdd8bSchristos free(rpid);
151ba9bdd8bSchristos
152ba9bdd8bSchristos return (assert);
153ba9bdd8bSchristos }
154ba9bdd8bSchristos
155ba9bdd8bSchristos static void
print_assert(FILE * out_f,const fido_assert_t * assert,size_t idx,int flags)156ba9bdd8bSchristos print_assert(FILE *out_f, const fido_assert_t *assert, size_t idx, int flags)
157ba9bdd8bSchristos {
158ba9bdd8bSchristos char *cdh = NULL;
159ba9bdd8bSchristos char *authdata = NULL;
160ba9bdd8bSchristos char *sig = NULL;
161ba9bdd8bSchristos char *user_id = NULL;
162ba9bdd8bSchristos char *hmac_secret = NULL;
16395dbdf32Schristos char *key = NULL;
164ba9bdd8bSchristos int r;
165ba9bdd8bSchristos
166ba9bdd8bSchristos r = base64_encode(fido_assert_clientdata_hash_ptr(assert),
167ba9bdd8bSchristos fido_assert_clientdata_hash_len(assert), &cdh);
168ba9bdd8bSchristos r |= base64_encode(fido_assert_authdata_ptr(assert, idx),
169ba9bdd8bSchristos fido_assert_authdata_len(assert, 0), &authdata);
170ba9bdd8bSchristos r |= base64_encode(fido_assert_sig_ptr(assert, idx),
171ba9bdd8bSchristos fido_assert_sig_len(assert, idx), &sig);
172ba9bdd8bSchristos if (flags & FLAG_RK)
173ba9bdd8bSchristos r |= base64_encode(fido_assert_user_id_ptr(assert, idx),
174ba9bdd8bSchristos fido_assert_user_id_len(assert, idx), &user_id);
175ba9bdd8bSchristos if (flags & FLAG_HMAC)
176ba9bdd8bSchristos r |= base64_encode(fido_assert_hmac_secret_ptr(assert, idx),
177ba9bdd8bSchristos fido_assert_hmac_secret_len(assert, idx), &hmac_secret);
17895dbdf32Schristos if (flags & FLAG_LARGEBLOB)
17995dbdf32Schristos r |= base64_encode(fido_assert_largeblob_key_ptr(assert, idx),
18095dbdf32Schristos fido_assert_largeblob_key_len(assert, idx), &key);
181ba9bdd8bSchristos if (r < 0)
182ba9bdd8bSchristos errx(1, "output error");
183ba9bdd8bSchristos
184ba9bdd8bSchristos fprintf(out_f, "%s\n", cdh);
185ba9bdd8bSchristos fprintf(out_f, "%s\n", fido_assert_rp_id(assert));
186ba9bdd8bSchristos fprintf(out_f, "%s\n", authdata);
187ba9bdd8bSchristos fprintf(out_f, "%s\n", sig);
188ba9bdd8bSchristos if (flags & FLAG_RK)
189ba9bdd8bSchristos fprintf(out_f, "%s\n", user_id);
190ba9bdd8bSchristos if (hmac_secret) {
191ba9bdd8bSchristos fprintf(out_f, "%s\n", hmac_secret);
192ba9bdd8bSchristos explicit_bzero(hmac_secret, strlen(hmac_secret));
193ba9bdd8bSchristos }
19495dbdf32Schristos if (key) {
19595dbdf32Schristos fprintf(out_f, "%s\n", key);
19695dbdf32Schristos explicit_bzero(key, strlen(key));
19795dbdf32Schristos }
198ba9bdd8bSchristos
19995dbdf32Schristos free(key);
200ba9bdd8bSchristos free(hmac_secret);
201ba9bdd8bSchristos free(cdh);
202ba9bdd8bSchristos free(authdata);
203ba9bdd8bSchristos free(sig);
204ba9bdd8bSchristos free(user_id);
205ba9bdd8bSchristos }
206ba9bdd8bSchristos
207ba9bdd8bSchristos int
assert_get(int argc,char ** argv)208ba9bdd8bSchristos assert_get(int argc, char **argv)
209ba9bdd8bSchristos {
210ba9bdd8bSchristos fido_dev_t *dev = NULL;
211ba9bdd8bSchristos fido_assert_t *assert = NULL;
2121fc1e710Schristos struct toggle opt;
213ba9bdd8bSchristos char prompt[1024];
214*2d40c451Schristos char pin[128];
215ba9bdd8bSchristos char *in_path = NULL;
216ba9bdd8bSchristos char *out_path = NULL;
217ba9bdd8bSchristos FILE *in_f = NULL;
218ba9bdd8bSchristos FILE *out_f = NULL;
219ba9bdd8bSchristos int flags = 0;
220ba9bdd8bSchristos int ch;
221ba9bdd8bSchristos int r;
222ba9bdd8bSchristos
2231fc1e710Schristos opt.up = opt.uv = opt.pin = FIDO_OPT_OMIT;
2241fc1e710Schristos
22595dbdf32Schristos while ((ch = getopt(argc, argv, "bdhi:o:prt:uv")) != -1) {
226ba9bdd8bSchristos switch (ch) {
22795dbdf32Schristos case 'b':
22895dbdf32Schristos flags |= FLAG_LARGEBLOB;
22995dbdf32Schristos break;
230ba9bdd8bSchristos case 'd':
231ba9bdd8bSchristos flags |= FLAG_DEBUG;
232ba9bdd8bSchristos break;
233ba9bdd8bSchristos case 'h':
234ba9bdd8bSchristos flags |= FLAG_HMAC;
235ba9bdd8bSchristos break;
236ba9bdd8bSchristos case 'i':
237ba9bdd8bSchristos in_path = optarg;
238ba9bdd8bSchristos break;
239ba9bdd8bSchristos case 'o':
240ba9bdd8bSchristos out_path = optarg;
241ba9bdd8bSchristos break;
242ba9bdd8bSchristos case 'p':
2431fc1e710Schristos opt.up = FIDO_OPT_TRUE;
244ba9bdd8bSchristos break;
245ba9bdd8bSchristos case 'r':
246ba9bdd8bSchristos flags |= FLAG_RK;
247ba9bdd8bSchristos break;
2481fc1e710Schristos case 't' :
2491fc1e710Schristos parse_toggle(optarg, &opt);
2501fc1e710Schristos break;
251ba9bdd8bSchristos case 'u':
252ba9bdd8bSchristos flags |= FLAG_U2F;
253ba9bdd8bSchristos break;
254ba9bdd8bSchristos case 'v':
2551fc1e710Schristos /* -v implies both pin and uv for historical reasons */
2561fc1e710Schristos opt.pin = FIDO_OPT_TRUE;
2571fc1e710Schristos opt.uv = FIDO_OPT_TRUE;
258ba9bdd8bSchristos break;
259ba9bdd8bSchristos default:
260ba9bdd8bSchristos usage();
261ba9bdd8bSchristos }
262ba9bdd8bSchristos }
263ba9bdd8bSchristos
264ba9bdd8bSchristos argc -= optind;
265ba9bdd8bSchristos argv += optind;
266ba9bdd8bSchristos
267ba9bdd8bSchristos if (argc < 1)
268ba9bdd8bSchristos usage();
269ba9bdd8bSchristos
270ba9bdd8bSchristos in_f = open_read(in_path);
271ba9bdd8bSchristos out_f = open_write(out_path);
272ba9bdd8bSchristos
273ba9bdd8bSchristos fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0);
274ba9bdd8bSchristos
2751fc1e710Schristos assert = prepare_assert(in_f, flags, &opt);
276ba9bdd8bSchristos
277ba9bdd8bSchristos dev = open_dev(argv[0]);
278ba9bdd8bSchristos if (flags & FLAG_U2F)
279ba9bdd8bSchristos fido_dev_force_u2f(dev);
280ba9bdd8bSchristos
2811fc1e710Schristos if (opt.pin == FIDO_OPT_TRUE) {
282ba9bdd8bSchristos r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ",
283ba9bdd8bSchristos argv[0]);
284ba9bdd8bSchristos if (r < 0 || (size_t)r >= sizeof(prompt))
285ba9bdd8bSchristos errx(1, "snprintf");
286ba9bdd8bSchristos if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF))
287ba9bdd8bSchristos errx(1, "readpassphrase");
288*2d40c451Schristos if (strlen(pin) < 4 || strlen(pin) > 63) {
289*2d40c451Schristos explicit_bzero(pin, sizeof(pin));
290*2d40c451Schristos errx(1, "invalid PIN length");
291*2d40c451Schristos }
292ba9bdd8bSchristos r = fido_dev_get_assert(dev, assert, pin);
293ba9bdd8bSchristos } else
294ba9bdd8bSchristos r = fido_dev_get_assert(dev, assert, NULL);
295ba9bdd8bSchristos
296ba9bdd8bSchristos explicit_bzero(pin, sizeof(pin));
297ba9bdd8bSchristos
298ba9bdd8bSchristos if (r != FIDO_OK)
299ba9bdd8bSchristos errx(1, "fido_dev_get_assert: %s", fido_strerr(r));
300ba9bdd8bSchristos
301ba9bdd8bSchristos if (flags & FLAG_RK) {
302ba9bdd8bSchristos for (size_t idx = 0; idx < fido_assert_count(assert); idx++)
303ba9bdd8bSchristos print_assert(out_f, assert, idx, flags);
304ba9bdd8bSchristos } else {
305ba9bdd8bSchristos if (fido_assert_count(assert) != 1)
306ba9bdd8bSchristos errx(1, "fido_assert_count: %zu",
307ba9bdd8bSchristos fido_assert_count(assert));
308ba9bdd8bSchristos print_assert(out_f, assert, 0, flags);
309ba9bdd8bSchristos }
310ba9bdd8bSchristos
311ba9bdd8bSchristos fido_dev_close(dev);
312ba9bdd8bSchristos fido_dev_free(&dev);
313ba9bdd8bSchristos fido_assert_free(&assert);
314ba9bdd8bSchristos
315ba9bdd8bSchristos fclose(in_f);
316ba9bdd8bSchristos fclose(out_f);
317ba9bdd8bSchristos in_f = NULL;
318ba9bdd8bSchristos out_f = NULL;
319ba9bdd8bSchristos
320ba9bdd8bSchristos exit(0);
321ba9bdd8bSchristos }
322