xref: /netbsd-src/crypto/external/bsd/openssh/dist/ssh-sk.c (revision 9469f4f13c84743995b7d51c506f9c9849ba30de)
1*9469f4f1Schristos /*	$NetBSD: ssh-sk.c,v 1.9 2024/09/24 21:32:19 christos Exp $	*/
2*9469f4f1Schristos /* $OpenBSD: ssh-sk.c,v 1.41 2024/08/15 00:51:51 djm Exp $ */
3e160b4e8Schristos 
418504831Schristos /*
518504831Schristos  * Copyright (c) 2019 Google LLC
618504831Schristos  *
718504831Schristos  * Permission to use, copy, modify, and distribute this software for any
818504831Schristos  * purpose with or without fee is hereby granted, provided that the above
918504831Schristos  * copyright notice and this permission notice appear in all copies.
1018504831Schristos  *
1118504831Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1218504831Schristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1318504831Schristos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1418504831Schristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1518504831Schristos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1618504831Schristos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1718504831Schristos  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1818504831Schristos  */
19ed75d7a8Schristos #include "includes.h"
20*9469f4f1Schristos __RCSID("$NetBSD: ssh-sk.c,v 1.9 2024/09/24 21:32:19 christos Exp $");
2118504831Schristos 
2218504831Schristos /* #define DEBUG_SK 1 */
2318504831Schristos 
2418504831Schristos #include <dlfcn.h>
2518504831Schristos #include <stddef.h>
2618504831Schristos #include <stdint.h>
2718504831Schristos #include <string.h>
2818504831Schristos #include <stdio.h>
2918504831Schristos 
3018504831Schristos #ifdef WITH_OPENSSL
3118504831Schristos #include <openssl/objects.h>
3218504831Schristos #include <openssl/ec.h>
33*9469f4f1Schristos #include <openssl/evp.h>
3418504831Schristos #endif /* WITH_OPENSSL */
3518504831Schristos 
3618504831Schristos #include "log.h"
3718504831Schristos #include "misc.h"
3818504831Schristos #include "sshbuf.h"
3918504831Schristos #include "sshkey.h"
4018504831Schristos #include "ssherr.h"
4118504831Schristos #include "digest.h"
4218504831Schristos 
4318504831Schristos #include "ssh-sk.h"
4418504831Schristos #include "sk-api.h"
4518504831Schristos #include "crypto_api.h"
4618504831Schristos 
4718504831Schristos struct sshsk_provider {
4818504831Schristos 	char *path;
4918504831Schristos 	void *dlhandle;
5018504831Schristos 
5118504831Schristos 	/* Return the version of the middleware API */
5218504831Schristos 	uint32_t (*sk_api_version)(void);
5318504831Schristos 
5418504831Schristos 	/* Enroll a U2F key (private key generation) */
5518504831Schristos 	int (*sk_enroll)(int alg, const uint8_t *challenge,
5618504831Schristos 	    size_t challenge_len, const char *application, uint8_t flags,
5718504831Schristos 	    const char *pin, struct sk_option **opts,
5818504831Schristos 	    struct sk_enroll_response **enroll_response);
5918504831Schristos 
6018504831Schristos 	/* Sign a challenge */
6118504831Schristos 	int (*sk_sign)(int alg, const uint8_t *message, size_t message_len,
6218504831Schristos 	    const char *application,
6318504831Schristos 	    const uint8_t *key_handle, size_t key_handle_len,
6418504831Schristos 	    uint8_t flags, const char *pin, struct sk_option **opts,
6518504831Schristos 	    struct sk_sign_response **sign_response);
6618504831Schristos 
6718504831Schristos 	/* Enumerate resident keys */
6818504831Schristos 	int (*sk_load_resident_keys)(const char *pin, struct sk_option **opts,
6918504831Schristos 	    struct sk_resident_key ***rks, size_t *nrks);
7018504831Schristos };
7118504831Schristos 
7218504831Schristos /* Built-in version */
7318504831Schristos int ssh_sk_enroll(int alg, const uint8_t *challenge,
7418504831Schristos     size_t challenge_len, const char *application, uint8_t flags,
7518504831Schristos     const char *pin, struct sk_option **opts,
7618504831Schristos     struct sk_enroll_response **enroll_response);
7718504831Schristos int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len,
7818504831Schristos     const char *application,
7918504831Schristos     const uint8_t *key_handle, size_t key_handle_len,
8018504831Schristos     uint8_t flags, const char *pin, struct sk_option **opts,
8118504831Schristos     struct sk_sign_response **sign_response);
8218504831Schristos int ssh_sk_load_resident_keys(const char *pin, struct sk_option **opts,
8318504831Schristos     struct sk_resident_key ***rks, size_t *nrks);
8418504831Schristos 
8518504831Schristos static void
8618504831Schristos sshsk_free(struct sshsk_provider *p)
8718504831Schristos {
8818504831Schristos 	if (p == NULL)
8918504831Schristos 		return;
9018504831Schristos 	free(p->path);
9118504831Schristos 	if (p->dlhandle != NULL)
9218504831Schristos 		dlclose(p->dlhandle);
9318504831Schristos 	free(p);
9418504831Schristos }
9518504831Schristos 
9618504831Schristos static struct sshsk_provider *
9718504831Schristos sshsk_open(const char *path)
9818504831Schristos {
9918504831Schristos 	struct sshsk_provider *ret = NULL;
10018504831Schristos 	uint32_t version;
10118504831Schristos 
1028db691beSchristos 	if (path == NULL || *path == '\0') {
1038db691beSchristos 		error("No FIDO SecurityKeyProvider specified");
1048db691beSchristos 		return NULL;
1058db691beSchristos 	}
10618504831Schristos 	if ((ret = calloc(1, sizeof(*ret))) == NULL) {
10717418e98Schristos 		error_f("calloc failed");
10818504831Schristos 		return NULL;
10918504831Schristos 	}
11018504831Schristos 	if ((ret->path = strdup(path)) == NULL) {
11117418e98Schristos 		error_f("strdup failed");
11218504831Schristos 		goto fail;
11318504831Schristos 	}
11418504831Schristos 	/* Skip the rest if we're using the linked in middleware */
11518504831Schristos 	if (strcasecmp(ret->path, "internal") == 0) {
11618504831Schristos 		ret->sk_enroll = ssh_sk_enroll;
11718504831Schristos 		ret->sk_sign = ssh_sk_sign;
11818504831Schristos 		ret->sk_load_resident_keys = ssh_sk_load_resident_keys;
11918504831Schristos 		return ret;
12018504831Schristos 	}
121a629fefcSchristos 	if (lib_contains_symbol(path, "sk_api_version") != 0) {
122a629fefcSchristos 		error("provider %s is not an OpenSSH FIDO library", path);
123a629fefcSchristos 		goto fail;
124a629fefcSchristos 	}
12518504831Schristos 	if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) {
12618504831Schristos 		error("Provider \"%s\" dlopen failed: %s", path, dlerror());
12718504831Schristos 		goto fail;
12818504831Schristos 	}
12918504831Schristos 	if ((ret->sk_api_version = dlsym(ret->dlhandle,
13018504831Schristos 	    "sk_api_version")) == NULL) {
131a629fefcSchristos 		fatal("Provider \"%s\" dlsym(sk_api_version) failed: %s",
13218504831Schristos 		    path, dlerror());
13318504831Schristos 	}
13418504831Schristos 	version = ret->sk_api_version();
13517418e98Schristos 	debug_f("provider %s implements version 0x%08lx", ret->path,
13617418e98Schristos 	    (u_long)version);
13718504831Schristos 	if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) {
13818504831Schristos 		error("Provider \"%s\" implements unsupported "
13918504831Schristos 		    "version 0x%08lx (supported: 0x%08lx)",
14018504831Schristos 		    path, (u_long)version, (u_long)SSH_SK_VERSION_MAJOR);
14118504831Schristos 		goto fail;
14218504831Schristos 	}
14318504831Schristos 	if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) {
14418504831Schristos 		error("Provider %s dlsym(sk_enroll) failed: %s",
14518504831Schristos 		    path, dlerror());
14618504831Schristos 		goto fail;
14718504831Schristos 	}
14818504831Schristos 	if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) {
14918504831Schristos 		error("Provider \"%s\" dlsym(sk_sign) failed: %s",
15018504831Schristos 		    path, dlerror());
15118504831Schristos 		goto fail;
15218504831Schristos 	}
15318504831Schristos 	if ((ret->sk_load_resident_keys = dlsym(ret->dlhandle,
15418504831Schristos 	    "sk_load_resident_keys")) == NULL) {
15518504831Schristos 		error("Provider \"%s\" dlsym(sk_load_resident_keys) "
15618504831Schristos 		    "failed: %s", path, dlerror());
15718504831Schristos 		goto fail;
15818504831Schristos 	}
15918504831Schristos 	/* success */
16018504831Schristos 	return ret;
16118504831Schristos fail:
16218504831Schristos 	sshsk_free(ret);
16318504831Schristos 	return NULL;
16418504831Schristos }
16518504831Schristos 
16618504831Schristos static void
16718504831Schristos sshsk_free_enroll_response(struct sk_enroll_response *r)
16818504831Schristos {
16918504831Schristos 	if (r == NULL)
17018504831Schristos 		return;
17118504831Schristos 	freezero(r->key_handle, r->key_handle_len);
17218504831Schristos 	freezero(r->public_key, r->public_key_len);
17318504831Schristos 	freezero(r->signature, r->signature_len);
17418504831Schristos 	freezero(r->attestation_cert, r->attestation_cert_len);
1752d3b0f52Schristos 	freezero(r->authdata, r->authdata_len);
17618504831Schristos 	freezero(r, sizeof(*r));
17718504831Schristos }
17818504831Schristos 
17918504831Schristos static void
18018504831Schristos sshsk_free_sign_response(struct sk_sign_response *r)
18118504831Schristos {
18218504831Schristos 	if (r == NULL)
18318504831Schristos 		return;
18418504831Schristos 	freezero(r->sig_r, r->sig_r_len);
18518504831Schristos 	freezero(r->sig_s, r->sig_s_len);
18618504831Schristos 	freezero(r, sizeof(*r));
18718504831Schristos }
18818504831Schristos 
18918504831Schristos #ifdef WITH_OPENSSL
19018504831Schristos /* Assemble key from response */
19118504831Schristos static int
19218504831Schristos sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
19318504831Schristos {
19418504831Schristos 	struct sshkey *key = NULL;
19518504831Schristos 	struct sshbuf *b = NULL;
196*9469f4f1Schristos 	EC_KEY *ecdsa = NULL;
19718504831Schristos 	EC_POINT *q = NULL;
198*9469f4f1Schristos 	const EC_GROUP *g = NULL;
19918504831Schristos 	int r;
20018504831Schristos 
20118504831Schristos 	*keyp = NULL;
20218504831Schristos 	if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) {
20317418e98Schristos 		error_f("sshkey_new failed");
20418504831Schristos 		r = SSH_ERR_ALLOC_FAIL;
20518504831Schristos 		goto out;
20618504831Schristos 	}
20718504831Schristos 	key->ecdsa_nid = NID_X9_62_prime256v1;
208*9469f4f1Schristos 	if ((ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL ||
209*9469f4f1Schristos 	    (g = EC_KEY_get0_group(ecdsa)) == NULL ||
210*9469f4f1Schristos 	    (q = EC_POINT_new(g)) == NULL ||
21118504831Schristos 	    (b = sshbuf_new()) == NULL) {
21217418e98Schristos 		error_f("allocation failed");
21318504831Schristos 		r = SSH_ERR_ALLOC_FAIL;
21418504831Schristos 		goto out;
21518504831Schristos 	}
21618504831Schristos 	if ((r = sshbuf_put_string(b,
21718504831Schristos 	    resp->public_key, resp->public_key_len)) != 0) {
21817418e98Schristos 		error_fr(r, "sshbuf_put_string");
21918504831Schristos 		goto out;
22018504831Schristos 	}
221*9469f4f1Schristos 	if ((r = sshbuf_get_ec(b, q, g)) != 0) {
22217418e98Schristos 		error_fr(r, "parse");
22318504831Schristos 		r = SSH_ERR_INVALID_FORMAT;
22418504831Schristos 		goto out;
22518504831Schristos 	}
226*9469f4f1Schristos 	if (sshkey_ec_validate_public(g, q) != 0) {
22718504831Schristos 		error("Authenticator returned invalid ECDSA key");
22818504831Schristos 		r = SSH_ERR_KEY_INVALID_EC_VALUE;
22918504831Schristos 		goto out;
23018504831Schristos 	}
231*9469f4f1Schristos 	if (EC_KEY_set_public_key(ecdsa, q) != 1) {
23218504831Schristos 		/* XXX assume it is a allocation error */
23317418e98Schristos 		error_f("allocation failed");
23418504831Schristos 		r = SSH_ERR_ALLOC_FAIL;
23518504831Schristos 		goto out;
23618504831Schristos 	}
237*9469f4f1Schristos 	if ((key->pkey = EVP_PKEY_new()) == NULL) {
238*9469f4f1Schristos 		error_f("allocation failed");
239*9469f4f1Schristos 		r = SSH_ERR_ALLOC_FAIL;
240*9469f4f1Schristos 		goto out;
241*9469f4f1Schristos 	}
242*9469f4f1Schristos 	if (EVP_PKEY_set1_EC_KEY(key->pkey, ecdsa) != 1) {
243*9469f4f1Schristos 		error_f("Assigning EC_KEY failed");
244*9469f4f1Schristos 		r = SSH_ERR_LIBCRYPTO_ERROR;
245*9469f4f1Schristos 		goto out;
246*9469f4f1Schristos 	}
24718504831Schristos 	/* success */
24818504831Schristos 	*keyp = key;
24918504831Schristos 	key = NULL; /* transferred */
25018504831Schristos 	r = 0;
25118504831Schristos  out:
25218504831Schristos 	sshkey_free(key);
25318504831Schristos 	sshbuf_free(b);
254*9469f4f1Schristos 	EC_KEY_free(ecdsa);
255*9469f4f1Schristos 	EC_POINT_free(q);
25618504831Schristos 	return r;
25718504831Schristos }
25818504831Schristos #endif /* WITH_OPENSSL */
25918504831Schristos 
26018504831Schristos static int
26118504831Schristos sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
26218504831Schristos {
26318504831Schristos 	struct sshkey *key = NULL;
26418504831Schristos 	int r;
26518504831Schristos 
26618504831Schristos 	*keyp = NULL;
26718504831Schristos 	if (resp->public_key_len != ED25519_PK_SZ) {
26817418e98Schristos 		error_f("invalid size: %zu", resp->public_key_len);
26918504831Schristos 		r = SSH_ERR_INVALID_FORMAT;
27018504831Schristos 		goto out;
27118504831Schristos 	}
27218504831Schristos 	if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) {
27317418e98Schristos 		error_f("sshkey_new failed");
27418504831Schristos 		r = SSH_ERR_ALLOC_FAIL;
27518504831Schristos 		goto out;
27618504831Schristos 	}
27718504831Schristos 	if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
27817418e98Schristos 		error_f("malloc failed");
27918504831Schristos 		r = SSH_ERR_ALLOC_FAIL;
28018504831Schristos 		goto out;
28118504831Schristos 	}
28218504831Schristos 	memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ);
28318504831Schristos 	/* success */
28418504831Schristos 	*keyp = key;
28518504831Schristos 	key = NULL; /* transferred */
28618504831Schristos 	r = 0;
28718504831Schristos  out:
28818504831Schristos 	sshkey_free(key);
28918504831Schristos 	return r;
29018504831Schristos }
29118504831Schristos 
29218504831Schristos static int
29318504831Schristos sshsk_key_from_response(int alg, const char *application, uint8_t flags,
29418504831Schristos     struct sk_enroll_response *resp, struct sshkey **keyp)
29518504831Schristos {
29618504831Schristos 	struct sshkey *key = NULL;
29718504831Schristos 	int r = SSH_ERR_INTERNAL_ERROR;
29818504831Schristos 
29918504831Schristos 	*keyp = NULL;
30018504831Schristos 
30118504831Schristos 	/* Check response validity */
30218504831Schristos 	if (resp->public_key == NULL || resp->key_handle == NULL) {
30317418e98Schristos 		error_f("sk_enroll response invalid");
30418504831Schristos 		r = SSH_ERR_INVALID_FORMAT;
30518504831Schristos 		goto out;
30618504831Schristos 	}
30718504831Schristos 	switch (alg) {
30818504831Schristos #ifdef WITH_OPENSSL
30918504831Schristos 	case SSH_SK_ECDSA:
31018504831Schristos 		if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0)
31118504831Schristos 			goto out;
31218504831Schristos 		break;
31318504831Schristos #endif /* WITH_OPENSSL */
31418504831Schristos 	case SSH_SK_ED25519:
31518504831Schristos 		if ((r = sshsk_ed25519_assemble(resp, &key)) != 0)
31618504831Schristos 			goto out;
31718504831Schristos 		break;
31818504831Schristos 	default:
31917418e98Schristos 		error_f("unsupported algorithm %d", alg);
32018504831Schristos 		r = SSH_ERR_INVALID_ARGUMENT;
32118504831Schristos 		goto out;
32218504831Schristos 	}
32318504831Schristos 	key->sk_flags = flags;
32418504831Schristos 	if ((key->sk_key_handle = sshbuf_new()) == NULL ||
32518504831Schristos 	    (key->sk_reserved = sshbuf_new()) == NULL) {
32617418e98Schristos 		error_f("allocation failed");
32718504831Schristos 		r = SSH_ERR_ALLOC_FAIL;
32818504831Schristos 		goto out;
32918504831Schristos 	}
33018504831Schristos 	if ((key->sk_application = strdup(application)) == NULL) {
33117418e98Schristos 		error_f("strdup application failed");
33218504831Schristos 		r = SSH_ERR_ALLOC_FAIL;
33318504831Schristos 		goto out;
33418504831Schristos 	}
33518504831Schristos 	if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle,
33618504831Schristos 	    resp->key_handle_len)) != 0) {
33717418e98Schristos 		error_fr(r, "put key handle");
33818504831Schristos 		goto out;
33918504831Schristos 	}
34018504831Schristos 	/* success */
34118504831Schristos 	r = 0;
34218504831Schristos 	*keyp = key;
34318504831Schristos 	key = NULL;
34418504831Schristos  out:
34518504831Schristos 	sshkey_free(key);
34618504831Schristos 	return r;
34718504831Schristos }
34818504831Schristos 
34918504831Schristos static int
35018504831Schristos skerr_to_ssherr(int skerr)
35118504831Schristos {
35218504831Schristos 	switch (skerr) {
35318504831Schristos 	case SSH_SK_ERR_UNSUPPORTED:
35418504831Schristos 		return SSH_ERR_FEATURE_UNSUPPORTED;
35518504831Schristos 	case SSH_SK_ERR_PIN_REQUIRED:
35618504831Schristos 		return SSH_ERR_KEY_WRONG_PASSPHRASE;
35718504831Schristos 	case SSH_SK_ERR_DEVICE_NOT_FOUND:
35818504831Schristos 		return SSH_ERR_DEVICE_NOT_FOUND;
359e160b4e8Schristos 	case SSH_SK_ERR_CREDENTIAL_EXISTS:
360e160b4e8Schristos 		return SSH_ERR_KEY_BAD_PERMISSIONS;
36118504831Schristos 	case SSH_SK_ERR_GENERAL:
36218504831Schristos 	default:
36318504831Schristos 		return SSH_ERR_INVALID_FORMAT;
36418504831Schristos 	}
36518504831Schristos }
36618504831Schristos 
36718504831Schristos static void
36818504831Schristos sshsk_free_options(struct sk_option **opts)
36918504831Schristos {
37018504831Schristos 	size_t i;
37118504831Schristos 
37218504831Schristos 	if (opts == NULL)
37318504831Schristos 		return;
37418504831Schristos 	for (i = 0; opts[i] != NULL; i++) {
37518504831Schristos 		free(opts[i]->name);
37618504831Schristos 		free(opts[i]->value);
37718504831Schristos 		free(opts[i]);
37818504831Schristos 	}
37918504831Schristos 	free(opts);
38018504831Schristos }
38118504831Schristos 
38218504831Schristos static int
38318504831Schristos sshsk_add_option(struct sk_option ***optsp, size_t *noptsp,
38418504831Schristos     const char *name, const char *value, uint8_t required)
38518504831Schristos {
38618504831Schristos 	struct sk_option **opts = *optsp;
38718504831Schristos 	size_t nopts = *noptsp;
38818504831Schristos 
38918504831Schristos 	if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */
39018504831Schristos 	    sizeof(*opts))) == NULL) {
39117418e98Schristos 		error_f("array alloc failed");
39218504831Schristos 		return SSH_ERR_ALLOC_FAIL;
39318504831Schristos 	}
39418504831Schristos 	*optsp = opts;
39518504831Schristos 	*noptsp = nopts + 1;
39618504831Schristos 	if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) {
39717418e98Schristos 		error_f("alloc failed");
39818504831Schristos 		return SSH_ERR_ALLOC_FAIL;
39918504831Schristos 	}
40018504831Schristos 	if ((opts[nopts]->name = strdup(name)) == NULL ||
40118504831Schristos 	    (opts[nopts]->value = strdup(value)) == NULL) {
40217418e98Schristos 		error_f("alloc failed");
40318504831Schristos 		return SSH_ERR_ALLOC_FAIL;
40418504831Schristos 	}
40518504831Schristos 	opts[nopts]->required = required;
40618504831Schristos 	return 0;
40718504831Schristos }
40818504831Schristos 
40918504831Schristos static int
41018504831Schristos make_options(const char *device, const char *user_id,
41118504831Schristos     struct sk_option ***optsp)
41218504831Schristos {
41318504831Schristos 	struct sk_option **opts = NULL;
41418504831Schristos 	size_t nopts = 0;
41518504831Schristos 	int r, ret = SSH_ERR_INTERNAL_ERROR;
41618504831Schristos 
41718504831Schristos 	if (device != NULL &&
41818504831Schristos 	    (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) {
41918504831Schristos 		ret = r;
42018504831Schristos 		goto out;
42118504831Schristos 	}
42218504831Schristos 	if (user_id != NULL &&
42318504831Schristos 	    (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) {
42418504831Schristos 		ret = r;
42518504831Schristos 		goto out;
42618504831Schristos 	}
42718504831Schristos 	/* success */
42818504831Schristos 	*optsp = opts;
42918504831Schristos 	opts = NULL;
43018504831Schristos 	nopts = 0;
43118504831Schristos 	ret = 0;
43218504831Schristos  out:
43318504831Schristos 	sshsk_free_options(opts);
43418504831Schristos 	return ret;
43518504831Schristos }
43618504831Schristos 
4372d3b0f52Schristos 
4382d3b0f52Schristos static int
4392d3b0f52Schristos fill_attestation_blob(const struct sk_enroll_response *resp,
4402d3b0f52Schristos     struct sshbuf *attest)
4412d3b0f52Schristos {
4422d3b0f52Schristos 	int r;
4432d3b0f52Schristos 
4442d3b0f52Schristos 	if (attest == NULL)
4452d3b0f52Schristos 		return 0; /* nothing to do */
4462d3b0f52Schristos 	if ((r = sshbuf_put_cstring(attest, "ssh-sk-attest-v01")) != 0 ||
4472d3b0f52Schristos 	    (r = sshbuf_put_string(attest,
4482d3b0f52Schristos 	    resp->attestation_cert, resp->attestation_cert_len)) != 0 ||
4492d3b0f52Schristos 	    (r = sshbuf_put_string(attest,
4502d3b0f52Schristos 	    resp->signature, resp->signature_len)) != 0 ||
4512d3b0f52Schristos 	    (r = sshbuf_put_string(attest,
4522d3b0f52Schristos 	    resp->authdata, resp->authdata_len)) != 0 ||
4532d3b0f52Schristos 	    (r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */
4542d3b0f52Schristos 	    (r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) {
45517418e98Schristos 		error_fr(r, "compose");
4562d3b0f52Schristos 		return r;
4572d3b0f52Schristos 	}
4582d3b0f52Schristos 	/* success */
4592d3b0f52Schristos 	return 0;
4602d3b0f52Schristos }
4612d3b0f52Schristos 
46218504831Schristos int
46318504831Schristos sshsk_enroll(int type, const char *provider_path, const char *device,
46418504831Schristos     const char *application, const char *userid, uint8_t flags,
46518504831Schristos     const char *pin, struct sshbuf *challenge_buf,
46618504831Schristos     struct sshkey **keyp, struct sshbuf *attest)
46718504831Schristos {
46818504831Schristos 	struct sshsk_provider *skp = NULL;
46918504831Schristos 	struct sshkey *key = NULL;
47018504831Schristos 	u_char randchall[32];
47118504831Schristos 	const u_char *challenge;
47218504831Schristos 	size_t challenge_len;
47318504831Schristos 	struct sk_enroll_response *resp = NULL;
47418504831Schristos 	struct sk_option **opts = NULL;
47518504831Schristos 	int r = SSH_ERR_INTERNAL_ERROR;
47618504831Schristos 	int alg;
47718504831Schristos 
47817418e98Schristos 	debug_f("provider \"%s\", device \"%s\", application \"%s\", "
47917418e98Schristos 	    "userid \"%s\", flags 0x%02x, challenge len %zu%s",
48018504831Schristos 	    provider_path, device, application, userid, flags,
48118504831Schristos 	    challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf),
48218504831Schristos 	    (pin != NULL && *pin != '\0') ? " with-pin" : "");
48318504831Schristos 
48418504831Schristos 	*keyp = NULL;
48518504831Schristos 	if (attest)
48618504831Schristos 		sshbuf_reset(attest);
48718504831Schristos 
48818504831Schristos 	if ((r = make_options(device, userid, &opts)) != 0)
48918504831Schristos 		goto out;
49018504831Schristos 
49118504831Schristos 	switch (type) {
49218504831Schristos #ifdef WITH_OPENSSL
49318504831Schristos 	case KEY_ECDSA_SK:
49418504831Schristos 		alg = SSH_SK_ECDSA;
49518504831Schristos 		break;
49618504831Schristos #endif /* WITH_OPENSSL */
49718504831Schristos 	case KEY_ED25519_SK:
49818504831Schristos 		alg = SSH_SK_ED25519;
49918504831Schristos 		break;
50018504831Schristos 	default:
50117418e98Schristos 		error_f("unsupported key type");
50218504831Schristos 		r = SSH_ERR_INVALID_ARGUMENT;
50318504831Schristos 		goto out;
50418504831Schristos 	}
50518504831Schristos 	if (provider_path == NULL) {
50617418e98Schristos 		error_f("missing provider");
50718504831Schristos 		r = SSH_ERR_INVALID_ARGUMENT;
50818504831Schristos 		goto out;
50918504831Schristos 	}
51018504831Schristos 	if (application == NULL || *application == '\0') {
51117418e98Schristos 		error_f("missing application");
51218504831Schristos 		r = SSH_ERR_INVALID_ARGUMENT;
51318504831Schristos 		goto out;
51418504831Schristos 	}
51518504831Schristos 	if (challenge_buf == NULL) {
51617418e98Schristos 		debug_f("using random challenge");
51718504831Schristos 		arc4random_buf(randchall, sizeof(randchall));
51818504831Schristos 		challenge = randchall;
51918504831Schristos 		challenge_len = sizeof(randchall);
52018504831Schristos 	} else if (sshbuf_len(challenge_buf) == 0) {
52118504831Schristos 		error("Missing enrollment challenge");
52218504831Schristos 		r = SSH_ERR_INVALID_ARGUMENT;
52318504831Schristos 		goto out;
52418504831Schristos 	} else {
52518504831Schristos 		challenge = sshbuf_ptr(challenge_buf);
52618504831Schristos 		challenge_len = sshbuf_len(challenge_buf);
52717418e98Schristos 		debug3_f("using explicit challenge len=%zd", challenge_len);
52818504831Schristos 	}
52918504831Schristos 	if ((skp = sshsk_open(provider_path)) == NULL) {
53018504831Schristos 		r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
53118504831Schristos 		goto out;
53218504831Schristos 	}
53318504831Schristos 	/* XXX validate flags? */
53418504831Schristos 	/* enroll key */
53518504831Schristos 	if ((r = skp->sk_enroll(alg, challenge, challenge_len, application,
53618504831Schristos 	    flags, pin, opts, &resp)) != 0) {
53717418e98Schristos 		debug_f("provider \"%s\" failure %d", provider_path, r);
53818504831Schristos 		r = skerr_to_ssherr(r);
53918504831Schristos 		goto out;
54018504831Schristos 	}
54118504831Schristos 
542a03ec00cSchristos 	if ((r = sshsk_key_from_response(alg, application, resp->flags,
54318504831Schristos 	    resp, &key)) != 0)
54418504831Schristos 		goto out;
54518504831Schristos 
54618504831Schristos 	/* Optionally fill in the attestation information */
5472d3b0f52Schristos 	if ((r = fill_attestation_blob(resp, attest)) != 0)
54818504831Schristos 		goto out;
5492d3b0f52Schristos 
55018504831Schristos 	/* success */
55118504831Schristos 	*keyp = key;
55218504831Schristos 	key = NULL; /* transferred */
55318504831Schristos 	r = 0;
55418504831Schristos  out:
55518504831Schristos 	sshsk_free_options(opts);
55618504831Schristos 	sshsk_free(skp);
55718504831Schristos 	sshkey_free(key);
55818504831Schristos 	sshsk_free_enroll_response(resp);
55918504831Schristos 	explicit_bzero(randchall, sizeof(randchall));
56018504831Schristos 	return r;
56118504831Schristos }
56218504831Schristos 
56318504831Schristos #ifdef WITH_OPENSSL
56418504831Schristos static int
56518504831Schristos sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig)
56618504831Schristos {
56718504831Schristos 	struct sshbuf *inner_sig = NULL;
56818504831Schristos 	int r = SSH_ERR_INTERNAL_ERROR;
56918504831Schristos 
57018504831Schristos 	/* Check response validity */
57118504831Schristos 	if (resp->sig_r == NULL || resp->sig_s == NULL) {
57217418e98Schristos 		error_f("sk_sign response invalid");
57318504831Schristos 		r = SSH_ERR_INVALID_FORMAT;
57418504831Schristos 		goto out;
57518504831Schristos 	}
57618504831Schristos 	if ((inner_sig = sshbuf_new()) == NULL) {
57718504831Schristos 		r = SSH_ERR_ALLOC_FAIL;
57818504831Schristos 		goto out;
57918504831Schristos 	}
58018504831Schristos 	/* Prepare and append inner signature object */
58118504831Schristos 	if ((r = sshbuf_put_bignum2_bytes(inner_sig,
58218504831Schristos 	    resp->sig_r, resp->sig_r_len)) != 0 ||
58318504831Schristos 	    (r = sshbuf_put_bignum2_bytes(inner_sig,
58418504831Schristos 	    resp->sig_s, resp->sig_s_len)) != 0) {
58517418e98Schristos 		error_fr(r, "compose inner");
58618504831Schristos 		goto out;
58718504831Schristos 	}
58818504831Schristos 	if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 ||
58918504831Schristos 	    (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
59018504831Schristos 	    (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
59117418e98Schristos 		error_fr(r, "compose");
59218504831Schristos 		goto out;
59318504831Schristos 	}
59418504831Schristos #ifdef DEBUG_SK
59518504831Schristos 	fprintf(stderr, "%s: sig_r:\n", __func__);
59618504831Schristos 	sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
59718504831Schristos 	fprintf(stderr, "%s: sig_s:\n", __func__);
59818504831Schristos 	sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr);
59918504831Schristos 	fprintf(stderr, "%s: inner:\n", __func__);
60018504831Schristos 	sshbuf_dump(inner_sig, stderr);
60118504831Schristos #endif
60218504831Schristos 	r = 0;
60318504831Schristos  out:
60418504831Schristos 	sshbuf_free(inner_sig);
60518504831Schristos 	return r;
60618504831Schristos }
60718504831Schristos #endif /* WITH_OPENSSL */
60818504831Schristos 
60918504831Schristos static int
61018504831Schristos sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig)
61118504831Schristos {
61218504831Schristos 	int r = SSH_ERR_INTERNAL_ERROR;
61318504831Schristos 
61418504831Schristos 	/* Check response validity */
61518504831Schristos 	if (resp->sig_r == NULL) {
61617418e98Schristos 		error_f("sk_sign response invalid");
61718504831Schristos 		r = SSH_ERR_INVALID_FORMAT;
61818504831Schristos 		goto out;
61918504831Schristos 	}
62018504831Schristos 	if ((r = sshbuf_put_string(sig,
62118504831Schristos 	    resp->sig_r, resp->sig_r_len)) != 0 ||
62218504831Schristos 	    (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
62318504831Schristos 	    (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
62417418e98Schristos 		error_fr(r, "compose");
62518504831Schristos 		goto out;
62618504831Schristos 	}
62718504831Schristos #ifdef DEBUG_SK
62818504831Schristos 	fprintf(stderr, "%s: sig_r:\n", __func__);
62918504831Schristos 	sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
63018504831Schristos #endif
63118504831Schristos 	r = 0;
63218504831Schristos  out:
6338db691beSchristos 	return r;
63418504831Schristos }
63518504831Schristos 
63618504831Schristos int
63718504831Schristos sshsk_sign(const char *provider_path, struct sshkey *key,
63818504831Schristos     u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
63918504831Schristos     u_int compat, const char *pin)
64018504831Schristos {
64118504831Schristos 	struct sshsk_provider *skp = NULL;
64218504831Schristos 	int r = SSH_ERR_INTERNAL_ERROR;
64318504831Schristos 	int type, alg;
64418504831Schristos 	struct sk_sign_response *resp = NULL;
64518504831Schristos 	struct sshbuf *inner_sig = NULL, *sig = NULL;
64618504831Schristos 	struct sk_option **opts = NULL;
64718504831Schristos 
64817418e98Schristos 	debug_f("provider \"%s\", key %s, flags 0x%02x%s",
64918504831Schristos 	    provider_path, sshkey_type(key), key->sk_flags,
65018504831Schristos 	    (pin != NULL && *pin != '\0') ? " with-pin" : "");
65118504831Schristos 
65218504831Schristos 	if (sigp != NULL)
65318504831Schristos 		*sigp = NULL;
65418504831Schristos 	if (lenp != NULL)
65518504831Schristos 		*lenp = 0;
65618504831Schristos 	type = sshkey_type_plain(key->type);
65718504831Schristos 	switch (type) {
65818504831Schristos #ifdef WITH_OPENSSL
65918504831Schristos 	case KEY_ECDSA_SK:
66018504831Schristos 		alg = SSH_SK_ECDSA;
66118504831Schristos 		break;
66218504831Schristos #endif /* WITH_OPENSSL */
66318504831Schristos 	case KEY_ED25519_SK:
66418504831Schristos 		alg = SSH_SK_ED25519;
66518504831Schristos 		break;
66618504831Schristos 	default:
66718504831Schristos 		return SSH_ERR_INVALID_ARGUMENT;
66818504831Schristos 	}
66918504831Schristos 	if (provider_path == NULL ||
67018504831Schristos 	    key->sk_key_handle == NULL ||
67118504831Schristos 	    key->sk_application == NULL || *key->sk_application == '\0') {
67218504831Schristos 		r = SSH_ERR_INVALID_ARGUMENT;
67318504831Schristos 		goto out;
67418504831Schristos 	}
67518504831Schristos 	if ((skp = sshsk_open(provider_path)) == NULL) {
67618504831Schristos 		r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
67718504831Schristos 		goto out;
67818504831Schristos 	}
67917418e98Schristos #ifdef DEBUG_SK
68017418e98Schristos 	fprintf(stderr, "%s: sk_flags = 0x%02x, sk_application = \"%s\"\n",
68117418e98Schristos 	    __func__, key->sk_flags, key->sk_application);
68217418e98Schristos 	fprintf(stderr, "%s: sk_key_handle:\n", __func__);
68317418e98Schristos 	sshbuf_dump(key->sk_key_handle, stderr);
68417418e98Schristos #endif
6858db691beSchristos 	if ((r = skp->sk_sign(alg, data, datalen, key->sk_application,
68618504831Schristos 	    sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle),
68718504831Schristos 	    key->sk_flags, pin, opts, &resp)) != 0) {
68817418e98Schristos 		debug_f("sk_sign failed with code %d", r);
68918504831Schristos 		r = skerr_to_ssherr(r);
69018504831Schristos 		goto out;
69118504831Schristos 	}
69218504831Schristos 	/* Assemble signature */
69318504831Schristos 	if ((sig = sshbuf_new()) == NULL) {
69418504831Schristos 		r = SSH_ERR_ALLOC_FAIL;
69518504831Schristos 		goto out;
69618504831Schristos 	}
69718504831Schristos 	if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) {
69817418e98Schristos 		error_fr(r, "compose outer");
69918504831Schristos 		goto out;
70018504831Schristos 	}
70118504831Schristos 	switch (type) {
70218504831Schristos #ifdef WITH_OPENSSL
70318504831Schristos 	case KEY_ECDSA_SK:
70418504831Schristos 		if ((r = sshsk_ecdsa_sig(resp, sig)) != 0)
70518504831Schristos 			goto out;
70618504831Schristos 		break;
70718504831Schristos #endif /* WITH_OPENSSL */
70818504831Schristos 	case KEY_ED25519_SK:
70918504831Schristos 		if ((r = sshsk_ed25519_sig(resp, sig)) != 0)
71018504831Schristos 			goto out;
71118504831Schristos 		break;
71218504831Schristos 	}
71318504831Schristos #ifdef DEBUG_SK
71418504831Schristos 	fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
71518504831Schristos 	    __func__, resp->flags, resp->counter);
71617418e98Schristos 	fprintf(stderr, "%s: data to sign:\n", __func__);
71717418e98Schristos 	sshbuf_dump_data(data, datalen, stderr);
71818504831Schristos 	fprintf(stderr, "%s: sigbuf:\n", __func__);
71918504831Schristos 	sshbuf_dump(sig, stderr);
72018504831Schristos #endif
72118504831Schristos 	if (sigp != NULL) {
72218504831Schristos 		if ((*sigp = malloc(sshbuf_len(sig))) == NULL) {
72318504831Schristos 			r = SSH_ERR_ALLOC_FAIL;
72418504831Schristos 			goto out;
72518504831Schristos 		}
72618504831Schristos 		memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig));
72718504831Schristos 	}
72818504831Schristos 	if (lenp != NULL)
72918504831Schristos 		*lenp = sshbuf_len(sig);
73018504831Schristos 	/* success */
73118504831Schristos 	r = 0;
73218504831Schristos  out:
73318504831Schristos 	sshsk_free_options(opts);
73418504831Schristos 	sshsk_free(skp);
73518504831Schristos 	sshsk_free_sign_response(resp);
73618504831Schristos 	sshbuf_free(sig);
73718504831Schristos 	sshbuf_free(inner_sig);
73818504831Schristos 	return r;
73918504831Schristos }
74018504831Schristos 
74118504831Schristos static void
74218504831Schristos sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks)
74318504831Schristos {
74418504831Schristos 	size_t i;
74518504831Schristos 
74618504831Schristos 	if (nrks == 0 || rks == NULL)
74718504831Schristos 		return;
74818504831Schristos 	for (i = 0; i < nrks; i++) {
74918504831Schristos 		free(rks[i]->application);
750a03ec00cSchristos 		freezero(rks[i]->user_id, rks[i]->user_id_len);
75118504831Schristos 		freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
75218504831Schristos 		freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
75318504831Schristos 		freezero(rks[i]->key.signature, rks[i]->key.signature_len);
75418504831Schristos 		freezero(rks[i]->key.attestation_cert,
75518504831Schristos 		    rks[i]->key.attestation_cert_len);
75618504831Schristos 		freezero(rks[i], sizeof(**rks));
75718504831Schristos 	}
75818504831Schristos 	free(rks);
75918504831Schristos }
76018504831Schristos 
761a03ec00cSchristos static void
762a03ec00cSchristos sshsk_free_resident_key(struct sshsk_resident_key *srk)
763a03ec00cSchristos {
764a03ec00cSchristos 	if (srk == NULL)
765a03ec00cSchristos 		return;
766a03ec00cSchristos 	sshkey_free(srk->key);
767a03ec00cSchristos 	freezero(srk->user_id, srk->user_id_len);
768a03ec00cSchristos 	free(srk);
769a03ec00cSchristos }
770a03ec00cSchristos 
771a03ec00cSchristos 
772a03ec00cSchristos void
773a03ec00cSchristos sshsk_free_resident_keys(struct sshsk_resident_key **srks, size_t nsrks)
774a03ec00cSchristos {
775a03ec00cSchristos 	size_t i;
776a03ec00cSchristos 
777a03ec00cSchristos 	if (srks == NULL || nsrks == 0)
778a03ec00cSchristos 		return;
779a03ec00cSchristos 
780a03ec00cSchristos 	for (i = 0; i < nsrks; i++)
781a03ec00cSchristos 		sshsk_free_resident_key(srks[i]);
782a03ec00cSchristos 	free(srks);
783a03ec00cSchristos }
784a03ec00cSchristos 
78518504831Schristos int
78618504831Schristos sshsk_load_resident(const char *provider_path, const char *device,
787a03ec00cSchristos     const char *pin, u_int flags, struct sshsk_resident_key ***srksp,
788a03ec00cSchristos     size_t *nsrksp)
78918504831Schristos {
79018504831Schristos 	struct sshsk_provider *skp = NULL;
79118504831Schristos 	int r = SSH_ERR_INTERNAL_ERROR;
79218504831Schristos 	struct sk_resident_key **rks = NULL;
793a03ec00cSchristos 	size_t i, nrks = 0, nsrks = 0;
794a03ec00cSchristos 	struct sshkey *key = NULL;
795a03ec00cSchristos 	struct sshsk_resident_key *srk = NULL, **srks = NULL, **tmp;
796a03ec00cSchristos 	uint8_t sk_flags;
79718504831Schristos 	struct sk_option **opts = NULL;
79818504831Schristos 
79917418e98Schristos 	debug_f("provider \"%s\"%s", provider_path,
80018504831Schristos 	    (pin != NULL && *pin != '\0') ? ", have-pin": "");
80118504831Schristos 
802a03ec00cSchristos 	if (srksp == NULL || nsrksp == NULL)
80318504831Schristos 		return SSH_ERR_INVALID_ARGUMENT;
804a03ec00cSchristos 	*srksp = NULL;
805a03ec00cSchristos 	*nsrksp = 0;
80618504831Schristos 
80718504831Schristos 	if ((r = make_options(device, NULL, &opts)) != 0)
80818504831Schristos 		goto out;
80918504831Schristos 	if ((skp = sshsk_open(provider_path)) == NULL) {
81018504831Schristos 		r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
81118504831Schristos 		goto out;
81218504831Schristos 	}
81318504831Schristos 	if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) {
81418504831Schristos 		error("Provider \"%s\" returned failure %d", provider_path, r);
81518504831Schristos 		r = skerr_to_ssherr(r);
81618504831Schristos 		goto out;
81718504831Schristos 	}
81818504831Schristos 	for (i = 0; i < nrks; i++) {
819a03ec00cSchristos 		debug3_f("rk %zu: slot %zu, alg %d, app \"%s\", uidlen %zu",
820a03ec00cSchristos 		    i, rks[i]->slot, rks[i]->alg, rks[i]->application,
821a03ec00cSchristos 		    rks[i]->user_id_len);
82218504831Schristos 		/* XXX need better filter here */
82318504831Schristos 		if (strncmp(rks[i]->application, "ssh:", 4) != 0)
82418504831Schristos 			continue;
82518504831Schristos 		switch (rks[i]->alg) {
82618504831Schristos 		case SSH_SK_ECDSA:
82718504831Schristos 		case SSH_SK_ED25519:
82818504831Schristos 			break;
82918504831Schristos 		default:
83018504831Schristos 			continue;
83118504831Schristos 		}
832a03ec00cSchristos 		sk_flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY;
8332d3b0f52Schristos 		if ((rks[i]->flags & SSH_SK_USER_VERIFICATION_REQD))
834a03ec00cSchristos 			sk_flags |= SSH_SK_USER_VERIFICATION_REQD;
83518504831Schristos 		if ((r = sshsk_key_from_response(rks[i]->alg,
836a03ec00cSchristos 		    rks[i]->application, sk_flags, &rks[i]->key, &key)) != 0)
83718504831Schristos 			goto out;
838a03ec00cSchristos 		if ((srk = calloc(1, sizeof(*srk))) == NULL) {
839a03ec00cSchristos 			error_f("calloc failed");
840a03ec00cSchristos 			r = SSH_ERR_ALLOC_FAIL;
841a03ec00cSchristos 			goto out;
842a03ec00cSchristos 		}
843a03ec00cSchristos 		srk->key = key;
844a03ec00cSchristos 		key = NULL; /* transferred */
845a03ec00cSchristos 		if ((srk->user_id = calloc(1, rks[i]->user_id_len)) == NULL) {
846a03ec00cSchristos 			error_f("calloc failed");
847a03ec00cSchristos 			r = SSH_ERR_ALLOC_FAIL;
848a03ec00cSchristos 			goto out;
849a03ec00cSchristos 		}
850a03ec00cSchristos 		memcpy(srk->user_id, rks[i]->user_id, rks[i]->user_id_len);
851a03ec00cSchristos 		srk->user_id_len = rks[i]->user_id_len;
852a03ec00cSchristos 		if ((tmp = recallocarray(srks, nsrks, nsrks + 1,
85318504831Schristos 		    sizeof(*tmp))) == NULL) {
85417418e98Schristos 			error_f("recallocarray failed");
85518504831Schristos 			r = SSH_ERR_ALLOC_FAIL;
85618504831Schristos 			goto out;
85718504831Schristos 		}
858a03ec00cSchristos 		srks = tmp;
859a03ec00cSchristos 		srks[nsrks++] = srk;
860a03ec00cSchristos 		srk = NULL;
86118504831Schristos 		/* XXX synthesise comment */
86218504831Schristos 	}
86318504831Schristos 	/* success */
864a03ec00cSchristos 	*srksp = srks;
865a03ec00cSchristos 	*nsrksp = nsrks;
866a03ec00cSchristos 	srks = NULL;
867a03ec00cSchristos 	nsrks = 0;
86818504831Schristos 	r = 0;
86918504831Schristos  out:
87018504831Schristos 	sshsk_free_options(opts);
87118504831Schristos 	sshsk_free(skp);
87218504831Schristos 	sshsk_free_sk_resident_keys(rks, nrks);
87318504831Schristos 	sshkey_free(key);
874a03ec00cSchristos 	sshsk_free_resident_key(srk);
875a03ec00cSchristos 	sshsk_free_resident_keys(srks, nsrks);
87618504831Schristos 	return r;
87718504831Schristos }
87818504831Schristos 
879