1*1323ec57SEd Maste /* $OpenBSD: ssh-sk.c,v 1.38 2022/01/14 03:35:10 djm Exp $ */ 219261079SEd Maste /* 319261079SEd Maste * Copyright (c) 2019 Google LLC 419261079SEd Maste * 519261079SEd Maste * Permission to use, copy, modify, and distribute this software for any 619261079SEd Maste * purpose with or without fee is hereby granted, provided that the above 719261079SEd Maste * copyright notice and this permission notice appear in all copies. 819261079SEd Maste * 919261079SEd Maste * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1019261079SEd Maste * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1119261079SEd Maste * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1219261079SEd Maste * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1319261079SEd Maste * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1419261079SEd Maste * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1519261079SEd Maste * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1619261079SEd Maste */ 1719261079SEd Maste 1819261079SEd Maste /* #define DEBUG_SK 1 */ 1919261079SEd Maste 2019261079SEd Maste #include "includes.h" 2119261079SEd Maste 2219261079SEd Maste #ifdef ENABLE_SK 2319261079SEd Maste 2419261079SEd Maste #include <dlfcn.h> 2519261079SEd Maste #include <stddef.h> 2619261079SEd Maste #ifdef HAVE_STDINT_H 2719261079SEd Maste # include <stdint.h> 2819261079SEd Maste #endif 2919261079SEd Maste #include <string.h> 3019261079SEd Maste #include <stdio.h> 3119261079SEd Maste 32*1323ec57SEd Maste #if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) 3319261079SEd Maste #include <openssl/objects.h> 3419261079SEd Maste #include <openssl/ec.h> 35*1323ec57SEd Maste #endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ 3619261079SEd Maste 3719261079SEd Maste #include "log.h" 3819261079SEd Maste #include "misc.h" 3919261079SEd Maste #include "sshbuf.h" 4019261079SEd Maste #include "sshkey.h" 4119261079SEd Maste #include "ssherr.h" 4219261079SEd Maste #include "digest.h" 4319261079SEd Maste 4419261079SEd Maste #include "ssh-sk.h" 4519261079SEd Maste #include "sk-api.h" 4619261079SEd Maste #include "crypto_api.h" 4719261079SEd Maste 48*1323ec57SEd Maste /* 49*1323ec57SEd Maste * Almost every use of OpenSSL in this file is for ECDSA-NISTP256. 50*1323ec57SEd Maste * This is strictly a larger hammer than necessary, but it reduces changes 51*1323ec57SEd Maste * with upstream. 52*1323ec57SEd Maste */ 53*1323ec57SEd Maste #ifndef OPENSSL_HAS_ECC 54*1323ec57SEd Maste # undef WITH_OPENSSL 55*1323ec57SEd Maste #endif 56*1323ec57SEd Maste 5719261079SEd Maste struct sshsk_provider { 5819261079SEd Maste char *path; 5919261079SEd Maste void *dlhandle; 6019261079SEd Maste 6119261079SEd Maste /* Return the version of the middleware API */ 6219261079SEd Maste uint32_t (*sk_api_version)(void); 6319261079SEd Maste 6419261079SEd Maste /* Enroll a U2F key (private key generation) */ 6519261079SEd Maste int (*sk_enroll)(int alg, const uint8_t *challenge, 6619261079SEd Maste size_t challenge_len, const char *application, uint8_t flags, 6719261079SEd Maste const char *pin, struct sk_option **opts, 6819261079SEd Maste struct sk_enroll_response **enroll_response); 6919261079SEd Maste 7019261079SEd Maste /* Sign a challenge */ 7119261079SEd Maste int (*sk_sign)(int alg, const uint8_t *message, size_t message_len, 7219261079SEd Maste const char *application, 7319261079SEd Maste const uint8_t *key_handle, size_t key_handle_len, 7419261079SEd Maste uint8_t flags, const char *pin, struct sk_option **opts, 7519261079SEd Maste struct sk_sign_response **sign_response); 7619261079SEd Maste 7719261079SEd Maste /* Enumerate resident keys */ 7819261079SEd Maste int (*sk_load_resident_keys)(const char *pin, struct sk_option **opts, 7919261079SEd Maste struct sk_resident_key ***rks, size_t *nrks); 8019261079SEd Maste }; 8119261079SEd Maste 8219261079SEd Maste /* Built-in version */ 8319261079SEd Maste int ssh_sk_enroll(int alg, const uint8_t *challenge, 8419261079SEd Maste size_t challenge_len, const char *application, uint8_t flags, 8519261079SEd Maste const char *pin, struct sk_option **opts, 8619261079SEd Maste struct sk_enroll_response **enroll_response); 8719261079SEd Maste int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, 8819261079SEd Maste const char *application, 8919261079SEd Maste const uint8_t *key_handle, size_t key_handle_len, 9019261079SEd Maste uint8_t flags, const char *pin, struct sk_option **opts, 9119261079SEd Maste struct sk_sign_response **sign_response); 9219261079SEd Maste int ssh_sk_load_resident_keys(const char *pin, struct sk_option **opts, 9319261079SEd Maste struct sk_resident_key ***rks, size_t *nrks); 9419261079SEd Maste 9519261079SEd Maste static void 9619261079SEd Maste sshsk_free(struct sshsk_provider *p) 9719261079SEd Maste { 9819261079SEd Maste if (p == NULL) 9919261079SEd Maste return; 10019261079SEd Maste free(p->path); 10119261079SEd Maste if (p->dlhandle != NULL) 10219261079SEd Maste dlclose(p->dlhandle); 10319261079SEd Maste free(p); 10419261079SEd Maste } 10519261079SEd Maste 10619261079SEd Maste static struct sshsk_provider * 10719261079SEd Maste sshsk_open(const char *path) 10819261079SEd Maste { 10919261079SEd Maste struct sshsk_provider *ret = NULL; 11019261079SEd Maste uint32_t version; 11119261079SEd Maste 11219261079SEd Maste if (path == NULL || *path == '\0') { 11319261079SEd Maste error("No FIDO SecurityKeyProvider specified"); 11419261079SEd Maste return NULL; 11519261079SEd Maste } 11619261079SEd Maste if ((ret = calloc(1, sizeof(*ret))) == NULL) { 11719261079SEd Maste error_f("calloc failed"); 11819261079SEd Maste return NULL; 11919261079SEd Maste } 12019261079SEd Maste if ((ret->path = strdup(path)) == NULL) { 12119261079SEd Maste error_f("strdup failed"); 12219261079SEd Maste goto fail; 12319261079SEd Maste } 12419261079SEd Maste /* Skip the rest if we're using the linked in middleware */ 12519261079SEd Maste if (strcasecmp(ret->path, "internal") == 0) { 12619261079SEd Maste #ifdef ENABLE_SK_INTERNAL 12719261079SEd Maste ret->sk_enroll = ssh_sk_enroll; 12819261079SEd Maste ret->sk_sign = ssh_sk_sign; 12919261079SEd Maste ret->sk_load_resident_keys = ssh_sk_load_resident_keys; 13019261079SEd Maste #else 13119261079SEd Maste error("internal security key support not enabled"); 13219261079SEd Maste #endif 13319261079SEd Maste return ret; 13419261079SEd Maste } 13519261079SEd Maste if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) { 13619261079SEd Maste error("Provider \"%s\" dlopen failed: %s", path, dlerror()); 13719261079SEd Maste goto fail; 13819261079SEd Maste } 13919261079SEd Maste if ((ret->sk_api_version = dlsym(ret->dlhandle, 14019261079SEd Maste "sk_api_version")) == NULL) { 14119261079SEd Maste error("Provider \"%s\" dlsym(sk_api_version) failed: %s", 14219261079SEd Maste path, dlerror()); 14319261079SEd Maste goto fail; 14419261079SEd Maste } 14519261079SEd Maste version = ret->sk_api_version(); 14619261079SEd Maste debug_f("provider %s implements version 0x%08lx", ret->path, 14719261079SEd Maste (u_long)version); 14819261079SEd Maste if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) { 14919261079SEd Maste error("Provider \"%s\" implements unsupported " 15019261079SEd Maste "version 0x%08lx (supported: 0x%08lx)", 15119261079SEd Maste path, (u_long)version, (u_long)SSH_SK_VERSION_MAJOR); 15219261079SEd Maste goto fail; 15319261079SEd Maste } 15419261079SEd Maste if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) { 15519261079SEd Maste error("Provider %s dlsym(sk_enroll) failed: %s", 15619261079SEd Maste path, dlerror()); 15719261079SEd Maste goto fail; 15819261079SEd Maste } 15919261079SEd Maste if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) { 16019261079SEd Maste error("Provider \"%s\" dlsym(sk_sign) failed: %s", 16119261079SEd Maste path, dlerror()); 16219261079SEd Maste goto fail; 16319261079SEd Maste } 16419261079SEd Maste if ((ret->sk_load_resident_keys = dlsym(ret->dlhandle, 16519261079SEd Maste "sk_load_resident_keys")) == NULL) { 16619261079SEd Maste error("Provider \"%s\" dlsym(sk_load_resident_keys) " 16719261079SEd Maste "failed: %s", path, dlerror()); 16819261079SEd Maste goto fail; 16919261079SEd Maste } 17019261079SEd Maste /* success */ 17119261079SEd Maste return ret; 17219261079SEd Maste fail: 17319261079SEd Maste sshsk_free(ret); 17419261079SEd Maste return NULL; 17519261079SEd Maste } 17619261079SEd Maste 17719261079SEd Maste static void 17819261079SEd Maste sshsk_free_enroll_response(struct sk_enroll_response *r) 17919261079SEd Maste { 18019261079SEd Maste if (r == NULL) 18119261079SEd Maste return; 18219261079SEd Maste freezero(r->key_handle, r->key_handle_len); 18319261079SEd Maste freezero(r->public_key, r->public_key_len); 18419261079SEd Maste freezero(r->signature, r->signature_len); 18519261079SEd Maste freezero(r->attestation_cert, r->attestation_cert_len); 18619261079SEd Maste freezero(r->authdata, r->authdata_len); 18719261079SEd Maste freezero(r, sizeof(*r)); 18819261079SEd Maste } 18919261079SEd Maste 19019261079SEd Maste static void 19119261079SEd Maste sshsk_free_sign_response(struct sk_sign_response *r) 19219261079SEd Maste { 19319261079SEd Maste if (r == NULL) 19419261079SEd Maste return; 19519261079SEd Maste freezero(r->sig_r, r->sig_r_len); 19619261079SEd Maste freezero(r->sig_s, r->sig_s_len); 19719261079SEd Maste freezero(r, sizeof(*r)); 19819261079SEd Maste } 19919261079SEd Maste 20019261079SEd Maste #ifdef WITH_OPENSSL 20119261079SEd Maste /* Assemble key from response */ 20219261079SEd Maste static int 20319261079SEd Maste sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp) 20419261079SEd Maste { 20519261079SEd Maste struct sshkey *key = NULL; 20619261079SEd Maste struct sshbuf *b = NULL; 20719261079SEd Maste EC_POINT *q = NULL; 20819261079SEd Maste int r; 20919261079SEd Maste 21019261079SEd Maste *keyp = NULL; 21119261079SEd Maste if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) { 21219261079SEd Maste error_f("sshkey_new failed"); 21319261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 21419261079SEd Maste goto out; 21519261079SEd Maste } 21619261079SEd Maste key->ecdsa_nid = NID_X9_62_prime256v1; 21719261079SEd Maste if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL || 21819261079SEd Maste (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL || 21919261079SEd Maste (b = sshbuf_new()) == NULL) { 22019261079SEd Maste error_f("allocation failed"); 22119261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 22219261079SEd Maste goto out; 22319261079SEd Maste } 22419261079SEd Maste if ((r = sshbuf_put_string(b, 22519261079SEd Maste resp->public_key, resp->public_key_len)) != 0) { 22619261079SEd Maste error_fr(r, "sshbuf_put_string"); 22719261079SEd Maste goto out; 22819261079SEd Maste } 22919261079SEd Maste if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) { 23019261079SEd Maste error_fr(r, "parse"); 23119261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 23219261079SEd Maste goto out; 23319261079SEd Maste } 23419261079SEd Maste if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) { 23519261079SEd Maste error("Authenticator returned invalid ECDSA key"); 23619261079SEd Maste r = SSH_ERR_KEY_INVALID_EC_VALUE; 23719261079SEd Maste goto out; 23819261079SEd Maste } 23919261079SEd Maste if (EC_KEY_set_public_key(key->ecdsa, q) != 1) { 24019261079SEd Maste /* XXX assume it is a allocation error */ 24119261079SEd Maste error_f("allocation failed"); 24219261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 24319261079SEd Maste goto out; 24419261079SEd Maste } 24519261079SEd Maste /* success */ 24619261079SEd Maste *keyp = key; 24719261079SEd Maste key = NULL; /* transferred */ 24819261079SEd Maste r = 0; 24919261079SEd Maste out: 25019261079SEd Maste EC_POINT_free(q); 25119261079SEd Maste sshkey_free(key); 25219261079SEd Maste sshbuf_free(b); 25319261079SEd Maste return r; 25419261079SEd Maste } 25519261079SEd Maste #endif /* WITH_OPENSSL */ 25619261079SEd Maste 25719261079SEd Maste static int 25819261079SEd Maste sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp) 25919261079SEd Maste { 26019261079SEd Maste struct sshkey *key = NULL; 26119261079SEd Maste int r; 26219261079SEd Maste 26319261079SEd Maste *keyp = NULL; 26419261079SEd Maste if (resp->public_key_len != ED25519_PK_SZ) { 26519261079SEd Maste error_f("invalid size: %zu", resp->public_key_len); 26619261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 26719261079SEd Maste goto out; 26819261079SEd Maste } 26919261079SEd Maste if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) { 27019261079SEd Maste error_f("sshkey_new failed"); 27119261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 27219261079SEd Maste goto out; 27319261079SEd Maste } 27419261079SEd Maste if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { 27519261079SEd Maste error_f("malloc failed"); 27619261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 27719261079SEd Maste goto out; 27819261079SEd Maste } 27919261079SEd Maste memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ); 28019261079SEd Maste /* success */ 28119261079SEd Maste *keyp = key; 28219261079SEd Maste key = NULL; /* transferred */ 28319261079SEd Maste r = 0; 28419261079SEd Maste out: 28519261079SEd Maste sshkey_free(key); 28619261079SEd Maste return r; 28719261079SEd Maste } 28819261079SEd Maste 28919261079SEd Maste static int 29019261079SEd Maste sshsk_key_from_response(int alg, const char *application, uint8_t flags, 29119261079SEd Maste struct sk_enroll_response *resp, struct sshkey **keyp) 29219261079SEd Maste { 29319261079SEd Maste struct sshkey *key = NULL; 29419261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 29519261079SEd Maste 29619261079SEd Maste *keyp = NULL; 29719261079SEd Maste 29819261079SEd Maste /* Check response validity */ 29919261079SEd Maste if (resp->public_key == NULL || resp->key_handle == NULL) { 30019261079SEd Maste error_f("sk_enroll response invalid"); 30119261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 30219261079SEd Maste goto out; 30319261079SEd Maste } 30419261079SEd Maste switch (alg) { 30519261079SEd Maste #ifdef WITH_OPENSSL 30619261079SEd Maste case SSH_SK_ECDSA: 30719261079SEd Maste if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0) 30819261079SEd Maste goto out; 30919261079SEd Maste break; 31019261079SEd Maste #endif /* WITH_OPENSSL */ 31119261079SEd Maste case SSH_SK_ED25519: 31219261079SEd Maste if ((r = sshsk_ed25519_assemble(resp, &key)) != 0) 31319261079SEd Maste goto out; 31419261079SEd Maste break; 31519261079SEd Maste default: 31619261079SEd Maste error_f("unsupported algorithm %d", alg); 31719261079SEd Maste r = SSH_ERR_INVALID_ARGUMENT; 31819261079SEd Maste goto out; 31919261079SEd Maste } 32019261079SEd Maste key->sk_flags = flags; 32119261079SEd Maste if ((key->sk_key_handle = sshbuf_new()) == NULL || 32219261079SEd Maste (key->sk_reserved = sshbuf_new()) == NULL) { 32319261079SEd Maste error_f("allocation failed"); 32419261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 32519261079SEd Maste goto out; 32619261079SEd Maste } 32719261079SEd Maste if ((key->sk_application = strdup(application)) == NULL) { 32819261079SEd Maste error_f("strdup application failed"); 32919261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 33019261079SEd Maste goto out; 33119261079SEd Maste } 33219261079SEd Maste if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle, 33319261079SEd Maste resp->key_handle_len)) != 0) { 33419261079SEd Maste error_fr(r, "put key handle"); 33519261079SEd Maste goto out; 33619261079SEd Maste } 33719261079SEd Maste /* success */ 33819261079SEd Maste r = 0; 33919261079SEd Maste *keyp = key; 34019261079SEd Maste key = NULL; 34119261079SEd Maste out: 34219261079SEd Maste sshkey_free(key); 34319261079SEd Maste return r; 34419261079SEd Maste } 34519261079SEd Maste 34619261079SEd Maste static int 34719261079SEd Maste skerr_to_ssherr(int skerr) 34819261079SEd Maste { 34919261079SEd Maste switch (skerr) { 35019261079SEd Maste case SSH_SK_ERR_UNSUPPORTED: 35119261079SEd Maste return SSH_ERR_FEATURE_UNSUPPORTED; 35219261079SEd Maste case SSH_SK_ERR_PIN_REQUIRED: 35319261079SEd Maste return SSH_ERR_KEY_WRONG_PASSPHRASE; 35419261079SEd Maste case SSH_SK_ERR_DEVICE_NOT_FOUND: 35519261079SEd Maste return SSH_ERR_DEVICE_NOT_FOUND; 35619261079SEd Maste case SSH_SK_ERR_GENERAL: 35719261079SEd Maste default: 35819261079SEd Maste return SSH_ERR_INVALID_FORMAT; 35919261079SEd Maste } 36019261079SEd Maste } 36119261079SEd Maste 36219261079SEd Maste static void 36319261079SEd Maste sshsk_free_options(struct sk_option **opts) 36419261079SEd Maste { 36519261079SEd Maste size_t i; 36619261079SEd Maste 36719261079SEd Maste if (opts == NULL) 36819261079SEd Maste return; 36919261079SEd Maste for (i = 0; opts[i] != NULL; i++) { 37019261079SEd Maste free(opts[i]->name); 37119261079SEd Maste free(opts[i]->value); 37219261079SEd Maste free(opts[i]); 37319261079SEd Maste } 37419261079SEd Maste free(opts); 37519261079SEd Maste } 37619261079SEd Maste 37719261079SEd Maste static int 37819261079SEd Maste sshsk_add_option(struct sk_option ***optsp, size_t *noptsp, 37919261079SEd Maste const char *name, const char *value, uint8_t required) 38019261079SEd Maste { 38119261079SEd Maste struct sk_option **opts = *optsp; 38219261079SEd Maste size_t nopts = *noptsp; 38319261079SEd Maste 38419261079SEd Maste if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */ 38519261079SEd Maste sizeof(*opts))) == NULL) { 38619261079SEd Maste error_f("array alloc failed"); 38719261079SEd Maste return SSH_ERR_ALLOC_FAIL; 38819261079SEd Maste } 38919261079SEd Maste *optsp = opts; 39019261079SEd Maste *noptsp = nopts + 1; 39119261079SEd Maste if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) { 39219261079SEd Maste error_f("alloc failed"); 39319261079SEd Maste return SSH_ERR_ALLOC_FAIL; 39419261079SEd Maste } 39519261079SEd Maste if ((opts[nopts]->name = strdup(name)) == NULL || 39619261079SEd Maste (opts[nopts]->value = strdup(value)) == NULL) { 39719261079SEd Maste error_f("alloc failed"); 39819261079SEd Maste return SSH_ERR_ALLOC_FAIL; 39919261079SEd Maste } 40019261079SEd Maste opts[nopts]->required = required; 40119261079SEd Maste return 0; 40219261079SEd Maste } 40319261079SEd Maste 40419261079SEd Maste static int 40519261079SEd Maste make_options(const char *device, const char *user_id, 40619261079SEd Maste struct sk_option ***optsp) 40719261079SEd Maste { 40819261079SEd Maste struct sk_option **opts = NULL; 40919261079SEd Maste size_t nopts = 0; 41019261079SEd Maste int r, ret = SSH_ERR_INTERNAL_ERROR; 41119261079SEd Maste 41219261079SEd Maste if (device != NULL && 41319261079SEd Maste (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) { 41419261079SEd Maste ret = r; 41519261079SEd Maste goto out; 41619261079SEd Maste } 41719261079SEd Maste if (user_id != NULL && 41819261079SEd Maste (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) { 41919261079SEd Maste ret = r; 42019261079SEd Maste goto out; 42119261079SEd Maste } 42219261079SEd Maste /* success */ 42319261079SEd Maste *optsp = opts; 42419261079SEd Maste opts = NULL; 42519261079SEd Maste nopts = 0; 42619261079SEd Maste ret = 0; 42719261079SEd Maste out: 42819261079SEd Maste sshsk_free_options(opts); 42919261079SEd Maste return ret; 43019261079SEd Maste } 43119261079SEd Maste 43219261079SEd Maste 43319261079SEd Maste static int 43419261079SEd Maste fill_attestation_blob(const struct sk_enroll_response *resp, 43519261079SEd Maste struct sshbuf *attest) 43619261079SEd Maste { 43719261079SEd Maste int r; 43819261079SEd Maste 43919261079SEd Maste if (attest == NULL) 44019261079SEd Maste return 0; /* nothing to do */ 44119261079SEd Maste if ((r = sshbuf_put_cstring(attest, "ssh-sk-attest-v01")) != 0 || 44219261079SEd Maste (r = sshbuf_put_string(attest, 44319261079SEd Maste resp->attestation_cert, resp->attestation_cert_len)) != 0 || 44419261079SEd Maste (r = sshbuf_put_string(attest, 44519261079SEd Maste resp->signature, resp->signature_len)) != 0 || 44619261079SEd Maste (r = sshbuf_put_string(attest, 44719261079SEd Maste resp->authdata, resp->authdata_len)) != 0 || 44819261079SEd Maste (r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */ 44919261079SEd Maste (r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) { 45019261079SEd Maste error_fr(r, "compose"); 45119261079SEd Maste return r; 45219261079SEd Maste } 45319261079SEd Maste /* success */ 45419261079SEd Maste return 0; 45519261079SEd Maste } 45619261079SEd Maste 45719261079SEd Maste int 45819261079SEd Maste sshsk_enroll(int type, const char *provider_path, const char *device, 45919261079SEd Maste const char *application, const char *userid, uint8_t flags, 46019261079SEd Maste const char *pin, struct sshbuf *challenge_buf, 46119261079SEd Maste struct sshkey **keyp, struct sshbuf *attest) 46219261079SEd Maste { 46319261079SEd Maste struct sshsk_provider *skp = NULL; 46419261079SEd Maste struct sshkey *key = NULL; 46519261079SEd Maste u_char randchall[32]; 46619261079SEd Maste const u_char *challenge; 46719261079SEd Maste size_t challenge_len; 46819261079SEd Maste struct sk_enroll_response *resp = NULL; 46919261079SEd Maste struct sk_option **opts = NULL; 47019261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 47119261079SEd Maste int alg; 47219261079SEd Maste 47319261079SEd Maste debug_f("provider \"%s\", device \"%s\", application \"%s\", " 47419261079SEd Maste "userid \"%s\", flags 0x%02x, challenge len %zu%s", 47519261079SEd Maste provider_path, device, application, userid, flags, 47619261079SEd Maste challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf), 47719261079SEd Maste (pin != NULL && *pin != '\0') ? " with-pin" : ""); 47819261079SEd Maste 47919261079SEd Maste *keyp = NULL; 48019261079SEd Maste if (attest) 48119261079SEd Maste sshbuf_reset(attest); 48219261079SEd Maste 48319261079SEd Maste if ((r = make_options(device, userid, &opts)) != 0) 48419261079SEd Maste goto out; 48519261079SEd Maste 48619261079SEd Maste switch (type) { 48719261079SEd Maste #ifdef WITH_OPENSSL 48819261079SEd Maste case KEY_ECDSA_SK: 48919261079SEd Maste alg = SSH_SK_ECDSA; 49019261079SEd Maste break; 49119261079SEd Maste #endif /* WITH_OPENSSL */ 49219261079SEd Maste case KEY_ED25519_SK: 49319261079SEd Maste alg = SSH_SK_ED25519; 49419261079SEd Maste break; 49519261079SEd Maste default: 49619261079SEd Maste error_f("unsupported key type"); 49719261079SEd Maste r = SSH_ERR_INVALID_ARGUMENT; 49819261079SEd Maste goto out; 49919261079SEd Maste } 50019261079SEd Maste if (provider_path == NULL) { 50119261079SEd Maste error_f("missing provider"); 50219261079SEd Maste r = SSH_ERR_INVALID_ARGUMENT; 50319261079SEd Maste goto out; 50419261079SEd Maste } 50519261079SEd Maste if (application == NULL || *application == '\0') { 50619261079SEd Maste error_f("missing application"); 50719261079SEd Maste r = SSH_ERR_INVALID_ARGUMENT; 50819261079SEd Maste goto out; 50919261079SEd Maste } 51019261079SEd Maste if (challenge_buf == NULL) { 51119261079SEd Maste debug_f("using random challenge"); 51219261079SEd Maste arc4random_buf(randchall, sizeof(randchall)); 51319261079SEd Maste challenge = randchall; 51419261079SEd Maste challenge_len = sizeof(randchall); 51519261079SEd Maste } else if (sshbuf_len(challenge_buf) == 0) { 51619261079SEd Maste error("Missing enrollment challenge"); 51719261079SEd Maste r = SSH_ERR_INVALID_ARGUMENT; 51819261079SEd Maste goto out; 51919261079SEd Maste } else { 52019261079SEd Maste challenge = sshbuf_ptr(challenge_buf); 52119261079SEd Maste challenge_len = sshbuf_len(challenge_buf); 52219261079SEd Maste debug3_f("using explicit challenge len=%zd", challenge_len); 52319261079SEd Maste } 52419261079SEd Maste if ((skp = sshsk_open(provider_path)) == NULL) { 52519261079SEd Maste r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ 52619261079SEd Maste goto out; 52719261079SEd Maste } 52819261079SEd Maste /* XXX validate flags? */ 52919261079SEd Maste /* enroll key */ 53019261079SEd Maste if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, 53119261079SEd Maste flags, pin, opts, &resp)) != 0) { 53219261079SEd Maste debug_f("provider \"%s\" failure %d", provider_path, r); 53319261079SEd Maste r = skerr_to_ssherr(r); 53419261079SEd Maste goto out; 53519261079SEd Maste } 53619261079SEd Maste 537*1323ec57SEd Maste if ((r = sshsk_key_from_response(alg, application, resp->flags, 53819261079SEd Maste resp, &key)) != 0) 53919261079SEd Maste goto out; 54019261079SEd Maste 54119261079SEd Maste /* Optionally fill in the attestation information */ 54219261079SEd Maste if ((r = fill_attestation_blob(resp, attest)) != 0) 54319261079SEd Maste goto out; 54419261079SEd Maste 54519261079SEd Maste /* success */ 54619261079SEd Maste *keyp = key; 54719261079SEd Maste key = NULL; /* transferred */ 54819261079SEd Maste r = 0; 54919261079SEd Maste out: 55019261079SEd Maste sshsk_free_options(opts); 55119261079SEd Maste sshsk_free(skp); 55219261079SEd Maste sshkey_free(key); 55319261079SEd Maste sshsk_free_enroll_response(resp); 55419261079SEd Maste explicit_bzero(randchall, sizeof(randchall)); 55519261079SEd Maste return r; 55619261079SEd Maste } 55719261079SEd Maste 55819261079SEd Maste #ifdef WITH_OPENSSL 55919261079SEd Maste static int 56019261079SEd Maste sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig) 56119261079SEd Maste { 56219261079SEd Maste struct sshbuf *inner_sig = NULL; 56319261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 56419261079SEd Maste 56519261079SEd Maste /* Check response validity */ 56619261079SEd Maste if (resp->sig_r == NULL || resp->sig_s == NULL) { 56719261079SEd Maste error_f("sk_sign response invalid"); 56819261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 56919261079SEd Maste goto out; 57019261079SEd Maste } 57119261079SEd Maste if ((inner_sig = sshbuf_new()) == NULL) { 57219261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 57319261079SEd Maste goto out; 57419261079SEd Maste } 57519261079SEd Maste /* Prepare and append inner signature object */ 57619261079SEd Maste if ((r = sshbuf_put_bignum2_bytes(inner_sig, 57719261079SEd Maste resp->sig_r, resp->sig_r_len)) != 0 || 57819261079SEd Maste (r = sshbuf_put_bignum2_bytes(inner_sig, 57919261079SEd Maste resp->sig_s, resp->sig_s_len)) != 0) { 58019261079SEd Maste error_fr(r, "compose inner"); 58119261079SEd Maste goto out; 58219261079SEd Maste } 58319261079SEd Maste if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 || 58419261079SEd Maste (r = sshbuf_put_u8(sig, resp->flags)) != 0 || 58519261079SEd Maste (r = sshbuf_put_u32(sig, resp->counter)) != 0) { 58619261079SEd Maste error_fr(r, "compose"); 58719261079SEd Maste goto out; 58819261079SEd Maste } 58919261079SEd Maste #ifdef DEBUG_SK 59019261079SEd Maste fprintf(stderr, "%s: sig_r:\n", __func__); 59119261079SEd Maste sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr); 59219261079SEd Maste fprintf(stderr, "%s: sig_s:\n", __func__); 59319261079SEd Maste sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr); 59419261079SEd Maste fprintf(stderr, "%s: inner:\n", __func__); 59519261079SEd Maste sshbuf_dump(inner_sig, stderr); 59619261079SEd Maste #endif 59719261079SEd Maste r = 0; 59819261079SEd Maste out: 59919261079SEd Maste sshbuf_free(inner_sig); 60019261079SEd Maste return r; 60119261079SEd Maste } 60219261079SEd Maste #endif /* WITH_OPENSSL */ 60319261079SEd Maste 60419261079SEd Maste static int 60519261079SEd Maste sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig) 60619261079SEd Maste { 60719261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 60819261079SEd Maste 60919261079SEd Maste /* Check response validity */ 61019261079SEd Maste if (resp->sig_r == NULL) { 61119261079SEd Maste error_f("sk_sign response invalid"); 61219261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 61319261079SEd Maste goto out; 61419261079SEd Maste } 61519261079SEd Maste if ((r = sshbuf_put_string(sig, 61619261079SEd Maste resp->sig_r, resp->sig_r_len)) != 0 || 61719261079SEd Maste (r = sshbuf_put_u8(sig, resp->flags)) != 0 || 61819261079SEd Maste (r = sshbuf_put_u32(sig, resp->counter)) != 0) { 61919261079SEd Maste error_fr(r, "compose"); 62019261079SEd Maste goto out; 62119261079SEd Maste } 62219261079SEd Maste #ifdef DEBUG_SK 62319261079SEd Maste fprintf(stderr, "%s: sig_r:\n", __func__); 62419261079SEd Maste sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr); 62519261079SEd Maste #endif 62619261079SEd Maste r = 0; 62719261079SEd Maste out: 62819261079SEd Maste return r; 62919261079SEd Maste } 63019261079SEd Maste 63119261079SEd Maste int 63219261079SEd Maste sshsk_sign(const char *provider_path, struct sshkey *key, 63319261079SEd Maste u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, 63419261079SEd Maste u_int compat, const char *pin) 63519261079SEd Maste { 63619261079SEd Maste struct sshsk_provider *skp = NULL; 63719261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 63819261079SEd Maste int type, alg; 63919261079SEd Maste struct sk_sign_response *resp = NULL; 64019261079SEd Maste struct sshbuf *inner_sig = NULL, *sig = NULL; 64119261079SEd Maste struct sk_option **opts = NULL; 64219261079SEd Maste 64319261079SEd Maste debug_f("provider \"%s\", key %s, flags 0x%02x%s", 64419261079SEd Maste provider_path, sshkey_type(key), key->sk_flags, 64519261079SEd Maste (pin != NULL && *pin != '\0') ? " with-pin" : ""); 64619261079SEd Maste 64719261079SEd Maste if (sigp != NULL) 64819261079SEd Maste *sigp = NULL; 64919261079SEd Maste if (lenp != NULL) 65019261079SEd Maste *lenp = 0; 65119261079SEd Maste type = sshkey_type_plain(key->type); 65219261079SEd Maste switch (type) { 65319261079SEd Maste #ifdef WITH_OPENSSL 65419261079SEd Maste case KEY_ECDSA_SK: 65519261079SEd Maste alg = SSH_SK_ECDSA; 65619261079SEd Maste break; 65719261079SEd Maste #endif /* WITH_OPENSSL */ 65819261079SEd Maste case KEY_ED25519_SK: 65919261079SEd Maste alg = SSH_SK_ED25519; 66019261079SEd Maste break; 66119261079SEd Maste default: 66219261079SEd Maste return SSH_ERR_INVALID_ARGUMENT; 66319261079SEd Maste } 66419261079SEd Maste if (provider_path == NULL || 66519261079SEd Maste key->sk_key_handle == NULL || 66619261079SEd Maste key->sk_application == NULL || *key->sk_application == '\0') { 66719261079SEd Maste r = SSH_ERR_INVALID_ARGUMENT; 66819261079SEd Maste goto out; 66919261079SEd Maste } 67019261079SEd Maste if ((skp = sshsk_open(provider_path)) == NULL) { 67119261079SEd Maste r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ 67219261079SEd Maste goto out; 67319261079SEd Maste } 67419261079SEd Maste #ifdef DEBUG_SK 67519261079SEd Maste fprintf(stderr, "%s: sk_flags = 0x%02x, sk_application = \"%s\"\n", 67619261079SEd Maste __func__, key->sk_flags, key->sk_application); 67719261079SEd Maste fprintf(stderr, "%s: sk_key_handle:\n", __func__); 67819261079SEd Maste sshbuf_dump(key->sk_key_handle, stderr); 67919261079SEd Maste #endif 68019261079SEd Maste if ((r = skp->sk_sign(alg, data, datalen, key->sk_application, 68119261079SEd Maste sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle), 68219261079SEd Maste key->sk_flags, pin, opts, &resp)) != 0) { 68319261079SEd Maste debug_f("sk_sign failed with code %d", r); 68419261079SEd Maste r = skerr_to_ssherr(r); 68519261079SEd Maste goto out; 68619261079SEd Maste } 68719261079SEd Maste /* Assemble signature */ 68819261079SEd Maste if ((sig = sshbuf_new()) == NULL) { 68919261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 69019261079SEd Maste goto out; 69119261079SEd Maste } 69219261079SEd Maste if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) { 69319261079SEd Maste error_fr(r, "compose outer"); 69419261079SEd Maste goto out; 69519261079SEd Maste } 69619261079SEd Maste switch (type) { 69719261079SEd Maste #ifdef WITH_OPENSSL 69819261079SEd Maste case KEY_ECDSA_SK: 69919261079SEd Maste if ((r = sshsk_ecdsa_sig(resp, sig)) != 0) 70019261079SEd Maste goto out; 70119261079SEd Maste break; 70219261079SEd Maste #endif /* WITH_OPENSSL */ 70319261079SEd Maste case KEY_ED25519_SK: 70419261079SEd Maste if ((r = sshsk_ed25519_sig(resp, sig)) != 0) 70519261079SEd Maste goto out; 70619261079SEd Maste break; 70719261079SEd Maste } 70819261079SEd Maste #ifdef DEBUG_SK 70919261079SEd Maste fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n", 71019261079SEd Maste __func__, resp->flags, resp->counter); 71119261079SEd Maste fprintf(stderr, "%s: data to sign:\n", __func__); 71219261079SEd Maste sshbuf_dump_data(data, datalen, stderr); 71319261079SEd Maste fprintf(stderr, "%s: sigbuf:\n", __func__); 71419261079SEd Maste sshbuf_dump(sig, stderr); 71519261079SEd Maste #endif 71619261079SEd Maste if (sigp != NULL) { 71719261079SEd Maste if ((*sigp = malloc(sshbuf_len(sig))) == NULL) { 71819261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 71919261079SEd Maste goto out; 72019261079SEd Maste } 72119261079SEd Maste memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig)); 72219261079SEd Maste } 72319261079SEd Maste if (lenp != NULL) 72419261079SEd Maste *lenp = sshbuf_len(sig); 72519261079SEd Maste /* success */ 72619261079SEd Maste r = 0; 72719261079SEd Maste out: 72819261079SEd Maste sshsk_free_options(opts); 72919261079SEd Maste sshsk_free(skp); 73019261079SEd Maste sshsk_free_sign_response(resp); 73119261079SEd Maste sshbuf_free(sig); 73219261079SEd Maste sshbuf_free(inner_sig); 73319261079SEd Maste return r; 73419261079SEd Maste } 73519261079SEd Maste 73619261079SEd Maste static void 73719261079SEd Maste sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks) 73819261079SEd Maste { 73919261079SEd Maste size_t i; 74019261079SEd Maste 74119261079SEd Maste if (nrks == 0 || rks == NULL) 74219261079SEd Maste return; 74319261079SEd Maste for (i = 0; i < nrks; i++) { 74419261079SEd Maste free(rks[i]->application); 745*1323ec57SEd Maste freezero(rks[i]->user_id, rks[i]->user_id_len); 74619261079SEd Maste freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len); 74719261079SEd Maste freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); 74819261079SEd Maste freezero(rks[i]->key.signature, rks[i]->key.signature_len); 74919261079SEd Maste freezero(rks[i]->key.attestation_cert, 75019261079SEd Maste rks[i]->key.attestation_cert_len); 75119261079SEd Maste freezero(rks[i], sizeof(**rks)); 75219261079SEd Maste } 75319261079SEd Maste free(rks); 75419261079SEd Maste } 75519261079SEd Maste 756*1323ec57SEd Maste static void 757*1323ec57SEd Maste sshsk_free_resident_key(struct sshsk_resident_key *srk) 758*1323ec57SEd Maste { 759*1323ec57SEd Maste if (srk == NULL) 760*1323ec57SEd Maste return; 761*1323ec57SEd Maste sshkey_free(srk->key); 762*1323ec57SEd Maste freezero(srk->user_id, srk->user_id_len); 763*1323ec57SEd Maste free(srk); 764*1323ec57SEd Maste } 765*1323ec57SEd Maste 766*1323ec57SEd Maste 767*1323ec57SEd Maste void 768*1323ec57SEd Maste sshsk_free_resident_keys(struct sshsk_resident_key **srks, size_t nsrks) 769*1323ec57SEd Maste { 770*1323ec57SEd Maste size_t i; 771*1323ec57SEd Maste 772*1323ec57SEd Maste if (srks == NULL || nsrks == 0) 773*1323ec57SEd Maste return; 774*1323ec57SEd Maste 775*1323ec57SEd Maste for (i = 0; i < nsrks; i++) 776*1323ec57SEd Maste sshsk_free_resident_key(srks[i]); 777*1323ec57SEd Maste free(srks); 778*1323ec57SEd Maste } 779*1323ec57SEd Maste 78019261079SEd Maste int 78119261079SEd Maste sshsk_load_resident(const char *provider_path, const char *device, 782*1323ec57SEd Maste const char *pin, u_int flags, struct sshsk_resident_key ***srksp, 783*1323ec57SEd Maste size_t *nsrksp) 78419261079SEd Maste { 78519261079SEd Maste struct sshsk_provider *skp = NULL; 78619261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 78719261079SEd Maste struct sk_resident_key **rks = NULL; 788*1323ec57SEd Maste size_t i, nrks = 0, nsrks = 0; 789*1323ec57SEd Maste struct sshkey *key = NULL; 790*1323ec57SEd Maste struct sshsk_resident_key *srk = NULL, **srks = NULL, **tmp; 791*1323ec57SEd Maste uint8_t sk_flags; 79219261079SEd Maste struct sk_option **opts = NULL; 79319261079SEd Maste 79419261079SEd Maste debug_f("provider \"%s\"%s", provider_path, 79519261079SEd Maste (pin != NULL && *pin != '\0') ? ", have-pin": ""); 79619261079SEd Maste 797*1323ec57SEd Maste if (srksp == NULL || nsrksp == NULL) 79819261079SEd Maste return SSH_ERR_INVALID_ARGUMENT; 799*1323ec57SEd Maste *srksp = NULL; 800*1323ec57SEd Maste *nsrksp = 0; 80119261079SEd Maste 80219261079SEd Maste if ((r = make_options(device, NULL, &opts)) != 0) 80319261079SEd Maste goto out; 80419261079SEd Maste if ((skp = sshsk_open(provider_path)) == NULL) { 80519261079SEd Maste r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ 80619261079SEd Maste goto out; 80719261079SEd Maste } 80819261079SEd Maste if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) { 80919261079SEd Maste error("Provider \"%s\" returned failure %d", provider_path, r); 81019261079SEd Maste r = skerr_to_ssherr(r); 81119261079SEd Maste goto out; 81219261079SEd Maste } 81319261079SEd Maste for (i = 0; i < nrks; i++) { 814*1323ec57SEd Maste debug3_f("rk %zu: slot %zu, alg %d, app \"%s\", uidlen %zu", 815*1323ec57SEd Maste i, rks[i]->slot, rks[i]->alg, rks[i]->application, 816*1323ec57SEd Maste rks[i]->user_id_len); 81719261079SEd Maste /* XXX need better filter here */ 81819261079SEd Maste if (strncmp(rks[i]->application, "ssh:", 4) != 0) 81919261079SEd Maste continue; 82019261079SEd Maste switch (rks[i]->alg) { 82119261079SEd Maste case SSH_SK_ECDSA: 82219261079SEd Maste case SSH_SK_ED25519: 82319261079SEd Maste break; 82419261079SEd Maste default: 82519261079SEd Maste continue; 82619261079SEd Maste } 827*1323ec57SEd Maste sk_flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY; 82819261079SEd Maste if ((rks[i]->flags & SSH_SK_USER_VERIFICATION_REQD)) 829*1323ec57SEd Maste sk_flags |= SSH_SK_USER_VERIFICATION_REQD; 83019261079SEd Maste if ((r = sshsk_key_from_response(rks[i]->alg, 831*1323ec57SEd Maste rks[i]->application, sk_flags, &rks[i]->key, &key)) != 0) 83219261079SEd Maste goto out; 833*1323ec57SEd Maste if ((srk = calloc(1, sizeof(*srk))) == NULL) { 834*1323ec57SEd Maste error_f("calloc failed"); 835*1323ec57SEd Maste r = SSH_ERR_ALLOC_FAIL; 836*1323ec57SEd Maste goto out; 837*1323ec57SEd Maste } 838*1323ec57SEd Maste srk->key = key; 839*1323ec57SEd Maste key = NULL; /* transferred */ 840*1323ec57SEd Maste if ((srk->user_id = calloc(1, rks[i]->user_id_len)) == NULL) { 841*1323ec57SEd Maste error_f("calloc failed"); 842*1323ec57SEd Maste r = SSH_ERR_ALLOC_FAIL; 843*1323ec57SEd Maste goto out; 844*1323ec57SEd Maste } 845*1323ec57SEd Maste memcpy(srk->user_id, rks[i]->user_id, rks[i]->user_id_len); 846*1323ec57SEd Maste srk->user_id_len = rks[i]->user_id_len; 847*1323ec57SEd Maste if ((tmp = recallocarray(srks, nsrks, nsrks + 1, 84819261079SEd Maste sizeof(*tmp))) == NULL) { 84919261079SEd Maste error_f("recallocarray failed"); 85019261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 85119261079SEd Maste goto out; 85219261079SEd Maste } 853*1323ec57SEd Maste srks = tmp; 854*1323ec57SEd Maste srks[nsrks++] = srk; 855*1323ec57SEd Maste srk = NULL; 85619261079SEd Maste /* XXX synthesise comment */ 85719261079SEd Maste } 85819261079SEd Maste /* success */ 859*1323ec57SEd Maste *srksp = srks; 860*1323ec57SEd Maste *nsrksp = nsrks; 861*1323ec57SEd Maste srks = NULL; 862*1323ec57SEd Maste nsrks = 0; 86319261079SEd Maste r = 0; 86419261079SEd Maste out: 86519261079SEd Maste sshsk_free_options(opts); 86619261079SEd Maste sshsk_free(skp); 86719261079SEd Maste sshsk_free_sk_resident_keys(rks, nrks); 86819261079SEd Maste sshkey_free(key); 869*1323ec57SEd Maste sshsk_free_resident_key(srk); 870*1323ec57SEd Maste sshsk_free_resident_keys(srks, nsrks); 87119261079SEd Maste return r; 87219261079SEd Maste } 87319261079SEd Maste 87419261079SEd Maste #endif /* ENABLE_SK */ 875