1*535af610SEd Maste /* $OpenBSD: sk-usbhid.c,v 1.46 2023/03/28 06:12:38 dtucker Exp $ */
219261079SEd Maste /*
319261079SEd Maste * Copyright (c) 2019 Markus Friedl
419261079SEd Maste * Copyright (c) 2020 Pedro Martelletto
519261079SEd Maste *
619261079SEd Maste * Permission to use, copy, modify, and distribute this software for any
719261079SEd Maste * purpose with or without fee is hereby granted, provided that the above
819261079SEd Maste * copyright notice and this permission notice appear in all copies.
919261079SEd Maste *
1019261079SEd Maste * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1119261079SEd Maste * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1219261079SEd Maste * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1319261079SEd Maste * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1419261079SEd Maste * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1519261079SEd Maste * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1619261079SEd Maste * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1719261079SEd Maste */
1819261079SEd Maste
1919261079SEd Maste #include "includes.h"
2019261079SEd Maste
2119261079SEd Maste #ifdef ENABLE_SK_INTERNAL
2219261079SEd Maste
23*535af610SEd Maste #ifdef HAVE_STDINT_H
2419261079SEd Maste # include <stdint.h>
25*535af610SEd Maste #endif
2619261079SEd Maste #include <stdlib.h>
2719261079SEd Maste #include <string.h>
2819261079SEd Maste #include <stdio.h>
2919261079SEd Maste #include <stddef.h>
3019261079SEd Maste #include <stdarg.h>
3119261079SEd Maste #include <time.h>
3219261079SEd Maste #ifdef HAVE_SHA2_H
3319261079SEd Maste #include <sha2.h>
3419261079SEd Maste #endif
3519261079SEd Maste
361323ec57SEd Maste /*
371323ec57SEd Maste * Almost every use of OpenSSL in this file is for ECDSA-NISTP256.
381323ec57SEd Maste * This is strictly a larger hammer than necessary, but it reduces changes
391323ec57SEd Maste * with upstream.
401323ec57SEd Maste */
411323ec57SEd Maste #ifndef OPENSSL_HAS_ECC
421323ec57SEd Maste # undef WITH_OPENSSL
431323ec57SEd Maste #endif
441323ec57SEd Maste
4519261079SEd Maste #ifdef WITH_OPENSSL
4619261079SEd Maste #include <openssl/opensslv.h>
4719261079SEd Maste #include <openssl/crypto.h>
4819261079SEd Maste #include <openssl/bn.h>
4919261079SEd Maste #include <openssl/ec.h>
5019261079SEd Maste #include <openssl/ecdsa.h>
5119261079SEd Maste #include <openssl/evp.h>
5219261079SEd Maste #endif /* WITH_OPENSSL */
5319261079SEd Maste
5419261079SEd Maste #include <fido.h>
5519261079SEd Maste #include <fido/credman.h>
5619261079SEd Maste
5719261079SEd Maste /* backwards compat for libfido2 */
5819261079SEd Maste #ifndef HAVE_FIDO_CRED_PROT
5919261079SEd Maste #define fido_cred_prot(x) (0)
6019261079SEd Maste #endif
6119261079SEd Maste #ifndef HAVE_FIDO_CRED_SET_PROT
6219261079SEd Maste #define fido_cred_set_prot(x, y) (FIDO_ERR_UNSUPPORTED_OPTION)
6319261079SEd Maste #endif
6419261079SEd Maste #ifndef HAVE_FIDO_DEV_SUPPORTS_CRED_PROT
6519261079SEd Maste #define fido_dev_supports_cred_prot(x) (0)
6619261079SEd Maste #endif
6719261079SEd Maste #ifndef HAVE_FIDO_DEV_GET_TOUCH_BEGIN
6819261079SEd Maste #define fido_dev_get_touch_begin(x) (FIDO_ERR_UNSUPPORTED_OPTION)
6919261079SEd Maste #endif
7019261079SEd Maste #ifndef HAVE_FIDO_DEV_GET_TOUCH_STATUS
7119261079SEd Maste #define fido_dev_get_touch_status(x, y, z) (FIDO_ERR_UNSUPPORTED_OPTION)
7219261079SEd Maste #endif
7319261079SEd Maste #ifndef FIDO_CRED_PROT_UV_REQUIRED
7419261079SEd Maste #define FIDO_CRED_PROT_UV_REQUIRED 0
7519261079SEd Maste #endif
7619261079SEd Maste #ifndef FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID
7719261079SEd Maste #define FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID 0
7819261079SEd Maste #endif
7919261079SEd Maste
8019261079SEd Maste #ifndef SK_STANDALONE
8119261079SEd Maste # include "log.h"
8219261079SEd Maste # include "xmalloc.h"
8319261079SEd Maste # include "misc.h"
8419261079SEd Maste /*
8519261079SEd Maste * If building as part of OpenSSH, then rename exported functions.
8619261079SEd Maste * This must be done before including sk-api.h.
8719261079SEd Maste */
8819261079SEd Maste # define sk_api_version ssh_sk_api_version
8919261079SEd Maste # define sk_enroll ssh_sk_enroll
9019261079SEd Maste # define sk_sign ssh_sk_sign
9119261079SEd Maste # define sk_load_resident_keys ssh_sk_load_resident_keys
9219261079SEd Maste #endif /* !SK_STANDALONE */
9319261079SEd Maste
9419261079SEd Maste #include "sk-api.h"
9519261079SEd Maste
9619261079SEd Maste /* #define SK_DEBUG 1 */
9719261079SEd Maste
9819261079SEd Maste #ifdef SK_DEBUG
9919261079SEd Maste #define SSH_FIDO_INIT_ARG FIDO_DEBUG
10019261079SEd Maste #else
10119261079SEd Maste #define SSH_FIDO_INIT_ARG 0
10219261079SEd Maste #endif
10319261079SEd Maste
10419261079SEd Maste #define MAX_FIDO_DEVICES 8
10519261079SEd Maste #define FIDO_POLL_MS 50
10619261079SEd Maste #define SELECT_MS 15000
10719261079SEd Maste #define POLL_SLEEP_NS 200000000
10819261079SEd Maste
1091323ec57SEd Maste #ifndef FIDO_ERR_OPERATION_DENIED
1101323ec57SEd Maste #define FIDO_ERR_OPERATION_DENIED 0x27
1111323ec57SEd Maste #endif
11219261079SEd Maste
11319261079SEd Maste struct sk_usbhid {
11419261079SEd Maste fido_dev_t *dev;
11519261079SEd Maste char *path;
11619261079SEd Maste };
11719261079SEd Maste
11819261079SEd Maste /* Return the version of the middleware API */
11919261079SEd Maste uint32_t sk_api_version(void);
12019261079SEd Maste
12119261079SEd Maste /* Enroll a U2F key (private key generation) */
12219261079SEd Maste int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
12319261079SEd Maste const char *application, uint8_t flags, const char *pin,
12419261079SEd Maste struct sk_option **options, struct sk_enroll_response **enroll_response);
12519261079SEd Maste
12619261079SEd Maste /* Sign a challenge */
12719261079SEd Maste int sk_sign(uint32_t alg, const uint8_t *data, size_t data_len,
12819261079SEd Maste const char *application, const uint8_t *key_handle, size_t key_handle_len,
12919261079SEd Maste uint8_t flags, const char *pin, struct sk_option **options,
13019261079SEd Maste struct sk_sign_response **sign_response);
13119261079SEd Maste
13219261079SEd Maste /* Load resident keys */
13319261079SEd Maste int sk_load_resident_keys(const char *pin, struct sk_option **options,
13419261079SEd Maste struct sk_resident_key ***rks, size_t *nrks);
13519261079SEd Maste
13619261079SEd Maste static void skdebug(const char *func, const char *fmt, ...)
13719261079SEd Maste __attribute__((__format__ (printf, 2, 3)));
13819261079SEd Maste
13919261079SEd Maste static void
skdebug(const char * func,const char * fmt,...)14019261079SEd Maste skdebug(const char *func, const char *fmt, ...)
14119261079SEd Maste {
14219261079SEd Maste #if !defined(SK_STANDALONE)
14319261079SEd Maste char *msg;
14419261079SEd Maste va_list ap;
14519261079SEd Maste
14619261079SEd Maste va_start(ap, fmt);
14719261079SEd Maste xvasprintf(&msg, fmt, ap);
14819261079SEd Maste va_end(ap);
14919261079SEd Maste debug("%s: %s", func, msg);
15019261079SEd Maste free(msg);
15119261079SEd Maste #elif defined(SK_DEBUG)
15219261079SEd Maste va_list ap;
15319261079SEd Maste
15419261079SEd Maste va_start(ap, fmt);
15519261079SEd Maste fprintf(stderr, "%s: ", func);
15619261079SEd Maste vfprintf(stderr, fmt, ap);
15719261079SEd Maste fputc('\n', stderr);
15819261079SEd Maste va_end(ap);
15919261079SEd Maste #else
16019261079SEd Maste (void)func; /* XXX */
16119261079SEd Maste (void)fmt; /* XXX */
16219261079SEd Maste #endif
16319261079SEd Maste }
16419261079SEd Maste
16519261079SEd Maste uint32_t
sk_api_version(void)16619261079SEd Maste sk_api_version(void)
16719261079SEd Maste {
16819261079SEd Maste return SSH_SK_VERSION_MAJOR;
16919261079SEd Maste }
17019261079SEd Maste
17119261079SEd Maste static struct sk_usbhid *
sk_open(const char * path)17219261079SEd Maste sk_open(const char *path)
17319261079SEd Maste {
17419261079SEd Maste struct sk_usbhid *sk;
17519261079SEd Maste int r;
17619261079SEd Maste
17719261079SEd Maste if (path == NULL) {
17819261079SEd Maste skdebug(__func__, "path == NULL");
17919261079SEd Maste return NULL;
18019261079SEd Maste }
18119261079SEd Maste if ((sk = calloc(1, sizeof(*sk))) == NULL) {
18219261079SEd Maste skdebug(__func__, "calloc sk failed");
18319261079SEd Maste return NULL;
18419261079SEd Maste }
18519261079SEd Maste if ((sk->path = strdup(path)) == NULL) {
18619261079SEd Maste skdebug(__func__, "strdup path failed");
18719261079SEd Maste free(sk);
18819261079SEd Maste return NULL;
18919261079SEd Maste }
19019261079SEd Maste if ((sk->dev = fido_dev_new()) == NULL) {
19119261079SEd Maste skdebug(__func__, "fido_dev_new failed");
19219261079SEd Maste free(sk->path);
19319261079SEd Maste free(sk);
19419261079SEd Maste return NULL;
19519261079SEd Maste }
19619261079SEd Maste if ((r = fido_dev_open(sk->dev, sk->path)) != FIDO_OK) {
19719261079SEd Maste skdebug(__func__, "fido_dev_open %s failed: %s", sk->path,
19819261079SEd Maste fido_strerr(r));
19919261079SEd Maste fido_dev_free(&sk->dev);
20019261079SEd Maste free(sk->path);
20119261079SEd Maste free(sk);
20219261079SEd Maste return NULL;
20319261079SEd Maste }
20419261079SEd Maste return sk;
20519261079SEd Maste }
20619261079SEd Maste
20719261079SEd Maste static void
sk_close(struct sk_usbhid * sk)20819261079SEd Maste sk_close(struct sk_usbhid *sk)
20919261079SEd Maste {
21019261079SEd Maste if (sk == NULL)
21119261079SEd Maste return;
21219261079SEd Maste fido_dev_cancel(sk->dev); /* cancel any pending operation */
21319261079SEd Maste fido_dev_close(sk->dev);
21419261079SEd Maste fido_dev_free(&sk->dev);
21519261079SEd Maste free(sk->path);
21619261079SEd Maste free(sk);
21719261079SEd Maste }
21819261079SEd Maste
21919261079SEd Maste static struct sk_usbhid **
sk_openv(const fido_dev_info_t * devlist,size_t ndevs,size_t * nopen)22019261079SEd Maste sk_openv(const fido_dev_info_t *devlist, size_t ndevs, size_t *nopen)
22119261079SEd Maste {
22219261079SEd Maste const fido_dev_info_t *di;
22319261079SEd Maste struct sk_usbhid **skv;
22419261079SEd Maste size_t i;
22519261079SEd Maste
22619261079SEd Maste *nopen = 0;
22719261079SEd Maste if ((skv = calloc(ndevs, sizeof(*skv))) == NULL) {
22819261079SEd Maste skdebug(__func__, "calloc skv failed");
22919261079SEd Maste return NULL;
23019261079SEd Maste }
23119261079SEd Maste for (i = 0; i < ndevs; i++) {
23219261079SEd Maste if ((di = fido_dev_info_ptr(devlist, i)) == NULL)
23319261079SEd Maste skdebug(__func__, "fido_dev_info_ptr failed");
23419261079SEd Maste else if ((skv[*nopen] = sk_open(fido_dev_info_path(di))) == NULL)
23519261079SEd Maste skdebug(__func__, "sk_open failed");
23619261079SEd Maste else
23719261079SEd Maste (*nopen)++;
23819261079SEd Maste }
23919261079SEd Maste if (*nopen == 0) {
24019261079SEd Maste for (i = 0; i < ndevs; i++)
24119261079SEd Maste sk_close(skv[i]);
24219261079SEd Maste free(skv);
24319261079SEd Maste skv = NULL;
24419261079SEd Maste }
24519261079SEd Maste
24619261079SEd Maste return skv;
24719261079SEd Maste }
24819261079SEd Maste
24919261079SEd Maste static void
sk_closev(struct sk_usbhid ** skv,size_t nsk)25019261079SEd Maste sk_closev(struct sk_usbhid **skv, size_t nsk)
25119261079SEd Maste {
25219261079SEd Maste size_t i;
25319261079SEd Maste
25419261079SEd Maste for (i = 0; i < nsk; i++)
25519261079SEd Maste sk_close(skv[i]);
25619261079SEd Maste free(skv);
25719261079SEd Maste }
25819261079SEd Maste
25919261079SEd Maste static int
sk_touch_begin(struct sk_usbhid ** skv,size_t nsk)26019261079SEd Maste sk_touch_begin(struct sk_usbhid **skv, size_t nsk)
26119261079SEd Maste {
26219261079SEd Maste size_t i, ok = 0;
26319261079SEd Maste int r;
26419261079SEd Maste
26519261079SEd Maste for (i = 0; i < nsk; i++)
26619261079SEd Maste if ((r = fido_dev_get_touch_begin(skv[i]->dev)) != FIDO_OK)
26719261079SEd Maste skdebug(__func__, "fido_dev_get_touch_begin %s failed:"
26819261079SEd Maste " %s", skv[i]->path, fido_strerr(r));
26919261079SEd Maste else
27019261079SEd Maste ok++;
27119261079SEd Maste
27219261079SEd Maste return ok ? 0 : -1;
27319261079SEd Maste }
27419261079SEd Maste
27519261079SEd Maste static int
sk_touch_poll(struct sk_usbhid ** skv,size_t nsk,int * touch,size_t * idx)27619261079SEd Maste sk_touch_poll(struct sk_usbhid **skv, size_t nsk, int *touch, size_t *idx)
27719261079SEd Maste {
27819261079SEd Maste struct timespec ts_pause;
27919261079SEd Maste size_t npoll, i;
28019261079SEd Maste int r;
28119261079SEd Maste
28219261079SEd Maste ts_pause.tv_sec = 0;
28319261079SEd Maste ts_pause.tv_nsec = POLL_SLEEP_NS;
28419261079SEd Maste nanosleep(&ts_pause, NULL);
28519261079SEd Maste npoll = nsk;
28619261079SEd Maste for (i = 0; i < nsk; i++) {
28719261079SEd Maste if (skv[i] == NULL)
28819261079SEd Maste continue; /* device discarded */
28919261079SEd Maste skdebug(__func__, "polling %s", skv[i]->path);
29019261079SEd Maste if ((r = fido_dev_get_touch_status(skv[i]->dev, touch,
29119261079SEd Maste FIDO_POLL_MS)) != FIDO_OK) {
29219261079SEd Maste skdebug(__func__, "fido_dev_get_touch_status %s: %s",
29319261079SEd Maste skv[i]->path, fido_strerr(r));
29419261079SEd Maste sk_close(skv[i]); /* discard device */
29519261079SEd Maste skv[i] = NULL;
29619261079SEd Maste if (--npoll == 0) {
29719261079SEd Maste skdebug(__func__, "no device left to poll");
29819261079SEd Maste return -1;
29919261079SEd Maste }
30019261079SEd Maste } else if (*touch) {
30119261079SEd Maste *idx = i;
30219261079SEd Maste return 0;
30319261079SEd Maste }
30419261079SEd Maste }
30519261079SEd Maste *touch = 0;
30619261079SEd Maste return 0;
30719261079SEd Maste }
30819261079SEd Maste
3091323ec57SEd Maste #if !defined(HAVE_FIDO_ASSERT_SET_CLIENTDATA) || \
3101323ec57SEd Maste !defined(HAVE_FIDO_CRED_SET_CLIENTDATA)
31119261079SEd Maste /* Calculate SHA256(m) */
31219261079SEd Maste static int
sha256_mem(const void * m,size_t mlen,u_char * d,size_t dlen)31319261079SEd Maste sha256_mem(const void *m, size_t mlen, u_char *d, size_t dlen)
31419261079SEd Maste {
31519261079SEd Maste #ifdef WITH_OPENSSL
31619261079SEd Maste u_int mdlen;
3171323ec57SEd Maste #else
3181323ec57SEd Maste SHA2_CTX ctx;
31919261079SEd Maste #endif
32019261079SEd Maste
32119261079SEd Maste if (dlen != 32)
32219261079SEd Maste return -1;
32319261079SEd Maste #ifdef WITH_OPENSSL
32419261079SEd Maste mdlen = dlen;
32519261079SEd Maste if (!EVP_Digest(m, mlen, d, &mdlen, EVP_sha256(), NULL))
32619261079SEd Maste return -1;
32719261079SEd Maste #else
3281323ec57SEd Maste SHA256Init(&ctx);
3291323ec57SEd Maste SHA256Update(&ctx, (const uint8_t *)m, mlen);
3301323ec57SEd Maste SHA256Final(d, &ctx);
33119261079SEd Maste #endif
33219261079SEd Maste return 0;
33319261079SEd Maste }
3341323ec57SEd Maste #endif /* !HAVE_FIDO_ASSERT_SET_CLIENTDATA || !HAVE_FIDO_CRED_SET_CLIENTDATA */
3351323ec57SEd Maste
3361323ec57SEd Maste #ifndef HAVE_FIDO_CRED_SET_CLIENTDATA
3371323ec57SEd Maste static int
fido_cred_set_clientdata(fido_cred_t * cred,const u_char * ptr,size_t len)3381323ec57SEd Maste fido_cred_set_clientdata(fido_cred_t *cred, const u_char *ptr, size_t len)
3391323ec57SEd Maste {
3401323ec57SEd Maste uint8_t d[32];
3411323ec57SEd Maste int r;
3421323ec57SEd Maste
3431323ec57SEd Maste if (sha256_mem(ptr, len, d, sizeof(d)) != 0) {
3441323ec57SEd Maste skdebug(__func__, "hash challenge failed");
3451323ec57SEd Maste return FIDO_ERR_INTERNAL;
3461323ec57SEd Maste }
3471323ec57SEd Maste r = fido_cred_set_clientdata_hash(cred, d, sizeof(d));
3481323ec57SEd Maste explicit_bzero(d, sizeof(d));
3491323ec57SEd Maste if (r != FIDO_OK) {
3501323ec57SEd Maste skdebug(__func__, "fido_cred_set_clientdata_hash failed: %s",
3511323ec57SEd Maste fido_strerr(r));
3521323ec57SEd Maste }
3531323ec57SEd Maste return r;
3541323ec57SEd Maste }
3551323ec57SEd Maste #endif /* HAVE_FIDO_CRED_SET_CLIENTDATA */
3561323ec57SEd Maste
3571323ec57SEd Maste #ifndef HAVE_FIDO_ASSERT_SET_CLIENTDATA
3581323ec57SEd Maste static int
fido_assert_set_clientdata(fido_assert_t * assert,const u_char * ptr,size_t len)3591323ec57SEd Maste fido_assert_set_clientdata(fido_assert_t *assert, const u_char *ptr, size_t len)
3601323ec57SEd Maste {
3611323ec57SEd Maste uint8_t d[32];
3621323ec57SEd Maste int r;
3631323ec57SEd Maste
3641323ec57SEd Maste if (sha256_mem(ptr, len, d, sizeof(d)) != 0) {
3651323ec57SEd Maste skdebug(__func__, "hash challenge failed");
3661323ec57SEd Maste return FIDO_ERR_INTERNAL;
3671323ec57SEd Maste }
3681323ec57SEd Maste r = fido_assert_set_clientdata_hash(assert, d, sizeof(d));
3691323ec57SEd Maste explicit_bzero(d, sizeof(d));
3701323ec57SEd Maste if (r != FIDO_OK) {
3711323ec57SEd Maste skdebug(__func__, "fido_assert_set_clientdata_hash failed: %s",
3721323ec57SEd Maste fido_strerr(r));
3731323ec57SEd Maste }
3741323ec57SEd Maste return r;
3751323ec57SEd Maste }
3761323ec57SEd Maste #endif /* HAVE_FIDO_ASSERT_SET_CLIENTDATA */
37719261079SEd Maste
37838a52bd3SEd Maste #ifndef HAVE_FIDO_DEV_IS_WINHELLO
37938a52bd3SEd Maste static bool
fido_dev_is_winhello(const fido_dev_t * fdev)38038a52bd3SEd Maste fido_dev_is_winhello(const fido_dev_t *fdev)
38138a52bd3SEd Maste {
38238a52bd3SEd Maste return 0;
38338a52bd3SEd Maste }
38438a52bd3SEd Maste #endif /* HAVE_FIDO_DEV_IS_WINHELLO */
38538a52bd3SEd Maste
38619261079SEd Maste /* Check if the specified key handle exists on a given sk. */
38719261079SEd Maste static int
sk_try(const struct sk_usbhid * sk,const char * application,const uint8_t * key_handle,size_t key_handle_len)38819261079SEd Maste sk_try(const struct sk_usbhid *sk, const char *application,
38919261079SEd Maste const uint8_t *key_handle, size_t key_handle_len)
39019261079SEd Maste {
39119261079SEd Maste fido_assert_t *assert = NULL;
39219261079SEd Maste int r = FIDO_ERR_INTERNAL;
3931323ec57SEd Maste uint8_t message[32];
39419261079SEd Maste
3951323ec57SEd Maste memset(message, '\0', sizeof(message));
39619261079SEd Maste if ((assert = fido_assert_new()) == NULL) {
39719261079SEd Maste skdebug(__func__, "fido_assert_new failed");
39819261079SEd Maste goto out;
39919261079SEd Maste }
4001323ec57SEd Maste /* generate an invalid signature on FIDO2 tokens */
4011323ec57SEd Maste if ((r = fido_assert_set_clientdata(assert, message,
40219261079SEd Maste sizeof(message))) != FIDO_OK) {
40338a52bd3SEd Maste skdebug(__func__, "fido_assert_set_clientdata: %s",
40419261079SEd Maste fido_strerr(r));
40519261079SEd Maste goto out;
40619261079SEd Maste }
40719261079SEd Maste if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
40819261079SEd Maste skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
40919261079SEd Maste goto out;
41019261079SEd Maste }
41119261079SEd Maste if ((r = fido_assert_allow_cred(assert, key_handle,
41219261079SEd Maste key_handle_len)) != FIDO_OK) {
41319261079SEd Maste skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
41419261079SEd Maste goto out;
41519261079SEd Maste }
41619261079SEd Maste if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
41719261079SEd Maste skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
41819261079SEd Maste goto out;
41919261079SEd Maste }
42019261079SEd Maste r = fido_dev_get_assert(sk->dev, assert, NULL);
42119261079SEd Maste skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
42219261079SEd Maste if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
42319261079SEd Maste /* U2F tokens may return this */
42419261079SEd Maste r = FIDO_OK;
42519261079SEd Maste }
42619261079SEd Maste out:
42719261079SEd Maste fido_assert_free(&assert);
42819261079SEd Maste
42919261079SEd Maste return r != FIDO_OK ? -1 : 0;
43019261079SEd Maste }
43119261079SEd Maste
4321323ec57SEd Maste static int
check_sk_options(fido_dev_t * dev,const char * opt,int * ret)4331323ec57SEd Maste check_sk_options(fido_dev_t *dev, const char *opt, int *ret)
4341323ec57SEd Maste {
4351323ec57SEd Maste fido_cbor_info_t *info;
4361323ec57SEd Maste char * const *name;
4371323ec57SEd Maste const bool *value;
4381323ec57SEd Maste size_t len, i;
4391323ec57SEd Maste int r;
4401323ec57SEd Maste
4411323ec57SEd Maste *ret = -1;
4421323ec57SEd Maste
4431323ec57SEd Maste if (!fido_dev_is_fido2(dev)) {
4441323ec57SEd Maste skdebug(__func__, "device is not fido2");
4451323ec57SEd Maste return 0;
4461323ec57SEd Maste }
4471323ec57SEd Maste if ((info = fido_cbor_info_new()) == NULL) {
4481323ec57SEd Maste skdebug(__func__, "fido_cbor_info_new failed");
4491323ec57SEd Maste return -1;
4501323ec57SEd Maste }
4511323ec57SEd Maste if ((r = fido_dev_get_cbor_info(dev, info)) != FIDO_OK) {
4521323ec57SEd Maste skdebug(__func__, "fido_dev_get_cbor_info: %s", fido_strerr(r));
4531323ec57SEd Maste fido_cbor_info_free(&info);
4541323ec57SEd Maste return -1;
4551323ec57SEd Maste }
4561323ec57SEd Maste name = fido_cbor_info_options_name_ptr(info);
4571323ec57SEd Maste value = fido_cbor_info_options_value_ptr(info);
4581323ec57SEd Maste len = fido_cbor_info_options_len(info);
4591323ec57SEd Maste for (i = 0; i < len; i++) {
4601323ec57SEd Maste if (!strcmp(name[i], opt)) {
4611323ec57SEd Maste *ret = value[i];
4621323ec57SEd Maste break;
4631323ec57SEd Maste }
4641323ec57SEd Maste }
4651323ec57SEd Maste fido_cbor_info_free(&info);
4661323ec57SEd Maste if (*ret == -1)
4671323ec57SEd Maste skdebug(__func__, "option %s is unknown", opt);
4681323ec57SEd Maste else
4691323ec57SEd Maste skdebug(__func__, "option %s is %s", opt, *ret ? "on" : "off");
4701323ec57SEd Maste
4711323ec57SEd Maste return 0;
4721323ec57SEd Maste }
4731323ec57SEd Maste
47419261079SEd Maste static struct sk_usbhid *
sk_select_by_cred(const fido_dev_info_t * devlist,size_t ndevs,const char * application,const uint8_t * key_handle,size_t key_handle_len)47519261079SEd Maste sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs,
47619261079SEd Maste const char *application, const uint8_t *key_handle, size_t key_handle_len)
47719261079SEd Maste {
47819261079SEd Maste struct sk_usbhid **skv, *sk;
47919261079SEd Maste size_t skvcnt, i;
4801323ec57SEd Maste int internal_uv;
48119261079SEd Maste
48219261079SEd Maste if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
48319261079SEd Maste skdebug(__func__, "sk_openv failed");
48419261079SEd Maste return NULL;
48519261079SEd Maste }
4861323ec57SEd Maste if (skvcnt == 1 && check_sk_options(skv[0]->dev, "uv",
4871323ec57SEd Maste &internal_uv) == 0 && internal_uv != -1) {
48819261079SEd Maste sk = skv[0];
48919261079SEd Maste skv[0] = NULL;
49019261079SEd Maste goto out;
49119261079SEd Maste }
49219261079SEd Maste sk = NULL;
49319261079SEd Maste for (i = 0; i < skvcnt; i++) {
49419261079SEd Maste if (sk_try(skv[i], application, key_handle,
49519261079SEd Maste key_handle_len) == 0) {
49619261079SEd Maste sk = skv[i];
49719261079SEd Maste skv[i] = NULL;
49819261079SEd Maste skdebug(__func__, "found key in %s", sk->path);
49919261079SEd Maste break;
50019261079SEd Maste }
50119261079SEd Maste }
50219261079SEd Maste out:
50319261079SEd Maste sk_closev(skv, skvcnt);
50419261079SEd Maste return sk;
50519261079SEd Maste }
50619261079SEd Maste
50719261079SEd Maste static struct sk_usbhid *
sk_select_by_touch(const fido_dev_info_t * devlist,size_t ndevs)50819261079SEd Maste sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs)
50919261079SEd Maste {
51019261079SEd Maste struct sk_usbhid **skv, *sk;
51119261079SEd Maste struct timeval tv_start, tv_now, tv_delta;
51219261079SEd Maste size_t skvcnt, idx;
51319261079SEd Maste int touch, ms_remain;
51419261079SEd Maste
51519261079SEd Maste if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
51619261079SEd Maste skdebug(__func__, "sk_openv failed");
51719261079SEd Maste return NULL;
51819261079SEd Maste }
51919261079SEd Maste sk = NULL;
52019261079SEd Maste if (skvcnt < 2) {
52119261079SEd Maste if (skvcnt == 1) {
52219261079SEd Maste /* single candidate */
52319261079SEd Maste sk = skv[0];
52419261079SEd Maste skv[0] = NULL;
52519261079SEd Maste }
52619261079SEd Maste goto out;
52719261079SEd Maste }
52819261079SEd Maste #ifndef HAVE_FIDO_DEV_GET_TOUCH_STATUS
52919261079SEd Maste skdebug(__func__, "libfido2 version does not support a feature needed for multiple tokens. Please upgrade to >=1.5.0");
53019261079SEd Maste goto out;
53119261079SEd Maste #endif
53219261079SEd Maste
53319261079SEd Maste if (sk_touch_begin(skv, skvcnt) == -1) {
53419261079SEd Maste skdebug(__func__, "sk_touch_begin failed");
53519261079SEd Maste goto out;
53619261079SEd Maste }
53719261079SEd Maste monotime_tv(&tv_start);
53819261079SEd Maste do {
53919261079SEd Maste if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) {
54019261079SEd Maste skdebug(__func__, "sk_touch_poll failed");
54119261079SEd Maste goto out;
54219261079SEd Maste }
54319261079SEd Maste if (touch) {
54419261079SEd Maste sk = skv[idx];
54519261079SEd Maste skv[idx] = NULL;
54619261079SEd Maste goto out;
54719261079SEd Maste }
54819261079SEd Maste monotime_tv(&tv_now);
54919261079SEd Maste timersub(&tv_now, &tv_start, &tv_delta);
55019261079SEd Maste ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 -
55119261079SEd Maste tv_delta.tv_usec / 1000;
55219261079SEd Maste } while (ms_remain >= FIDO_POLL_MS);
55319261079SEd Maste skdebug(__func__, "timeout");
55419261079SEd Maste out:
55519261079SEd Maste sk_closev(skv, skvcnt);
55619261079SEd Maste return sk;
55719261079SEd Maste }
55819261079SEd Maste
55919261079SEd Maste static struct sk_usbhid *
sk_probe(const char * application,const uint8_t * key_handle,size_t key_handle_len,int probe_resident)56019261079SEd Maste sk_probe(const char *application, const uint8_t *key_handle,
56138a52bd3SEd Maste size_t key_handle_len, int probe_resident)
56219261079SEd Maste {
56319261079SEd Maste struct sk_usbhid *sk;
56419261079SEd Maste fido_dev_info_t *devlist;
56519261079SEd Maste size_t ndevs;
56619261079SEd Maste int r;
56719261079SEd Maste
56838a52bd3SEd Maste #ifdef HAVE_CYGWIN
56938a52bd3SEd Maste if (!probe_resident && (sk = sk_open("windows://hello")) != NULL)
57038a52bd3SEd Maste return sk;
57138a52bd3SEd Maste #endif /* HAVE_CYGWIN */
57219261079SEd Maste if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
57319261079SEd Maste skdebug(__func__, "fido_dev_info_new failed");
57419261079SEd Maste return NULL;
57519261079SEd Maste }
57619261079SEd Maste if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
57719261079SEd Maste &ndevs)) != FIDO_OK) {
57819261079SEd Maste skdebug(__func__, "fido_dev_info_manifest failed: %s",
57919261079SEd Maste fido_strerr(r));
58019261079SEd Maste fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
58119261079SEd Maste return NULL;
58219261079SEd Maste }
58319261079SEd Maste skdebug(__func__, "%zu device(s) detected", ndevs);
58419261079SEd Maste if (ndevs == 0) {
58519261079SEd Maste sk = NULL;
58619261079SEd Maste } else if (application != NULL && key_handle != NULL) {
58719261079SEd Maste skdebug(__func__, "selecting sk by cred");
58819261079SEd Maste sk = sk_select_by_cred(devlist, ndevs, application, key_handle,
58919261079SEd Maste key_handle_len);
59019261079SEd Maste } else {
59119261079SEd Maste skdebug(__func__, "selecting sk by touch");
59219261079SEd Maste sk = sk_select_by_touch(devlist, ndevs);
59319261079SEd Maste }
59419261079SEd Maste fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
59519261079SEd Maste return sk;
59619261079SEd Maste }
59719261079SEd Maste
59819261079SEd Maste #ifdef WITH_OPENSSL
59919261079SEd Maste /*
60019261079SEd Maste * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
60119261079SEd Maste * but the API expects a SEC1 octet string.
60219261079SEd Maste */
60319261079SEd Maste static int
pack_public_key_ecdsa(const fido_cred_t * cred,struct sk_enroll_response * response)60419261079SEd Maste pack_public_key_ecdsa(const fido_cred_t *cred,
60519261079SEd Maste struct sk_enroll_response *response)
60619261079SEd Maste {
60719261079SEd Maste const uint8_t *ptr;
60819261079SEd Maste BIGNUM *x = NULL, *y = NULL;
60919261079SEd Maste EC_POINT *q = NULL;
61019261079SEd Maste EC_GROUP *g = NULL;
61119261079SEd Maste int ret = -1;
61219261079SEd Maste
61319261079SEd Maste response->public_key = NULL;
61419261079SEd Maste response->public_key_len = 0;
61519261079SEd Maste
61619261079SEd Maste if ((x = BN_new()) == NULL ||
61719261079SEd Maste (y = BN_new()) == NULL ||
61819261079SEd Maste (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
61919261079SEd Maste (q = EC_POINT_new(g)) == NULL) {
62019261079SEd Maste skdebug(__func__, "libcrypto setup failed");
62119261079SEd Maste goto out;
62219261079SEd Maste }
62319261079SEd Maste if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
62419261079SEd Maste skdebug(__func__, "fido_cred_pubkey_ptr failed");
62519261079SEd Maste goto out;
62619261079SEd Maste }
62719261079SEd Maste if (fido_cred_pubkey_len(cred) != 64) {
62819261079SEd Maste skdebug(__func__, "bad fido_cred_pubkey_len %zu",
62919261079SEd Maste fido_cred_pubkey_len(cred));
63019261079SEd Maste goto out;
63119261079SEd Maste }
63219261079SEd Maste
63319261079SEd Maste if (BN_bin2bn(ptr, 32, x) == NULL ||
63419261079SEd Maste BN_bin2bn(ptr + 32, 32, y) == NULL) {
63519261079SEd Maste skdebug(__func__, "BN_bin2bn failed");
63619261079SEd Maste goto out;
63719261079SEd Maste }
63819261079SEd Maste if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) {
63919261079SEd Maste skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
64019261079SEd Maste goto out;
64119261079SEd Maste }
64219261079SEd Maste response->public_key_len = EC_POINT_point2oct(g, q,
64319261079SEd Maste POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
64419261079SEd Maste if (response->public_key_len == 0 || response->public_key_len > 2048) {
64519261079SEd Maste skdebug(__func__, "bad pubkey length %zu",
64619261079SEd Maste response->public_key_len);
64719261079SEd Maste goto out;
64819261079SEd Maste }
64919261079SEd Maste if ((response->public_key = malloc(response->public_key_len)) == NULL) {
65019261079SEd Maste skdebug(__func__, "malloc pubkey failed");
65119261079SEd Maste goto out;
65219261079SEd Maste }
65319261079SEd Maste if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
65419261079SEd Maste response->public_key, response->public_key_len, NULL) == 0) {
65519261079SEd Maste skdebug(__func__, "EC_POINT_point2oct failed");
65619261079SEd Maste goto out;
65719261079SEd Maste }
65819261079SEd Maste /* success */
65919261079SEd Maste ret = 0;
66019261079SEd Maste out:
66119261079SEd Maste if (ret != 0 && response->public_key != NULL) {
66219261079SEd Maste memset(response->public_key, 0, response->public_key_len);
66319261079SEd Maste free(response->public_key);
66419261079SEd Maste response->public_key = NULL;
66519261079SEd Maste }
66619261079SEd Maste EC_POINT_free(q);
66719261079SEd Maste EC_GROUP_free(g);
66819261079SEd Maste BN_clear_free(x);
66919261079SEd Maste BN_clear_free(y);
67019261079SEd Maste return ret;
67119261079SEd Maste }
67219261079SEd Maste #endif /* WITH_OPENSSL */
67319261079SEd Maste
67419261079SEd Maste static int
pack_public_key_ed25519(const fido_cred_t * cred,struct sk_enroll_response * response)67519261079SEd Maste pack_public_key_ed25519(const fido_cred_t *cred,
67619261079SEd Maste struct sk_enroll_response *response)
67719261079SEd Maste {
67819261079SEd Maste const uint8_t *ptr;
67919261079SEd Maste size_t len;
68019261079SEd Maste int ret = -1;
68119261079SEd Maste
68219261079SEd Maste response->public_key = NULL;
68319261079SEd Maste response->public_key_len = 0;
68419261079SEd Maste
68519261079SEd Maste if ((len = fido_cred_pubkey_len(cred)) != 32) {
68619261079SEd Maste skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
68719261079SEd Maste goto out;
68819261079SEd Maste }
68919261079SEd Maste if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
69019261079SEd Maste skdebug(__func__, "fido_cred_pubkey_ptr failed");
69119261079SEd Maste goto out;
69219261079SEd Maste }
69319261079SEd Maste response->public_key_len = len;
69419261079SEd Maste if ((response->public_key = malloc(response->public_key_len)) == NULL) {
69519261079SEd Maste skdebug(__func__, "malloc pubkey failed");
69619261079SEd Maste goto out;
69719261079SEd Maste }
69819261079SEd Maste memcpy(response->public_key, ptr, len);
69919261079SEd Maste ret = 0;
70019261079SEd Maste out:
70119261079SEd Maste if (ret != 0)
70219261079SEd Maste free(response->public_key);
70319261079SEd Maste return ret;
70419261079SEd Maste }
70519261079SEd Maste
70619261079SEd Maste static int
pack_public_key(uint32_t alg,const fido_cred_t * cred,struct sk_enroll_response * response)70719261079SEd Maste pack_public_key(uint32_t alg, const fido_cred_t *cred,
70819261079SEd Maste struct sk_enroll_response *response)
70919261079SEd Maste {
71019261079SEd Maste switch(alg) {
71119261079SEd Maste #ifdef WITH_OPENSSL
71219261079SEd Maste case SSH_SK_ECDSA:
71319261079SEd Maste return pack_public_key_ecdsa(cred, response);
71419261079SEd Maste #endif /* WITH_OPENSSL */
71519261079SEd Maste case SSH_SK_ED25519:
71619261079SEd Maste return pack_public_key_ed25519(cred, response);
71719261079SEd Maste default:
71819261079SEd Maste return -1;
71919261079SEd Maste }
72019261079SEd Maste }
72119261079SEd Maste
72219261079SEd Maste static int
fidoerr_to_skerr(int fidoerr)72319261079SEd Maste fidoerr_to_skerr(int fidoerr)
72419261079SEd Maste {
72519261079SEd Maste switch (fidoerr) {
72619261079SEd Maste case FIDO_ERR_UNSUPPORTED_OPTION:
72719261079SEd Maste case FIDO_ERR_UNSUPPORTED_ALGORITHM:
72819261079SEd Maste return SSH_SK_ERR_UNSUPPORTED;
72919261079SEd Maste case FIDO_ERR_PIN_REQUIRED:
73019261079SEd Maste case FIDO_ERR_PIN_INVALID:
7311323ec57SEd Maste case FIDO_ERR_OPERATION_DENIED:
73219261079SEd Maste return SSH_SK_ERR_PIN_REQUIRED;
73319261079SEd Maste default:
73419261079SEd Maste return -1;
73519261079SEd Maste }
73619261079SEd Maste }
73719261079SEd Maste
73819261079SEd Maste static int
check_enroll_options(struct sk_option ** options,char ** devicep,uint8_t * user_id,size_t user_id_len)73919261079SEd Maste check_enroll_options(struct sk_option **options, char **devicep,
74019261079SEd Maste uint8_t *user_id, size_t user_id_len)
74119261079SEd Maste {
74219261079SEd Maste size_t i;
74319261079SEd Maste
74419261079SEd Maste if (options == NULL)
74519261079SEd Maste return 0;
74619261079SEd Maste for (i = 0; options[i] != NULL; i++) {
74719261079SEd Maste if (strcmp(options[i]->name, "device") == 0) {
74819261079SEd Maste if ((*devicep = strdup(options[i]->value)) == NULL) {
74919261079SEd Maste skdebug(__func__, "strdup device failed");
75019261079SEd Maste return -1;
75119261079SEd Maste }
75219261079SEd Maste skdebug(__func__, "requested device %s", *devicep);
75319261079SEd Maste } else if (strcmp(options[i]->name, "user") == 0) {
75419261079SEd Maste if (strlcpy(user_id, options[i]->value, user_id_len) >=
75519261079SEd Maste user_id_len) {
75619261079SEd Maste skdebug(__func__, "user too long");
75719261079SEd Maste return -1;
75819261079SEd Maste }
75919261079SEd Maste skdebug(__func__, "requested user %s",
76019261079SEd Maste (char *)user_id);
76119261079SEd Maste } else {
76219261079SEd Maste skdebug(__func__, "requested unsupported option %s",
76319261079SEd Maste options[i]->name);
76419261079SEd Maste if (options[i]->required) {
76519261079SEd Maste skdebug(__func__, "unknown required option");
76619261079SEd Maste return -1;
76719261079SEd Maste }
76819261079SEd Maste }
76919261079SEd Maste }
77019261079SEd Maste return 0;
77119261079SEd Maste }
77219261079SEd Maste
77338a52bd3SEd Maste static int
key_lookup(fido_dev_t * dev,const char * application,const uint8_t * user_id,size_t user_id_len,const char * pin)77438a52bd3SEd Maste key_lookup(fido_dev_t *dev, const char *application, const uint8_t *user_id,
77538a52bd3SEd Maste size_t user_id_len, const char *pin)
77638a52bd3SEd Maste {
77738a52bd3SEd Maste fido_assert_t *assert = NULL;
77838a52bd3SEd Maste uint8_t message[32];
77938a52bd3SEd Maste int r = FIDO_ERR_INTERNAL;
78038a52bd3SEd Maste int sk_supports_uv, uv;
78138a52bd3SEd Maste size_t i;
78238a52bd3SEd Maste
78338a52bd3SEd Maste memset(message, '\0', sizeof(message));
78438a52bd3SEd Maste if ((assert = fido_assert_new()) == NULL) {
78538a52bd3SEd Maste skdebug(__func__, "fido_assert_new failed");
78638a52bd3SEd Maste goto out;
78738a52bd3SEd Maste }
78838a52bd3SEd Maste /* generate an invalid signature on FIDO2 tokens */
78938a52bd3SEd Maste if ((r = fido_assert_set_clientdata(assert, message,
79038a52bd3SEd Maste sizeof(message))) != FIDO_OK) {
79138a52bd3SEd Maste skdebug(__func__, "fido_assert_set_clientdata: %s",
79238a52bd3SEd Maste fido_strerr(r));
79338a52bd3SEd Maste goto out;
79438a52bd3SEd Maste }
79538a52bd3SEd Maste if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
79638a52bd3SEd Maste skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
79738a52bd3SEd Maste goto out;
79838a52bd3SEd Maste }
79938a52bd3SEd Maste if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
80038a52bd3SEd Maste skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
80138a52bd3SEd Maste goto out;
80238a52bd3SEd Maste }
80338a52bd3SEd Maste uv = FIDO_OPT_OMIT;
80438a52bd3SEd Maste if (pin == NULL && check_sk_options(dev, "uv", &sk_supports_uv) == 0 &&
80538a52bd3SEd Maste sk_supports_uv != -1)
80638a52bd3SEd Maste uv = FIDO_OPT_TRUE;
80738a52bd3SEd Maste if ((r = fido_assert_set_uv(assert, uv)) != FIDO_OK) {
80838a52bd3SEd Maste skdebug(__func__, "fido_assert_set_uv: %s", fido_strerr(r));
80938a52bd3SEd Maste goto out;
81038a52bd3SEd Maste }
81138a52bd3SEd Maste if ((r = fido_dev_get_assert(dev, assert, pin)) != FIDO_OK) {
81238a52bd3SEd Maste skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
81338a52bd3SEd Maste goto out;
81438a52bd3SEd Maste }
81538a52bd3SEd Maste r = FIDO_ERR_NO_CREDENTIALS;
81638a52bd3SEd Maste skdebug(__func__, "%zu signatures returned", fido_assert_count(assert));
81738a52bd3SEd Maste for (i = 0; i < fido_assert_count(assert); i++) {
81838a52bd3SEd Maste if (fido_assert_user_id_len(assert, i) == user_id_len &&
81938a52bd3SEd Maste memcmp(fido_assert_user_id_ptr(assert, i), user_id,
82038a52bd3SEd Maste user_id_len) == 0) {
82138a52bd3SEd Maste skdebug(__func__, "credential exists");
82238a52bd3SEd Maste r = FIDO_OK;
82338a52bd3SEd Maste goto out;
82438a52bd3SEd Maste }
82538a52bd3SEd Maste }
82638a52bd3SEd Maste out:
82738a52bd3SEd Maste fido_assert_free(&assert);
82838a52bd3SEd Maste
82938a52bd3SEd Maste return r;
83038a52bd3SEd Maste }
83138a52bd3SEd Maste
83219261079SEd Maste int
sk_enroll(uint32_t alg,const uint8_t * challenge,size_t challenge_len,const char * application,uint8_t flags,const char * pin,struct sk_option ** options,struct sk_enroll_response ** enroll_response)83319261079SEd Maste sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
83419261079SEd Maste const char *application, uint8_t flags, const char *pin,
83519261079SEd Maste struct sk_option **options, struct sk_enroll_response **enroll_response)
83619261079SEd Maste {
83719261079SEd Maste fido_cred_t *cred = NULL;
83819261079SEd Maste const uint8_t *ptr;
8391323ec57SEd Maste uint8_t user_id[32];
84019261079SEd Maste struct sk_usbhid *sk = NULL;
84119261079SEd Maste struct sk_enroll_response *response = NULL;
84219261079SEd Maste size_t len;
84319261079SEd Maste int credprot;
84419261079SEd Maste int cose_alg;
84519261079SEd Maste int ret = SSH_SK_ERR_GENERAL;
84619261079SEd Maste int r;
84719261079SEd Maste char *device = NULL;
84819261079SEd Maste
84919261079SEd Maste fido_init(SSH_FIDO_INIT_ARG);
85019261079SEd Maste
85119261079SEd Maste if (enroll_response == NULL) {
85219261079SEd Maste skdebug(__func__, "enroll_response == NULL");
85319261079SEd Maste goto out;
85419261079SEd Maste }
85519261079SEd Maste *enroll_response = NULL;
85619261079SEd Maste memset(user_id, 0, sizeof(user_id));
85719261079SEd Maste if (check_enroll_options(options, &device, user_id,
85819261079SEd Maste sizeof(user_id)) != 0)
85919261079SEd Maste goto out; /* error already logged */
86019261079SEd Maste
86119261079SEd Maste switch(alg) {
86219261079SEd Maste #ifdef WITH_OPENSSL
86319261079SEd Maste case SSH_SK_ECDSA:
86419261079SEd Maste cose_alg = COSE_ES256;
86519261079SEd Maste break;
86619261079SEd Maste #endif /* WITH_OPENSSL */
86719261079SEd Maste case SSH_SK_ED25519:
86819261079SEd Maste cose_alg = COSE_EDDSA;
86919261079SEd Maste break;
87019261079SEd Maste default:
87119261079SEd Maste skdebug(__func__, "unsupported key type %d", alg);
87219261079SEd Maste goto out;
87319261079SEd Maste }
87419261079SEd Maste if (device != NULL)
87519261079SEd Maste sk = sk_open(device);
87619261079SEd Maste else
87738a52bd3SEd Maste sk = sk_probe(NULL, NULL, 0, 0);
87819261079SEd Maste if (sk == NULL) {
8791323ec57SEd Maste ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
88019261079SEd Maste skdebug(__func__, "failed to find sk");
88119261079SEd Maste goto out;
88219261079SEd Maste }
88319261079SEd Maste skdebug(__func__, "using device %s", sk->path);
88438a52bd3SEd Maste if ((flags & SSH_SK_RESIDENT_KEY) != 0 &&
88538a52bd3SEd Maste (flags & SSH_SK_FORCE_OPERATION) == 0 &&
88638a52bd3SEd Maste (r = key_lookup(sk->dev, application, user_id, sizeof(user_id),
88738a52bd3SEd Maste pin)) != FIDO_ERR_NO_CREDENTIALS) {
88838a52bd3SEd Maste if (r != FIDO_OK) {
88938a52bd3SEd Maste ret = fidoerr_to_skerr(r);
89038a52bd3SEd Maste skdebug(__func__, "key_lookup failed");
89138a52bd3SEd Maste } else {
89238a52bd3SEd Maste ret = SSH_SK_ERR_CREDENTIAL_EXISTS;
89338a52bd3SEd Maste skdebug(__func__, "key exists");
89438a52bd3SEd Maste }
89538a52bd3SEd Maste goto out;
89638a52bd3SEd Maste }
89719261079SEd Maste if ((cred = fido_cred_new()) == NULL) {
89819261079SEd Maste skdebug(__func__, "fido_cred_new failed");
89919261079SEd Maste goto out;
90019261079SEd Maste }
90119261079SEd Maste if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
90219261079SEd Maste skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
90319261079SEd Maste goto out;
90419261079SEd Maste }
9051323ec57SEd Maste if ((r = fido_cred_set_clientdata(cred,
9061323ec57SEd Maste challenge, challenge_len)) != FIDO_OK) {
9071323ec57SEd Maste skdebug(__func__, "fido_cred_set_clientdata: %s",
90819261079SEd Maste fido_strerr(r));
90919261079SEd Maste goto out;
91019261079SEd Maste }
91119261079SEd Maste if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ?
91219261079SEd Maste FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
91319261079SEd Maste skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
91419261079SEd Maste goto out;
91519261079SEd Maste }
91619261079SEd Maste if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
91719261079SEd Maste "openssh", "openssh", NULL)) != FIDO_OK) {
91819261079SEd Maste skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
91919261079SEd Maste goto out;
92019261079SEd Maste }
92119261079SEd Maste if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
92219261079SEd Maste skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
92319261079SEd Maste goto out;
92419261079SEd Maste }
92519261079SEd Maste if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) {
92619261079SEd Maste #if !defined(HAVE_FIDO_DEV_SUPPORTS_CRED_PROT) || \
92719261079SEd Maste !defined(HAVE_FIDO_CRED_SET_PROT)
92819261079SEd Maste skdebug(__func__, "libfido2 version does not support a feature required for this operation. Please upgrade to >=1.5.0");
92919261079SEd Maste ret = SSH_SK_ERR_UNSUPPORTED;
93019261079SEd Maste goto out;
93119261079SEd Maste credprot = 0; (void)credprot; /* avoid warning */
93219261079SEd Maste #endif
93319261079SEd Maste if (!fido_dev_supports_cred_prot(sk->dev)) {
93419261079SEd Maste skdebug(__func__, "%s does not support credprot, "
93519261079SEd Maste "refusing to create unprotected "
93619261079SEd Maste "resident/verify-required key", sk->path);
93719261079SEd Maste ret = SSH_SK_ERR_UNSUPPORTED;
93819261079SEd Maste goto out;
93919261079SEd Maste }
94019261079SEd Maste if ((flags & SSH_SK_USER_VERIFICATION_REQD))
94119261079SEd Maste credprot = FIDO_CRED_PROT_UV_REQUIRED;
94219261079SEd Maste else
94319261079SEd Maste credprot = FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID;
94419261079SEd Maste
94519261079SEd Maste if ((r = fido_cred_set_prot(cred, credprot)) != FIDO_OK) {
94619261079SEd Maste skdebug(__func__, "fido_cred_set_prot: %s",
94719261079SEd Maste fido_strerr(r));
94819261079SEd Maste ret = fidoerr_to_skerr(r);
94919261079SEd Maste goto out;
95019261079SEd Maste }
95119261079SEd Maste }
95219261079SEd Maste if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) {
95319261079SEd Maste skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
95419261079SEd Maste ret = fidoerr_to_skerr(r);
95519261079SEd Maste goto out;
95619261079SEd Maste }
95719261079SEd Maste if (fido_cred_x5c_ptr(cred) != NULL) {
95819261079SEd Maste if ((r = fido_cred_verify(cred)) != FIDO_OK) {
95919261079SEd Maste skdebug(__func__, "fido_cred_verify: %s",
96019261079SEd Maste fido_strerr(r));
96119261079SEd Maste goto out;
96219261079SEd Maste }
96319261079SEd Maste } else {
96419261079SEd Maste skdebug(__func__, "self-attested credential");
96519261079SEd Maste if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
96619261079SEd Maste skdebug(__func__, "fido_cred_verify_self: %s",
96719261079SEd Maste fido_strerr(r));
96819261079SEd Maste goto out;
96919261079SEd Maste }
97019261079SEd Maste }
97119261079SEd Maste if ((response = calloc(1, sizeof(*response))) == NULL) {
97219261079SEd Maste skdebug(__func__, "calloc response failed");
97319261079SEd Maste goto out;
97419261079SEd Maste }
9751323ec57SEd Maste response->flags = flags;
97619261079SEd Maste if (pack_public_key(alg, cred, response) != 0) {
97719261079SEd Maste skdebug(__func__, "pack_public_key failed");
97819261079SEd Maste goto out;
97919261079SEd Maste }
98019261079SEd Maste if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
98119261079SEd Maste len = fido_cred_id_len(cred);
98219261079SEd Maste if ((response->key_handle = calloc(1, len)) == NULL) {
98319261079SEd Maste skdebug(__func__, "calloc key handle failed");
98419261079SEd Maste goto out;
98519261079SEd Maste }
98619261079SEd Maste memcpy(response->key_handle, ptr, len);
98719261079SEd Maste response->key_handle_len = len;
98819261079SEd Maste }
98919261079SEd Maste if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
99019261079SEd Maste len = fido_cred_sig_len(cred);
99119261079SEd Maste if ((response->signature = calloc(1, len)) == NULL) {
99219261079SEd Maste skdebug(__func__, "calloc signature failed");
99319261079SEd Maste goto out;
99419261079SEd Maste }
99519261079SEd Maste memcpy(response->signature, ptr, len);
99619261079SEd Maste response->signature_len = len;
99719261079SEd Maste }
99819261079SEd Maste if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
99919261079SEd Maste len = fido_cred_x5c_len(cred);
100019261079SEd Maste skdebug(__func__, "attestation cert len=%zu", len);
100119261079SEd Maste if ((response->attestation_cert = calloc(1, len)) == NULL) {
100219261079SEd Maste skdebug(__func__, "calloc attestation cert failed");
100319261079SEd Maste goto out;
100419261079SEd Maste }
100519261079SEd Maste memcpy(response->attestation_cert, ptr, len);
100619261079SEd Maste response->attestation_cert_len = len;
100719261079SEd Maste }
100819261079SEd Maste if ((ptr = fido_cred_authdata_ptr(cred)) != NULL) {
100919261079SEd Maste len = fido_cred_authdata_len(cred);
101019261079SEd Maste skdebug(__func__, "authdata len=%zu", len);
101119261079SEd Maste if ((response->authdata = calloc(1, len)) == NULL) {
101219261079SEd Maste skdebug(__func__, "calloc authdata failed");
101319261079SEd Maste goto out;
101419261079SEd Maste }
101519261079SEd Maste memcpy(response->authdata, ptr, len);
101619261079SEd Maste response->authdata_len = len;
101719261079SEd Maste }
101819261079SEd Maste *enroll_response = response;
101919261079SEd Maste response = NULL;
102019261079SEd Maste ret = 0;
102119261079SEd Maste out:
102219261079SEd Maste free(device);
102319261079SEd Maste if (response != NULL) {
102419261079SEd Maste free(response->public_key);
102519261079SEd Maste free(response->key_handle);
102619261079SEd Maste free(response->signature);
102719261079SEd Maste free(response->attestation_cert);
102819261079SEd Maste free(response->authdata);
102919261079SEd Maste free(response);
103019261079SEd Maste }
103119261079SEd Maste sk_close(sk);
103219261079SEd Maste fido_cred_free(&cred);
103319261079SEd Maste return ret;
103419261079SEd Maste }
103519261079SEd Maste
103619261079SEd Maste #ifdef WITH_OPENSSL
103719261079SEd Maste static int
pack_sig_ecdsa(fido_assert_t * assert,struct sk_sign_response * response)103819261079SEd Maste pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
103919261079SEd Maste {
104019261079SEd Maste ECDSA_SIG *sig = NULL;
104119261079SEd Maste const BIGNUM *sig_r, *sig_s;
104219261079SEd Maste const unsigned char *cp;
104319261079SEd Maste size_t sig_len;
104419261079SEd Maste int ret = -1;
104519261079SEd Maste
104619261079SEd Maste cp = fido_assert_sig_ptr(assert, 0);
104719261079SEd Maste sig_len = fido_assert_sig_len(assert, 0);
104819261079SEd Maste if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
104919261079SEd Maste skdebug(__func__, "d2i_ECDSA_SIG failed");
105019261079SEd Maste goto out;
105119261079SEd Maste }
105219261079SEd Maste ECDSA_SIG_get0(sig, &sig_r, &sig_s);
105319261079SEd Maste response->sig_r_len = BN_num_bytes(sig_r);
105419261079SEd Maste response->sig_s_len = BN_num_bytes(sig_s);
105519261079SEd Maste if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
105619261079SEd Maste (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
105719261079SEd Maste skdebug(__func__, "calloc signature failed");
105819261079SEd Maste goto out;
105919261079SEd Maste }
106019261079SEd Maste BN_bn2bin(sig_r, response->sig_r);
106119261079SEd Maste BN_bn2bin(sig_s, response->sig_s);
106219261079SEd Maste ret = 0;
106319261079SEd Maste out:
106419261079SEd Maste ECDSA_SIG_free(sig);
106519261079SEd Maste if (ret != 0) {
106619261079SEd Maste free(response->sig_r);
106719261079SEd Maste free(response->sig_s);
106819261079SEd Maste response->sig_r = NULL;
106919261079SEd Maste response->sig_s = NULL;
107019261079SEd Maste }
107119261079SEd Maste return ret;
107219261079SEd Maste }
107319261079SEd Maste #endif /* WITH_OPENSSL */
107419261079SEd Maste
107519261079SEd Maste static int
pack_sig_ed25519(fido_assert_t * assert,struct sk_sign_response * response)107619261079SEd Maste pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
107719261079SEd Maste {
107819261079SEd Maste const unsigned char *ptr;
107919261079SEd Maste size_t len;
108019261079SEd Maste int ret = -1;
108119261079SEd Maste
108219261079SEd Maste ptr = fido_assert_sig_ptr(assert, 0);
108319261079SEd Maste len = fido_assert_sig_len(assert, 0);
108419261079SEd Maste if (len != 64) {
108519261079SEd Maste skdebug(__func__, "bad length %zu", len);
108619261079SEd Maste goto out;
108719261079SEd Maste }
108819261079SEd Maste response->sig_r_len = len;
108919261079SEd Maste if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
109019261079SEd Maste skdebug(__func__, "calloc signature failed");
109119261079SEd Maste goto out;
109219261079SEd Maste }
109319261079SEd Maste memcpy(response->sig_r, ptr, len);
109419261079SEd Maste ret = 0;
109519261079SEd Maste out:
109619261079SEd Maste if (ret != 0) {
109719261079SEd Maste free(response->sig_r);
109819261079SEd Maste response->sig_r = NULL;
109919261079SEd Maste }
110019261079SEd Maste return ret;
110119261079SEd Maste }
110219261079SEd Maste
110319261079SEd Maste static int
pack_sig(uint32_t alg,fido_assert_t * assert,struct sk_sign_response * response)110419261079SEd Maste pack_sig(uint32_t alg, fido_assert_t *assert,
110519261079SEd Maste struct sk_sign_response *response)
110619261079SEd Maste {
110719261079SEd Maste switch(alg) {
110819261079SEd Maste #ifdef WITH_OPENSSL
110919261079SEd Maste case SSH_SK_ECDSA:
111019261079SEd Maste return pack_sig_ecdsa(assert, response);
111119261079SEd Maste #endif /* WITH_OPENSSL */
111219261079SEd Maste case SSH_SK_ED25519:
111319261079SEd Maste return pack_sig_ed25519(assert, response);
111419261079SEd Maste default:
111519261079SEd Maste return -1;
111619261079SEd Maste }
111719261079SEd Maste }
111819261079SEd Maste
111919261079SEd Maste /* Checks sk_options for sk_sign() and sk_load_resident_keys() */
112019261079SEd Maste static int
check_sign_load_resident_options(struct sk_option ** options,char ** devicep)112119261079SEd Maste check_sign_load_resident_options(struct sk_option **options, char **devicep)
112219261079SEd Maste {
112319261079SEd Maste size_t i;
112419261079SEd Maste
112519261079SEd Maste if (options == NULL)
112619261079SEd Maste return 0;
112719261079SEd Maste for (i = 0; options[i] != NULL; i++) {
112819261079SEd Maste if (strcmp(options[i]->name, "device") == 0) {
112919261079SEd Maste if ((*devicep = strdup(options[i]->value)) == NULL) {
113019261079SEd Maste skdebug(__func__, "strdup device failed");
113119261079SEd Maste return -1;
113219261079SEd Maste }
113319261079SEd Maste skdebug(__func__, "requested device %s", *devicep);
113419261079SEd Maste } else {
113519261079SEd Maste skdebug(__func__, "requested unsupported option %s",
113619261079SEd Maste options[i]->name);
113719261079SEd Maste if (options[i]->required) {
113819261079SEd Maste skdebug(__func__, "unknown required option");
113919261079SEd Maste return -1;
114019261079SEd Maste }
114119261079SEd Maste }
114219261079SEd Maste }
114319261079SEd Maste return 0;
114419261079SEd Maste }
114519261079SEd Maste
114619261079SEd Maste int
sk_sign(uint32_t alg,const uint8_t * data,size_t datalen,const char * application,const uint8_t * key_handle,size_t key_handle_len,uint8_t flags,const char * pin,struct sk_option ** options,struct sk_sign_response ** sign_response)114719261079SEd Maste sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
114819261079SEd Maste const char *application,
114919261079SEd Maste const uint8_t *key_handle, size_t key_handle_len,
115019261079SEd Maste uint8_t flags, const char *pin, struct sk_option **options,
115119261079SEd Maste struct sk_sign_response **sign_response)
115219261079SEd Maste {
115319261079SEd Maste fido_assert_t *assert = NULL;
115419261079SEd Maste char *device = NULL;
115519261079SEd Maste struct sk_usbhid *sk = NULL;
115619261079SEd Maste struct sk_sign_response *response = NULL;
11571323ec57SEd Maste int ret = SSH_SK_ERR_GENERAL, internal_uv;
115819261079SEd Maste int r;
115919261079SEd Maste
116019261079SEd Maste fido_init(SSH_FIDO_INIT_ARG);
116119261079SEd Maste
116219261079SEd Maste if (sign_response == NULL) {
116319261079SEd Maste skdebug(__func__, "sign_response == NULL");
116419261079SEd Maste goto out;
116519261079SEd Maste }
116619261079SEd Maste *sign_response = NULL;
116719261079SEd Maste if (check_sign_load_resident_options(options, &device) != 0)
116819261079SEd Maste goto out; /* error already logged */
116919261079SEd Maste if (device != NULL)
117019261079SEd Maste sk = sk_open(device);
117119261079SEd Maste else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD))
117238a52bd3SEd Maste sk = sk_probe(NULL, NULL, 0, 0);
117319261079SEd Maste else
117438a52bd3SEd Maste sk = sk_probe(application, key_handle, key_handle_len, 0);
117519261079SEd Maste if (sk == NULL) {
11761323ec57SEd Maste ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
117719261079SEd Maste skdebug(__func__, "failed to find sk");
117819261079SEd Maste goto out;
117919261079SEd Maste }
118019261079SEd Maste if ((assert = fido_assert_new()) == NULL) {
118119261079SEd Maste skdebug(__func__, "fido_assert_new failed");
118219261079SEd Maste goto out;
118319261079SEd Maste }
11841323ec57SEd Maste if ((r = fido_assert_set_clientdata(assert,
11851323ec57SEd Maste data, datalen)) != FIDO_OK) {
11861323ec57SEd Maste skdebug(__func__, "fido_assert_set_clientdata: %s",
118719261079SEd Maste fido_strerr(r));
118819261079SEd Maste goto out;
118919261079SEd Maste }
119019261079SEd Maste if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
119119261079SEd Maste skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
119219261079SEd Maste goto out;
119319261079SEd Maste }
119419261079SEd Maste if ((r = fido_assert_allow_cred(assert, key_handle,
119519261079SEd Maste key_handle_len)) != FIDO_OK) {
119619261079SEd Maste skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
119719261079SEd Maste goto out;
119819261079SEd Maste }
119919261079SEd Maste if ((r = fido_assert_set_up(assert,
120019261079SEd Maste (flags & SSH_SK_USER_PRESENCE_REQD) ?
120119261079SEd Maste FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
120219261079SEd Maste skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
120319261079SEd Maste goto out;
120419261079SEd Maste }
120538a52bd3SEd Maste /*
120638a52bd3SEd Maste * WinHello requests the PIN by default. Make "uv" request explicit
120738a52bd3SEd Maste * to allow keys with and without -O verify-required to make sense.
120838a52bd3SEd Maste */
120938a52bd3SEd Maste if (pin == NULL && fido_dev_is_winhello (sk->dev) &&
121038a52bd3SEd Maste (r = fido_assert_set_uv(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
121138a52bd3SEd Maste skdebug(__func__, "fido_assert_set_uv: %s", fido_strerr(r));
121238a52bd3SEd Maste }
12131323ec57SEd Maste if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD)) {
12141323ec57SEd Maste if (check_sk_options(sk->dev, "uv", &internal_uv) < 0 ||
12151323ec57SEd Maste internal_uv != 1) {
12161323ec57SEd Maste skdebug(__func__, "check_sk_options uv");
12171323ec57SEd Maste ret = SSH_SK_ERR_PIN_REQUIRED;
121819261079SEd Maste goto out;
121919261079SEd Maste }
12201323ec57SEd Maste if ((r = fido_assert_set_uv(assert,
12211323ec57SEd Maste FIDO_OPT_TRUE)) != FIDO_OK) {
12221323ec57SEd Maste skdebug(__func__, "fido_assert_set_uv: %s",
12231323ec57SEd Maste fido_strerr(r));
12241323ec57SEd Maste ret = fidoerr_to_skerr(r);
12251323ec57SEd Maste goto out;
12261323ec57SEd Maste }
12271323ec57SEd Maste }
122819261079SEd Maste if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) {
122919261079SEd Maste skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
123019261079SEd Maste ret = fidoerr_to_skerr(r);
123119261079SEd Maste goto out;
123219261079SEd Maste }
123319261079SEd Maste if ((response = calloc(1, sizeof(*response))) == NULL) {
123419261079SEd Maste skdebug(__func__, "calloc response failed");
123519261079SEd Maste goto out;
123619261079SEd Maste }
123719261079SEd Maste response->flags = fido_assert_flags(assert, 0);
123819261079SEd Maste response->counter = fido_assert_sigcount(assert, 0);
123919261079SEd Maste if (pack_sig(alg, assert, response) != 0) {
124019261079SEd Maste skdebug(__func__, "pack_sig failed");
124119261079SEd Maste goto out;
124219261079SEd Maste }
124319261079SEd Maste *sign_response = response;
124419261079SEd Maste response = NULL;
124519261079SEd Maste ret = 0;
124619261079SEd Maste out:
124719261079SEd Maste free(device);
124819261079SEd Maste if (response != NULL) {
124919261079SEd Maste free(response->sig_r);
125019261079SEd Maste free(response->sig_s);
125119261079SEd Maste free(response);
125219261079SEd Maste }
125319261079SEd Maste sk_close(sk);
125419261079SEd Maste fido_assert_free(&assert);
125519261079SEd Maste return ret;
125619261079SEd Maste }
125719261079SEd Maste
125819261079SEd Maste static int
read_rks(struct sk_usbhid * sk,const char * pin,struct sk_resident_key *** rksp,size_t * nrksp)125919261079SEd Maste read_rks(struct sk_usbhid *sk, const char *pin,
126019261079SEd Maste struct sk_resident_key ***rksp, size_t *nrksp)
126119261079SEd Maste {
12621323ec57SEd Maste int ret = SSH_SK_ERR_GENERAL, r = -1, internal_uv;
126319261079SEd Maste fido_credman_metadata_t *metadata = NULL;
126419261079SEd Maste fido_credman_rp_t *rp = NULL;
126519261079SEd Maste fido_credman_rk_t *rk = NULL;
12661323ec57SEd Maste size_t i, j, nrp, nrk, user_id_len;
126719261079SEd Maste const fido_cred_t *cred;
12681323ec57SEd Maste const char *rp_id, *rp_name, *user_name;
126919261079SEd Maste struct sk_resident_key *srk = NULL, **tmp;
12701323ec57SEd Maste const u_char *user_id;
127119261079SEd Maste
127219261079SEd Maste if (pin == NULL) {
127319261079SEd Maste skdebug(__func__, "no PIN specified");
127419261079SEd Maste ret = SSH_SK_ERR_PIN_REQUIRED;
127519261079SEd Maste goto out;
127619261079SEd Maste }
127719261079SEd Maste if ((metadata = fido_credman_metadata_new()) == NULL) {
127819261079SEd Maste skdebug(__func__, "alloc failed");
127919261079SEd Maste goto out;
128019261079SEd Maste }
12811323ec57SEd Maste if (check_sk_options(sk->dev, "uv", &internal_uv) != 0) {
12821323ec57SEd Maste skdebug(__func__, "check_sk_options failed");
12831323ec57SEd Maste goto out;
12841323ec57SEd Maste }
128519261079SEd Maste
128619261079SEd Maste if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) {
128719261079SEd Maste if (r == FIDO_ERR_INVALID_COMMAND) {
128819261079SEd Maste skdebug(__func__, "device %s does not support "
128919261079SEd Maste "resident keys", sk->path);
129019261079SEd Maste ret = 0;
129119261079SEd Maste goto out;
129219261079SEd Maste }
129319261079SEd Maste skdebug(__func__, "get metadata for %s failed: %s",
129419261079SEd Maste sk->path, fido_strerr(r));
129519261079SEd Maste ret = fidoerr_to_skerr(r);
129619261079SEd Maste goto out;
129719261079SEd Maste }
129819261079SEd Maste skdebug(__func__, "existing %llu, remaining %llu",
129919261079SEd Maste (unsigned long long)fido_credman_rk_existing(metadata),
130019261079SEd Maste (unsigned long long)fido_credman_rk_remaining(metadata));
130119261079SEd Maste if ((rp = fido_credman_rp_new()) == NULL) {
130219261079SEd Maste skdebug(__func__, "alloc rp failed");
130319261079SEd Maste goto out;
130419261079SEd Maste }
130519261079SEd Maste if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) {
130619261079SEd Maste skdebug(__func__, "get RPs for %s failed: %s",
130719261079SEd Maste sk->path, fido_strerr(r));
130819261079SEd Maste goto out;
130919261079SEd Maste }
131019261079SEd Maste nrp = fido_credman_rp_count(rp);
131119261079SEd Maste skdebug(__func__, "Device %s has resident keys for %zu RPs",
131219261079SEd Maste sk->path, nrp);
131319261079SEd Maste
131419261079SEd Maste /* Iterate over RP IDs that have resident keys */
131519261079SEd Maste for (i = 0; i < nrp; i++) {
13161323ec57SEd Maste rp_id = fido_credman_rp_id(rp, i);
13171323ec57SEd Maste rp_name = fido_credman_rp_name(rp, i);
131819261079SEd Maste skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
13191323ec57SEd Maste i, rp_name == NULL ? "(none)" : rp_name,
13201323ec57SEd Maste rp_id == NULL ? "(none)" : rp_id,
132119261079SEd Maste fido_credman_rp_id_hash_len(rp, i));
132219261079SEd Maste
132319261079SEd Maste /* Skip non-SSH RP IDs */
13241323ec57SEd Maste if (rp_id == NULL ||
13251323ec57SEd Maste strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
132619261079SEd Maste continue;
132719261079SEd Maste
132819261079SEd Maste fido_credman_rk_free(&rk);
132919261079SEd Maste if ((rk = fido_credman_rk_new()) == NULL) {
133019261079SEd Maste skdebug(__func__, "alloc rk failed");
133119261079SEd Maste goto out;
133219261079SEd Maste }
133319261079SEd Maste if ((r = fido_credman_get_dev_rk(sk->dev,
133419261079SEd Maste fido_credman_rp_id(rp, i), rk, pin)) != 0) {
133519261079SEd Maste skdebug(__func__, "get RKs for %s slot %zu failed: %s",
133619261079SEd Maste sk->path, i, fido_strerr(r));
133719261079SEd Maste goto out;
133819261079SEd Maste }
133919261079SEd Maste nrk = fido_credman_rk_count(rk);
134019261079SEd Maste skdebug(__func__, "RP \"%s\" has %zu resident keys",
134119261079SEd Maste fido_credman_rp_id(rp, i), nrk);
134219261079SEd Maste
134319261079SEd Maste /* Iterate over resident keys for this RP ID */
134419261079SEd Maste for (j = 0; j < nrk; j++) {
134519261079SEd Maste if ((cred = fido_credman_rk(rk, j)) == NULL) {
134619261079SEd Maste skdebug(__func__, "no RK in slot %zu", j);
134719261079SEd Maste continue;
134819261079SEd Maste }
13491323ec57SEd Maste if ((user_name = fido_cred_user_name(cred)) == NULL)
13501323ec57SEd Maste user_name = "";
13511323ec57SEd Maste user_id = fido_cred_user_id_ptr(cred);
13521323ec57SEd Maste user_id_len = fido_cred_user_id_len(cred);
13531323ec57SEd Maste skdebug(__func__, "Device %s RP \"%s\" user \"%s\" "
13541323ec57SEd Maste "uidlen %zu slot %zu: type %d flags 0x%02x "
13551323ec57SEd Maste "prot 0x%02x", sk->path, rp_id, user_name,
13561323ec57SEd Maste user_id_len, j, fido_cred_type(cred),
135719261079SEd Maste fido_cred_flags(cred), fido_cred_prot(cred));
135819261079SEd Maste
135919261079SEd Maste /* build response entry */
136019261079SEd Maste if ((srk = calloc(1, sizeof(*srk))) == NULL ||
136119261079SEd Maste (srk->key.key_handle = calloc(1,
136219261079SEd Maste fido_cred_id_len(cred))) == NULL ||
13631323ec57SEd Maste (srk->application = strdup(rp_id)) == NULL ||
13641323ec57SEd Maste (user_id_len > 0 &&
13651323ec57SEd Maste (srk->user_id = calloc(1, user_id_len)) == NULL)) {
136619261079SEd Maste skdebug(__func__, "alloc sk_resident_key");
136719261079SEd Maste goto out;
136819261079SEd Maste }
136919261079SEd Maste
137019261079SEd Maste srk->key.key_handle_len = fido_cred_id_len(cred);
137119261079SEd Maste memcpy(srk->key.key_handle, fido_cred_id_ptr(cred),
137219261079SEd Maste srk->key.key_handle_len);
13731323ec57SEd Maste srk->user_id_len = user_id_len;
13741323ec57SEd Maste if (srk->user_id_len != 0)
13751323ec57SEd Maste memcpy(srk->user_id, user_id, srk->user_id_len);
137619261079SEd Maste
137719261079SEd Maste switch (fido_cred_type(cred)) {
137819261079SEd Maste case COSE_ES256:
137919261079SEd Maste srk->alg = SSH_SK_ECDSA;
138019261079SEd Maste break;
138119261079SEd Maste case COSE_EDDSA:
138219261079SEd Maste srk->alg = SSH_SK_ED25519;
138319261079SEd Maste break;
138419261079SEd Maste default:
138519261079SEd Maste skdebug(__func__, "unsupported key type %d",
138619261079SEd Maste fido_cred_type(cred));
138719261079SEd Maste goto out; /* XXX free rk and continue */
138819261079SEd Maste }
138919261079SEd Maste
13901323ec57SEd Maste if (fido_cred_prot(cred) == FIDO_CRED_PROT_UV_REQUIRED
13911323ec57SEd Maste && internal_uv == -1)
139219261079SEd Maste srk->flags |= SSH_SK_USER_VERIFICATION_REQD;
139319261079SEd Maste
139419261079SEd Maste if ((r = pack_public_key(srk->alg, cred,
139519261079SEd Maste &srk->key)) != 0) {
139619261079SEd Maste skdebug(__func__, "pack public key failed");
139719261079SEd Maste goto out;
139819261079SEd Maste }
139919261079SEd Maste /* append */
140019261079SEd Maste if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
140119261079SEd Maste sizeof(**rksp))) == NULL) {
140219261079SEd Maste skdebug(__func__, "alloc rksp");
140319261079SEd Maste goto out;
140419261079SEd Maste }
140519261079SEd Maste *rksp = tmp;
140619261079SEd Maste (*rksp)[(*nrksp)++] = srk;
140719261079SEd Maste srk = NULL;
140819261079SEd Maste }
140919261079SEd Maste }
141019261079SEd Maste /* Success */
141119261079SEd Maste ret = 0;
141219261079SEd Maste out:
141319261079SEd Maste if (srk != NULL) {
141419261079SEd Maste free(srk->application);
141519261079SEd Maste freezero(srk->key.public_key, srk->key.public_key_len);
141619261079SEd Maste freezero(srk->key.key_handle, srk->key.key_handle_len);
14171323ec57SEd Maste freezero(srk->user_id, srk->user_id_len);
141819261079SEd Maste freezero(srk, sizeof(*srk));
141919261079SEd Maste }
142019261079SEd Maste fido_credman_rp_free(&rp);
142119261079SEd Maste fido_credman_rk_free(&rk);
142219261079SEd Maste fido_credman_metadata_free(&metadata);
142319261079SEd Maste return ret;
142419261079SEd Maste }
142519261079SEd Maste
142619261079SEd Maste int
sk_load_resident_keys(const char * pin,struct sk_option ** options,struct sk_resident_key *** rksp,size_t * nrksp)142719261079SEd Maste sk_load_resident_keys(const char *pin, struct sk_option **options,
142819261079SEd Maste struct sk_resident_key ***rksp, size_t *nrksp)
142919261079SEd Maste {
143019261079SEd Maste int ret = SSH_SK_ERR_GENERAL, r = -1;
143119261079SEd Maste size_t i, nrks = 0;
143219261079SEd Maste struct sk_resident_key **rks = NULL;
143319261079SEd Maste struct sk_usbhid *sk = NULL;
143419261079SEd Maste char *device = NULL;
143519261079SEd Maste
143619261079SEd Maste *rksp = NULL;
143719261079SEd Maste *nrksp = 0;
143819261079SEd Maste
143919261079SEd Maste fido_init(SSH_FIDO_INIT_ARG);
144019261079SEd Maste
144119261079SEd Maste if (check_sign_load_resident_options(options, &device) != 0)
144219261079SEd Maste goto out; /* error already logged */
144319261079SEd Maste if (device != NULL)
144419261079SEd Maste sk = sk_open(device);
144519261079SEd Maste else
144638a52bd3SEd Maste sk = sk_probe(NULL, NULL, 0, 1);
144719261079SEd Maste if (sk == NULL) {
14481323ec57SEd Maste ret = SSH_SK_ERR_DEVICE_NOT_FOUND;
144919261079SEd Maste skdebug(__func__, "failed to find sk");
145019261079SEd Maste goto out;
145119261079SEd Maste }
145219261079SEd Maste skdebug(__func__, "trying %s", sk->path);
145319261079SEd Maste if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) {
145419261079SEd Maste skdebug(__func__, "read_rks failed for %s", sk->path);
145519261079SEd Maste ret = r;
145619261079SEd Maste goto out;
145719261079SEd Maste }
145819261079SEd Maste /* success, unless we have no keys but a specific error */
145919261079SEd Maste if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
146019261079SEd Maste ret = 0;
146119261079SEd Maste *rksp = rks;
146219261079SEd Maste *nrksp = nrks;
146319261079SEd Maste rks = NULL;
146419261079SEd Maste nrks = 0;
146519261079SEd Maste out:
146619261079SEd Maste sk_close(sk);
146719261079SEd Maste for (i = 0; i < nrks; i++) {
146819261079SEd Maste free(rks[i]->application);
146919261079SEd Maste freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
147019261079SEd Maste freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
14711323ec57SEd Maste freezero(rks[i]->user_id, rks[i]->user_id_len);
147219261079SEd Maste freezero(rks[i], sizeof(*rks[i]));
147319261079SEd Maste }
147438a52bd3SEd Maste free(device);
147519261079SEd Maste free(rks);
147619261079SEd Maste return ret;
147719261079SEd Maste }
147819261079SEd Maste
147919261079SEd Maste #endif /* ENABLE_SK_INTERNAL */
1480