1ba9bdd8bSchristos /*
2*2d40c451Schristos * Copyright (c) 2018-2022 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
895dbdf32Schristos #include <fido.h>
995dbdf32Schristos #include <fido/es256.h>
10*2d40c451Schristos #include <fido/es384.h>
1195dbdf32Schristos #include <fido/rs256.h>
1295dbdf32Schristos #include <fido/eddsa.h>
13ba9bdd8bSchristos
14ba9bdd8bSchristos #include <stdbool.h>
15ba9bdd8bSchristos #include <stdio.h>
16ba9bdd8bSchristos #include <stdlib.h>
17ba9bdd8bSchristos #include <string.h>
18ba9bdd8bSchristos #ifdef HAVE_UNISTD_H
19ba9bdd8bSchristos #include <unistd.h>
20ba9bdd8bSchristos #endif
21ba9bdd8bSchristos
221fc1e710Schristos #include "../openbsd-compat/openbsd-compat.h"
2395dbdf32Schristos #include "extern.h"
24ba9bdd8bSchristos
25*2d40c451Schristos static const unsigned char cd[32] = {
26ba9bdd8bSchristos 0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7,
27ba9bdd8bSchristos 0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56,
28ba9bdd8bSchristos 0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52,
29ba9bdd8bSchristos 0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76,
30ba9bdd8bSchristos };
31ba9bdd8bSchristos
32ba9bdd8bSchristos static void
usage(void)33ba9bdd8bSchristos usage(void)
34ba9bdd8bSchristos {
35*2d40c451Schristos fprintf(stderr, "usage: assert [-t es256|es384|rs256|eddsa] "
36*2d40c451Schristos "[-a cred_id] [-h hmac_secret] [-s hmac_salt] [-P pin] "
37*2d40c451Schristos "[-T seconds] [-b blobkey] [-puv] <pubkey> <device>\n");
38ba9bdd8bSchristos exit(EXIT_FAILURE);
39ba9bdd8bSchristos }
40ba9bdd8bSchristos
41ba9bdd8bSchristos static void
verify_assert(int type,const unsigned char * authdata_ptr,size_t authdata_len,const unsigned char * sig_ptr,size_t sig_len,bool up,bool uv,int ext,const char * key)42ba9bdd8bSchristos verify_assert(int type, const unsigned char *authdata_ptr, size_t authdata_len,
43ba9bdd8bSchristos const unsigned char *sig_ptr, size_t sig_len, bool up, bool uv, int ext,
44ba9bdd8bSchristos const char *key)
45ba9bdd8bSchristos {
46ba9bdd8bSchristos fido_assert_t *assert = NULL;
47ba9bdd8bSchristos EC_KEY *ec = NULL;
48ba9bdd8bSchristos RSA *rsa = NULL;
49ba9bdd8bSchristos EVP_PKEY *eddsa = NULL;
50ba9bdd8bSchristos es256_pk_t *es256_pk = NULL;
51*2d40c451Schristos es384_pk_t *es384_pk = NULL;
52ba9bdd8bSchristos rs256_pk_t *rs256_pk = NULL;
53ba9bdd8bSchristos eddsa_pk_t *eddsa_pk = NULL;
54ba9bdd8bSchristos void *pk;
55ba9bdd8bSchristos int r;
56ba9bdd8bSchristos
57ba9bdd8bSchristos /* credential pubkey */
58ba9bdd8bSchristos switch (type) {
59ba9bdd8bSchristos case COSE_ES256:
60ba9bdd8bSchristos if ((ec = read_ec_pubkey(key)) == NULL)
61ba9bdd8bSchristos errx(1, "read_ec_pubkey");
62ba9bdd8bSchristos
63ba9bdd8bSchristos if ((es256_pk = es256_pk_new()) == NULL)
64ba9bdd8bSchristos errx(1, "es256_pk_new");
65ba9bdd8bSchristos
66ba9bdd8bSchristos if (es256_pk_from_EC_KEY(es256_pk, ec) != FIDO_OK)
67ba9bdd8bSchristos errx(1, "es256_pk_from_EC_KEY");
68ba9bdd8bSchristos
69ba9bdd8bSchristos pk = es256_pk;
70ba9bdd8bSchristos EC_KEY_free(ec);
71ba9bdd8bSchristos ec = NULL;
72ba9bdd8bSchristos
73ba9bdd8bSchristos break;
74*2d40c451Schristos case COSE_ES384:
75*2d40c451Schristos if ((ec = read_ec_pubkey(key)) == NULL)
76*2d40c451Schristos errx(1, "read_ec_pubkey");
77*2d40c451Schristos
78*2d40c451Schristos if ((es384_pk = es384_pk_new()) == NULL)
79*2d40c451Schristos errx(1, "es384_pk_new");
80*2d40c451Schristos
81*2d40c451Schristos if (es384_pk_from_EC_KEY(es384_pk, ec) != FIDO_OK)
82*2d40c451Schristos errx(1, "es384_pk_from_EC_KEY");
83*2d40c451Schristos
84*2d40c451Schristos pk = es384_pk;
85*2d40c451Schristos EC_KEY_free(ec);
86*2d40c451Schristos ec = NULL;
87*2d40c451Schristos
88*2d40c451Schristos break;
89ba9bdd8bSchristos case COSE_RS256:
90ba9bdd8bSchristos if ((rsa = read_rsa_pubkey(key)) == NULL)
91ba9bdd8bSchristos errx(1, "read_rsa_pubkey");
92ba9bdd8bSchristos
93ba9bdd8bSchristos if ((rs256_pk = rs256_pk_new()) == NULL)
94ba9bdd8bSchristos errx(1, "rs256_pk_new");
95ba9bdd8bSchristos
96ba9bdd8bSchristos if (rs256_pk_from_RSA(rs256_pk, rsa) != FIDO_OK)
97ba9bdd8bSchristos errx(1, "rs256_pk_from_RSA");
98ba9bdd8bSchristos
99ba9bdd8bSchristos pk = rs256_pk;
100ba9bdd8bSchristos RSA_free(rsa);
101ba9bdd8bSchristos rsa = NULL;
102ba9bdd8bSchristos
103ba9bdd8bSchristos break;
104ba9bdd8bSchristos case COSE_EDDSA:
105ba9bdd8bSchristos if ((eddsa = read_eddsa_pubkey(key)) == NULL)
106ba9bdd8bSchristos errx(1, "read_eddsa_pubkey");
107ba9bdd8bSchristos
108ba9bdd8bSchristos if ((eddsa_pk = eddsa_pk_new()) == NULL)
109ba9bdd8bSchristos errx(1, "eddsa_pk_new");
110ba9bdd8bSchristos
111ba9bdd8bSchristos if (eddsa_pk_from_EVP_PKEY(eddsa_pk, eddsa) != FIDO_OK)
112ba9bdd8bSchristos errx(1, "eddsa_pk_from_EVP_PKEY");
113ba9bdd8bSchristos
114ba9bdd8bSchristos pk = eddsa_pk;
115ba9bdd8bSchristos EVP_PKEY_free(eddsa);
116ba9bdd8bSchristos eddsa = NULL;
117ba9bdd8bSchristos
118ba9bdd8bSchristos break;
119ba9bdd8bSchristos default:
120ba9bdd8bSchristos errx(1, "unknown credential type %d", type);
121ba9bdd8bSchristos }
122ba9bdd8bSchristos
123ba9bdd8bSchristos if ((assert = fido_assert_new()) == NULL)
124ba9bdd8bSchristos errx(1, "fido_assert_new");
125ba9bdd8bSchristos
126ba9bdd8bSchristos /* client data hash */
127*2d40c451Schristos r = fido_assert_set_clientdata(assert, cd, sizeof(cd));
128ba9bdd8bSchristos if (r != FIDO_OK)
129*2d40c451Schristos errx(1, "fido_assert_set_clientdata: %s (0x%x)", fido_strerr(r), r);
130ba9bdd8bSchristos
131ba9bdd8bSchristos /* relying party */
132ba9bdd8bSchristos r = fido_assert_set_rp(assert, "localhost");
133ba9bdd8bSchristos if (r != FIDO_OK)
134ba9bdd8bSchristos errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r);
135ba9bdd8bSchristos
136ba9bdd8bSchristos /* authdata */
137ba9bdd8bSchristos r = fido_assert_set_count(assert, 1);
138ba9bdd8bSchristos if (r != FIDO_OK)
139ba9bdd8bSchristos errx(1, "fido_assert_set_count: %s (0x%x)", fido_strerr(r), r);
140ba9bdd8bSchristos r = fido_assert_set_authdata(assert, 0, authdata_ptr, authdata_len);
141ba9bdd8bSchristos if (r != FIDO_OK)
142ba9bdd8bSchristos errx(1, "fido_assert_set_authdata: %s (0x%x)", fido_strerr(r), r);
143ba9bdd8bSchristos
144ba9bdd8bSchristos /* extension */
145ba9bdd8bSchristos r = fido_assert_set_extensions(assert, ext);
146ba9bdd8bSchristos if (r != FIDO_OK)
147ba9bdd8bSchristos errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r),
148ba9bdd8bSchristos r);
149ba9bdd8bSchristos
150ba9bdd8bSchristos /* user presence */
151ba9bdd8bSchristos if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
152ba9bdd8bSchristos errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r);
153ba9bdd8bSchristos
154ba9bdd8bSchristos /* user verification */
155ba9bdd8bSchristos if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
156ba9bdd8bSchristos errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r);
157ba9bdd8bSchristos
158ba9bdd8bSchristos /* sig */
159ba9bdd8bSchristos r = fido_assert_set_sig(assert, 0, sig_ptr, sig_len);
160ba9bdd8bSchristos if (r != FIDO_OK)
161ba9bdd8bSchristos errx(1, "fido_assert_set_sig: %s (0x%x)", fido_strerr(r), r);
162ba9bdd8bSchristos
163ba9bdd8bSchristos r = fido_assert_verify(assert, 0, type, pk);
164ba9bdd8bSchristos if (r != FIDO_OK)
165ba9bdd8bSchristos errx(1, "fido_assert_verify: %s (0x%x)", fido_strerr(r), r);
166ba9bdd8bSchristos
167ba9bdd8bSchristos es256_pk_free(&es256_pk);
168*2d40c451Schristos es384_pk_free(&es384_pk);
169ba9bdd8bSchristos rs256_pk_free(&rs256_pk);
170ba9bdd8bSchristos eddsa_pk_free(&eddsa_pk);
171ba9bdd8bSchristos
172ba9bdd8bSchristos fido_assert_free(&assert);
173ba9bdd8bSchristos }
174ba9bdd8bSchristos
175ba9bdd8bSchristos int
main(int argc,char ** argv)176ba9bdd8bSchristos main(int argc, char **argv)
177ba9bdd8bSchristos {
178ba9bdd8bSchristos bool up = false;
179ba9bdd8bSchristos bool uv = false;
180ba9bdd8bSchristos bool u2f = false;
181ba9bdd8bSchristos fido_dev_t *dev = NULL;
182ba9bdd8bSchristos fido_assert_t *assert = NULL;
183ba9bdd8bSchristos const char *pin = NULL;
18495dbdf32Schristos const char *blobkey_out = NULL;
185ba9bdd8bSchristos const char *hmac_out = NULL;
186ba9bdd8bSchristos unsigned char *body = NULL;
187*2d40c451Schristos long long ms = 0;
188ba9bdd8bSchristos size_t len;
189ba9bdd8bSchristos int type = COSE_ES256;
190ba9bdd8bSchristos int ext = 0;
191ba9bdd8bSchristos int ch;
192ba9bdd8bSchristos int r;
193ba9bdd8bSchristos
194ba9bdd8bSchristos if ((assert = fido_assert_new()) == NULL)
195ba9bdd8bSchristos errx(1, "fido_assert_new");
196ba9bdd8bSchristos
19795dbdf32Schristos while ((ch = getopt(argc, argv, "P:T:a:b:h:ps:t:uv")) != -1) {
198ba9bdd8bSchristos switch (ch) {
199ba9bdd8bSchristos case 'P':
200ba9bdd8bSchristos pin = optarg;
201ba9bdd8bSchristos break;
202ba9bdd8bSchristos case 'T':
203*2d40c451Schristos if (base10(optarg, &ms) < 0)
204ba9bdd8bSchristos errx(1, "base10: %s", optarg);
205*2d40c451Schristos if (ms <= 0 || ms > 30)
206ba9bdd8bSchristos errx(1, "-T: %s must be in (0,30]", optarg);
207*2d40c451Schristos ms *= 1000; /* seconds to milliseconds */
208ba9bdd8bSchristos break;
209ba9bdd8bSchristos case 'a':
210ba9bdd8bSchristos if (read_blob(optarg, &body, &len) < 0)
211ba9bdd8bSchristos errx(1, "read_blob: %s", optarg);
212ba9bdd8bSchristos if ((r = fido_assert_allow_cred(assert, body,
213ba9bdd8bSchristos len)) != FIDO_OK)
214ba9bdd8bSchristos errx(1, "fido_assert_allow_cred: %s (0x%x)",
215ba9bdd8bSchristos fido_strerr(r), r);
216ba9bdd8bSchristos free(body);
217ba9bdd8bSchristos body = NULL;
218ba9bdd8bSchristos break;
21995dbdf32Schristos case 'b':
22095dbdf32Schristos ext |= FIDO_EXT_LARGEBLOB_KEY;
22195dbdf32Schristos blobkey_out = optarg;
22295dbdf32Schristos break;
223ba9bdd8bSchristos case 'h':
224ba9bdd8bSchristos hmac_out = optarg;
225ba9bdd8bSchristos break;
226ba9bdd8bSchristos case 'p':
227ba9bdd8bSchristos up = true;
228ba9bdd8bSchristos break;
229ba9bdd8bSchristos case 's':
23095dbdf32Schristos ext |= FIDO_EXT_HMAC_SECRET;
231ba9bdd8bSchristos if (read_blob(optarg, &body, &len) < 0)
232ba9bdd8bSchristos errx(1, "read_blob: %s", optarg);
233ba9bdd8bSchristos if ((r = fido_assert_set_hmac_salt(assert, body,
234ba9bdd8bSchristos len)) != FIDO_OK)
235ba9bdd8bSchristos errx(1, "fido_assert_set_hmac_salt: %s (0x%x)",
236ba9bdd8bSchristos fido_strerr(r), r);
237ba9bdd8bSchristos free(body);
238ba9bdd8bSchristos body = NULL;
239ba9bdd8bSchristos break;
240ba9bdd8bSchristos case 't':
241*2d40c451Schristos if (strcmp(optarg, "es256") == 0)
242ba9bdd8bSchristos type = COSE_ES256;
243*2d40c451Schristos else if (strcmp(optarg, "es384") == 0)
244*2d40c451Schristos type = COSE_ES384;
245*2d40c451Schristos else if (strcmp(optarg, "rs256") == 0)
246ba9bdd8bSchristos type = COSE_RS256;
247ba9bdd8bSchristos else if (strcmp(optarg, "eddsa") == 0)
248ba9bdd8bSchristos type = COSE_EDDSA;
249ba9bdd8bSchristos else
250ba9bdd8bSchristos errx(1, "unknown type %s", optarg);
251ba9bdd8bSchristos break;
252ba9bdd8bSchristos case 'u':
253ba9bdd8bSchristos u2f = true;
254ba9bdd8bSchristos break;
255ba9bdd8bSchristos case 'v':
256ba9bdd8bSchristos uv = true;
257ba9bdd8bSchristos break;
258ba9bdd8bSchristos default:
259ba9bdd8bSchristos usage();
260ba9bdd8bSchristos }
261ba9bdd8bSchristos }
262ba9bdd8bSchristos
263ba9bdd8bSchristos argc -= optind;
264ba9bdd8bSchristos argv += optind;
265ba9bdd8bSchristos
266ba9bdd8bSchristos if (argc != 2)
267ba9bdd8bSchristos usage();
268ba9bdd8bSchristos
269ba9bdd8bSchristos fido_init(0);
270ba9bdd8bSchristos
271ba9bdd8bSchristos if ((dev = fido_dev_new()) == NULL)
272ba9bdd8bSchristos errx(1, "fido_dev_new");
273ba9bdd8bSchristos
274ba9bdd8bSchristos r = fido_dev_open(dev, argv[1]);
275ba9bdd8bSchristos if (r != FIDO_OK)
276ba9bdd8bSchristos errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
277ba9bdd8bSchristos if (u2f)
278ba9bdd8bSchristos fido_dev_force_u2f(dev);
279ba9bdd8bSchristos
280ba9bdd8bSchristos /* client data hash */
281*2d40c451Schristos r = fido_assert_set_clientdata(assert, cd, sizeof(cd));
282ba9bdd8bSchristos if (r != FIDO_OK)
283*2d40c451Schristos errx(1, "fido_assert_set_clientdata: %s (0x%x)", fido_strerr(r), r);
284ba9bdd8bSchristos
285ba9bdd8bSchristos /* relying party */
286ba9bdd8bSchristos r = fido_assert_set_rp(assert, "localhost");
287ba9bdd8bSchristos if (r != FIDO_OK)
288ba9bdd8bSchristos errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r);
289ba9bdd8bSchristos
290ba9bdd8bSchristos /* extensions */
291ba9bdd8bSchristos r = fido_assert_set_extensions(assert, ext);
292ba9bdd8bSchristos if (r != FIDO_OK)
293ba9bdd8bSchristos errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r),
294ba9bdd8bSchristos r);
295ba9bdd8bSchristos
296ba9bdd8bSchristos /* user presence */
297ba9bdd8bSchristos if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
298ba9bdd8bSchristos errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r);
299ba9bdd8bSchristos
300ba9bdd8bSchristos /* user verification */
301ba9bdd8bSchristos if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
302ba9bdd8bSchristos errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r);
303ba9bdd8bSchristos
304*2d40c451Schristos /* timeout */
305*2d40c451Schristos if (ms != 0 && (r = fido_dev_set_timeout(dev, (int)ms)) != FIDO_OK)
306*2d40c451Schristos errx(1, "fido_dev_set_timeout: %s (0x%x)", fido_strerr(r), r);
307ba9bdd8bSchristos
308*2d40c451Schristos if ((r = fido_dev_get_assert(dev, assert, pin)) != FIDO_OK) {
309ba9bdd8bSchristos fido_dev_cancel(dev);
310ba9bdd8bSchristos errx(1, "fido_dev_get_assert: %s (0x%x)", fido_strerr(r), r);
311ba9bdd8bSchristos }
312ba9bdd8bSchristos
313ba9bdd8bSchristos r = fido_dev_close(dev);
314ba9bdd8bSchristos if (r != FIDO_OK)
315ba9bdd8bSchristos errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
316ba9bdd8bSchristos
317ba9bdd8bSchristos fido_dev_free(&dev);
318ba9bdd8bSchristos
319ba9bdd8bSchristos if (fido_assert_count(assert) != 1)
320ba9bdd8bSchristos errx(1, "fido_assert_count: %d signatures returned",
321ba9bdd8bSchristos (int)fido_assert_count(assert));
322ba9bdd8bSchristos
3231fc1e710Schristos /* when verifying, pin implies uv */
3241fc1e710Schristos if (pin)
3251fc1e710Schristos uv = true;
3261fc1e710Schristos
327ba9bdd8bSchristos verify_assert(type, fido_assert_authdata_ptr(assert, 0),
328ba9bdd8bSchristos fido_assert_authdata_len(assert, 0), fido_assert_sig_ptr(assert, 0),
329ba9bdd8bSchristos fido_assert_sig_len(assert, 0), up, uv, ext, argv[0]);
330ba9bdd8bSchristos
331ba9bdd8bSchristos if (hmac_out != NULL) {
332ba9bdd8bSchristos /* extract the hmac secret */
333ba9bdd8bSchristos if (write_blob(hmac_out, fido_assert_hmac_secret_ptr(assert, 0),
334ba9bdd8bSchristos fido_assert_hmac_secret_len(assert, 0)) < 0)
335ba9bdd8bSchristos errx(1, "write_blob");
336ba9bdd8bSchristos }
337ba9bdd8bSchristos
33895dbdf32Schristos if (blobkey_out != NULL) {
33995dbdf32Schristos /* extract the hmac secret */
34095dbdf32Schristos if (write_blob(blobkey_out,
34195dbdf32Schristos fido_assert_largeblob_key_ptr(assert, 0),
34295dbdf32Schristos fido_assert_largeblob_key_len(assert, 0)) < 0)
34395dbdf32Schristos errx(1, "write_blob");
34495dbdf32Schristos }
34595dbdf32Schristos
346ba9bdd8bSchristos fido_assert_free(&assert);
347ba9bdd8bSchristos
348ba9bdd8bSchristos exit(0);
349ba9bdd8bSchristos }
350