upstream: Convert RSA and ECDSA key to the libcrypto EVP_PKEY API.

DSA remains unconverted as it will be removed within six months.

Based on patches originally from Dmitry Belyavskiy, but significantly
reworked based on feedback from Bob Beck, Joel Sing and especially
Theo Buehler (apologies to anyone I've missed).

ok tb@

OpenBSD-Commit-ID: d098744e89f1dc7e5952a6817bef234eced648b5
This commit is contained in:
djm@openbsd.org 2024-08-15 00:51:51 +00:00 committed by Damien Miller
parent 0af06e2c5b
commit 7bdfc20516
No known key found for this signature in database
14 changed files with 759 additions and 532 deletions

View File

@ -1,4 +1,4 @@
/* $OpenBSD: packet.c,v 1.315 2024/05/31 08:49:35 djm Exp $ */
/* $OpenBSD: packet.c,v 1.316 2024/08/15 00:51:51 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -2650,8 +2650,13 @@ sshpkt_put_ec(struct ssh *ssh, const EC_POINT *v, const EC_GROUP *g)
{
return sshbuf_put_ec(ssh->state->outgoing_packet, v, g);
}
#endif /* OPENSSL_HAS_ECC */
int
sshpkt_put_ec_pkey(struct ssh *ssh, EVP_PKEY *pkey)
{
return sshbuf_put_ec_pkey(ssh->state->outgoing_packet, pkey);
}
#endif /* OPENSSL_HAS_ECC */
int
sshpkt_put_bignum2(struct ssh *ssh, const BIGNUM *v)

View File

@ -1,4 +1,4 @@
/* $OpenBSD: packet.h,v 1.98 2024/05/17 06:42:04 jsg Exp $ */
/* $OpenBSD: packet.h,v 1.99 2024/08/15 00:51:51 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -20,6 +20,7 @@
#ifdef WITH_OPENSSL
# include <openssl/bn.h>
# include <openssl/evp.h>
# ifdef OPENSSL_HAS_ECC
# include <openssl/ec.h>
# else /* OPENSSL_HAS_ECC */
@ -32,6 +33,7 @@
# define EC_KEY void
# define EC_GROUP void
# define EC_POINT void
# define EVP_PKEY void
#endif /* WITH_OPENSSL */
#include <signal.h>
@ -191,6 +193,7 @@ int sshpkt_put_string(struct ssh *ssh, const void *v, size_t len);
int sshpkt_put_cstring(struct ssh *ssh, const void *v);
int sshpkt_put_stringb(struct ssh *ssh, const struct sshbuf *v);
int sshpkt_put_ec(struct ssh *ssh, const EC_POINT *v, const EC_GROUP *g);
int sshpkt_put_ec_pkey(struct ssh *ssh, EVP_PKEY *pkey);
int sshpkt_put_bignum2(struct ssh *ssh, const BIGNUM *v);
int sshpkt_get(struct ssh *ssh, void *valp, size_t len);
@ -213,6 +216,7 @@ const u_char *sshpkt_ptr(struct ssh *, size_t *lenp);
# undef EC_KEY
# undef EC_GROUP
# undef EC_POINT
# undef EVP_PKEY
#elif !defined(OPENSSL_HAS_ECC)
# undef EC_KEY
# undef EC_GROUP

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-ecdsa-sk.c,v 1.18 2023/03/08 04:43:12 guenther Exp $ */
/* $OpenBSD: ssh-ecdsa-sk.c,v 1.19 2024/08/15 00:51:51 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2010 Damien Miller. All rights reserved.
@ -237,11 +237,13 @@ ssh_ecdsa_sk_verify(const struct sshkey *key,
struct sshkey_sig_details **detailsp)
{
ECDSA_SIG *esig = NULL;
EVP_MD_CTX *md_ctx = NULL;
BIGNUM *sig_r = NULL, *sig_s = NULL;
u_char sig_flags;
u_char msghash[32], apphash[32], sighash[32];
u_char msghash[32], apphash[32];
u_int sig_counter;
int is_webauthn = 0, ret = SSH_ERR_INTERNAL_ERROR;
u_char *sigb = NULL, *cp;
int is_webauthn = 0, ret = SSH_ERR_INTERNAL_ERROR, len = 0;
struct sshbuf *b = NULL, *sigbuf = NULL, *original_signed = NULL;
struct sshbuf *webauthn_wrapper = NULL, *webauthn_exts = NULL;
char *ktype = NULL, *webauthn_origin = NULL;
@ -252,7 +254,7 @@ ssh_ecdsa_sk_verify(const struct sshkey *key,
if (detailsp != NULL)
*detailsp = NULL;
if (key == NULL || key->ecdsa == NULL ||
if (key == NULL || key->pkey == NULL ||
sshkey_type_plain(key->type) != KEY_ECDSA_SK ||
sig == NULL || siglen == 0)
return SSH_ERR_INVALID_ARGUMENT;
@ -363,21 +365,43 @@ ssh_ecdsa_sk_verify(const struct sshkey *key,
(ret = sshbuf_putb(original_signed, webauthn_exts)) != 0 ||
(ret = sshbuf_put(original_signed, msghash, sizeof(msghash))) != 0)
goto out;
/* Signature is over H(original_signed) */
if ((ret = ssh_digest_buffer(SSH_DIGEST_SHA256, original_signed,
sighash, sizeof(sighash))) != 0)
goto out;
details->sk_counter = sig_counter;
details->sk_flags = sig_flags;
#ifdef DEBUG_SK
fprintf(stderr, "%s: signed buf:\n", __func__);
sshbuf_dump(original_signed, stderr);
fprintf(stderr, "%s: signed hash:\n", __func__);
sshbuf_dump_data(sighash, sizeof(sighash), stderr);
#endif
if ((md_ctx = EVP_MD_CTX_new()) == NULL) {
ret = SSH_ERR_ALLOC_FAIL;
goto out;
}
if ((len = i2d_ECDSA_SIG(esig, NULL)) <= 0) {
len = 0;
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
if ((sigb = calloc(1, len)) == NULL) {
ret = SSH_ERR_ALLOC_FAIL;
goto out;
}
cp = sigb; /* ASN1_item_i2d increments the pointer past the object */
if (i2d_ECDSA_SIG(esig, &cp) != len) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
#ifdef DEBUG_SK
fprintf(stderr, "%s: signed hash:\n", __func__);
sshbuf_dump_data(sigb, len, stderr);
#endif
/* Verify it */
switch (ECDSA_do_verify(sighash, sizeof(sighash), esig, key->ecdsa)) {
if (EVP_DigestVerifyInit(md_ctx, NULL, EVP_sha256(), NULL,
key->pkey) != 1) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
switch (EVP_DigestVerify(md_ctx, sigb, len,
sshbuf_ptr(original_signed), sshbuf_len(original_signed))) {
case 1:
ret = 0;
break;
@ -397,7 +421,6 @@ ssh_ecdsa_sk_verify(const struct sshkey *key,
explicit_bzero(&sig_flags, sizeof(sig_flags));
explicit_bzero(&sig_counter, sizeof(sig_counter));
explicit_bzero(msghash, sizeof(msghash));
explicit_bzero(sighash, sizeof(msghash));
explicit_bzero(apphash, sizeof(apphash));
sshkey_sig_details_free(details);
sshbuf_free(webauthn_wrapper);
@ -410,6 +433,8 @@ ssh_ecdsa_sk_verify(const struct sshkey *key,
BN_clear_free(sig_r);
BN_clear_free(sig_s);
free(ktype);
freezero(sigb, len);
EVP_MD_CTX_free(md_ctx);
return ret;
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-ecdsa.c,v 1.26 2023/03/08 04:43:12 guenther Exp $ */
/* $OpenBSD: ssh-ecdsa.c,v 1.27 2024/08/15 00:51:51 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2010 Damien Miller. All rights reserved.
@ -45,6 +45,61 @@
#include "openbsd-compat/openssl-compat.h"
int
sshkey_ecdsa_fixup_group(EVP_PKEY *k)
{
int nids[] = {
NID_X9_62_prime256v1,
NID_secp384r1,
#ifdef OPENSSL_HAS_NISTP521
NID_secp521r1,
#endif
-1
};
int nid = -1;
u_int i;
const EC_GROUP *g;
EC_KEY *ec = NULL;
EC_GROUP *eg = NULL;
if ((ec = EVP_PKEY_get1_EC_KEY(k)) == NULL ||
(g = EC_KEY_get0_group(ec)) == NULL)
goto out;
/*
* The group may be stored in a ASN.1 encoded private key in one of two
* ways: as a "named group", which is reconstituted by ASN.1 object ID
* or explicit group parameters encoded into the key blob. Only the
* "named group" case sets the group NID for us, but we can figure
* it out for the other case by comparing against all the groups that
* are supported.
*/
if ((nid = EC_GROUP_get_curve_name(g)) > 0)
goto out;
nid = -1;
for (i = 0; nids[i] != -1; i++) {
if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL)
goto out;
if (EC_GROUP_cmp(g, eg, NULL) == 0)
break;
EC_GROUP_free(eg);
eg = NULL;
}
if (nids[i] == -1)
goto out;
/* Use the group with the NID attached */
EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE);
if (EC_KEY_set_group(ec, eg) != 1 ||
EVP_PKEY_set1_EC_KEY(k, ec) != 1)
goto out;
/* success */
nid = nids[i];
out:
EC_KEY_free(ec);
EC_GROUP_free(eg);
return nid;
}
static u_int
ssh_ecdsa_size(const struct sshkey *key)
{
@ -65,30 +120,16 @@ ssh_ecdsa_size(const struct sshkey *key)
static void
ssh_ecdsa_cleanup(struct sshkey *k)
{
EC_KEY_free(k->ecdsa);
k->ecdsa = NULL;
EVP_PKEY_free(k->pkey);
k->pkey = NULL;
}
static int
ssh_ecdsa_equal(const struct sshkey *a, const struct sshkey *b)
{
const EC_GROUP *grp_a, *grp_b;
const EC_POINT *pub_a, *pub_b;
if (a->ecdsa == NULL || b->ecdsa == NULL)
if (a->pkey == NULL || b->pkey == NULL)
return 0;
if ((grp_a = EC_KEY_get0_group(a->ecdsa)) == NULL ||
(grp_b = EC_KEY_get0_group(b->ecdsa)) == NULL)
return 0;
if ((pub_a = EC_KEY_get0_public_key(a->ecdsa)) == NULL ||
(pub_b = EC_KEY_get0_public_key(b->ecdsa)) == NULL)
return 0;
if (EC_GROUP_cmp(grp_a, grp_b, NULL) != 0)
return 0;
if (EC_POINT_cmp(grp_a, pub_a, pub_b, NULL) != 0)
return 0;
return 1;
return EVP_PKEY_cmp(a->pkey, b->pkey) == 1;
}
static int
@ -97,11 +138,11 @@ ssh_ecdsa_serialize_public(const struct sshkey *key, struct sshbuf *b,
{
int r;
if (key->ecdsa == NULL)
if (key->pkey == NULL)
return SSH_ERR_INVALID_ARGUMENT;
if ((r = sshbuf_put_cstring(b,
sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
(r = sshbuf_put_eckey(b, key->ecdsa)) != 0)
(r = sshbuf_put_ec_pkey(b, key->pkey)) != 0)
return r;
return 0;
@ -118,7 +159,7 @@ ssh_ecdsa_serialize_private(const struct sshkey *key, struct sshbuf *b,
return r;
}
if ((r = sshbuf_put_bignum2(b,
EC_KEY_get0_private_key(key->ecdsa))) != 0)
EC_KEY_get0_private_key(EVP_PKEY_get0_EC_KEY(key->pkey)))) != 0)
return r;
return 0;
}
@ -126,31 +167,64 @@ ssh_ecdsa_serialize_private(const struct sshkey *key, struct sshbuf *b,
static int
ssh_ecdsa_generate(struct sshkey *k, int bits)
{
EC_KEY *private;
EVP_PKEY *res = NULL;
EVP_PKEY_CTX *ctx = NULL;
int ret = SSH_ERR_INTERNAL_ERROR;
if ((k->ecdsa_nid = sshkey_ecdsa_bits_to_nid(bits)) == -1)
return SSH_ERR_KEY_LENGTH;
if ((private = EC_KEY_new_by_curve_name(k->ecdsa_nid)) == NULL)
if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) == NULL)
return SSH_ERR_ALLOC_FAIL;
if (EC_KEY_generate_key(private) != 1) {
EC_KEY_free(private);
return SSH_ERR_LIBCRYPTO_ERROR;
if (EVP_PKEY_keygen_init(ctx) <= 0 ||
EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, k->ecdsa_nid) <= 0 ||
EVP_PKEY_keygen(ctx, &res) <= 0) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
k->ecdsa = private;
return 0;
/* success */
k->pkey = res;
res = NULL;
ret = 0;
out:
EVP_PKEY_free(res);
EVP_PKEY_CTX_free(ctx);
return ret;
}
static int
ssh_ecdsa_copy_public(const struct sshkey *from, struct sshkey *to)
{
const EC_KEY *ec_from;
EC_KEY *ec_to = NULL;
int ret = SSH_ERR_INTERNAL_ERROR;
ec_from = EVP_PKEY_get0_EC_KEY(from->pkey);
if (ec_from == NULL)
return SSH_ERR_LIBCRYPTO_ERROR;
to->ecdsa_nid = from->ecdsa_nid;
if ((to->ecdsa = EC_KEY_new_by_curve_name(from->ecdsa_nid)) == NULL)
if ((ec_to = EC_KEY_new_by_curve_name(from->ecdsa_nid)) == NULL)
return SSH_ERR_ALLOC_FAIL;
if (EC_KEY_set_public_key(to->ecdsa,
EC_KEY_get0_public_key(from->ecdsa)) != 1)
return SSH_ERR_LIBCRYPTO_ERROR; /* caller will free k->ecdsa */
return 0;
if (EC_KEY_set_public_key(ec_to,
EC_KEY_get0_public_key(ec_from)) != 1) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
EVP_PKEY_free(to->pkey);
if ((to->pkey = EVP_PKEY_new()) == NULL) {
ret = SSH_ERR_ALLOC_FAIL;
goto out;
}
if (EVP_PKEY_set1_EC_KEY(to->pkey, ec_to) != 1) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
ret = 0;
out:
EC_KEY_free(ec_to);
return ret;
}
static int
@ -159,6 +233,8 @@ ssh_ecdsa_deserialize_public(const char *ktype, struct sshbuf *b,
{
int r;
char *curve = NULL;
EVP_PKEY *pkey = NULL;
EC_KEY *ec = NULL;
if ((key->ecdsa_nid = sshkey_ecdsa_nid_from_name(ktype)) == -1)
return SSH_ERR_INVALID_ARGUMENT;
@ -168,31 +244,39 @@ ssh_ecdsa_deserialize_public(const char *ktype, struct sshbuf *b,
r = SSH_ERR_EC_CURVE_MISMATCH;
goto out;
}
EC_KEY_free(key->ecdsa);
key->ecdsa = NULL;
if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL) {
if ((ec = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
if ((r = sshbuf_get_eckey(b, key->ecdsa)) != 0)
if ((r = sshbuf_get_eckey(b, ec)) != 0)
goto out;
if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa),
EC_KEY_get0_public_key(key->ecdsa)) != 0) {
if (sshkey_ec_validate_public(EC_KEY_get0_group(ec),
EC_KEY_get0_public_key(ec)) != 0) {
r = SSH_ERR_KEY_INVALID_EC_VALUE;
goto out;
}
if ((pkey = EVP_PKEY_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if (EVP_PKEY_set1_EC_KEY(pkey, ec) != 1) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
EVP_PKEY_free(key->pkey);
key->pkey = pkey;
pkey = NULL;
/* success */
r = 0;
#ifdef DEBUG_PK
sshkey_dump_ec_point(EC_KEY_get0_group(key->ecdsa),
EC_KEY_get0_public_key(key->ecdsa));
sshkey_dump_ec_point(
EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(key->pkey)),
EC_KEY_get0_public_key(EVP_PKEY_get0_EC_KEY(key->pkey)));
#endif
out:
EC_KEY_free(ec);
EVP_PKEY_free(pkey);
free(curve);
if (r != 0) {
EC_KEY_free(key->ecdsa);
key->ecdsa = NULL;
}
return r;
}
@ -202,6 +286,7 @@ ssh_ecdsa_deserialize_private(const char *ktype, struct sshbuf *b,
{
int r;
BIGNUM *exponent = NULL;
EC_KEY *ec = NULL;
if (!sshkey_is_cert(key)) {
if ((r = ssh_ecdsa_deserialize_public(ktype, b, key)) != 0)
@ -209,16 +294,25 @@ ssh_ecdsa_deserialize_private(const char *ktype, struct sshbuf *b,
}
if ((r = sshbuf_get_bignum2(b, &exponent)) != 0)
goto out;
if (EC_KEY_set_private_key(key->ecdsa, exponent) != 1) {
if ((ec = EVP_PKEY_get1_EC_KEY(key->pkey)) == NULL) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
if ((r = sshkey_ec_validate_private(key->ecdsa)) != 0)
if (EC_KEY_set_private_key(ec, exponent) != 1) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
if ((r = sshkey_ec_validate_private(ec)) != 0)
goto out;
if (EVP_PKEY_set1_EC_KEY(key->pkey, ec) != 1) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
/* success */
r = 0;
out:
BN_clear_free(exponent);
EC_KEY_free(ec);
return r;
}
@ -229,34 +323,35 @@ ssh_ecdsa_sign(struct sshkey *key,
const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
{
ECDSA_SIG *esig = NULL;
unsigned char *sigb = NULL;
const unsigned char *psig;
const BIGNUM *sig_r, *sig_s;
int hash_alg;
u_char digest[SSH_DIGEST_MAX_LENGTH];
size_t len, hlen;
size_t slen = 0;
struct sshbuf *b = NULL, *bb = NULL;
int ret = SSH_ERR_INTERNAL_ERROR;
int len = 0, ret = SSH_ERR_INTERNAL_ERROR;
if (lenp != NULL)
*lenp = 0;
if (sigp != NULL)
*sigp = NULL;
if (key == NULL || key->ecdsa == NULL ||
if (key == NULL || key->pkey == NULL ||
sshkey_type_plain(key->type) != KEY_ECDSA)
return SSH_ERR_INVALID_ARGUMENT;
if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
(hlen = ssh_digest_bytes(hash_alg)) == 0)
if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1)
return SSH_ERR_INTERNAL_ERROR;
if ((ret = ssh_digest_memory(hash_alg, data, dlen,
digest, sizeof(digest))) != 0)
if ((ret = sshkey_pkey_digest_sign(key->pkey, hash_alg, &sigb, &slen,
data, dlen)) != 0)
goto out;
if ((esig = ECDSA_do_sign(digest, hlen, key->ecdsa)) == NULL) {
psig = sigb;
if ((esig = d2i_ECDSA_SIG(NULL, &psig, slen)) == NULL) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) {
ret = SSH_ERR_ALLOC_FAIL;
goto out;
@ -280,7 +375,7 @@ ssh_ecdsa_sign(struct sshkey *key,
*lenp = len;
ret = 0;
out:
explicit_bzero(digest, sizeof(digest));
freezero(sigb, slen);
sshbuf_free(b);
sshbuf_free(bb);
ECDSA_SIG_free(esig);
@ -295,20 +390,18 @@ ssh_ecdsa_verify(const struct sshkey *key,
{
ECDSA_SIG *esig = NULL;
BIGNUM *sig_r = NULL, *sig_s = NULL;
int hash_alg;
u_char digest[SSH_DIGEST_MAX_LENGTH];
size_t hlen;
int hash_alg, len = 0;
int ret = SSH_ERR_INTERNAL_ERROR;
struct sshbuf *b = NULL, *sigbuf = NULL;
char *ktype = NULL;
unsigned char *sigb = NULL, *cp;
if (key == NULL || key->ecdsa == NULL ||
if (key == NULL || key->pkey == NULL ||
sshkey_type_plain(key->type) != KEY_ECDSA ||
sig == NULL || siglen == 0)
return SSH_ERR_INVALID_ARGUMENT;
if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
(hlen = ssh_digest_bytes(hash_alg)) == 0)
if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1)
return SSH_ERR_INTERNAL_ERROR;
/* fetch signature */
@ -334,6 +427,11 @@ ssh_ecdsa_verify(const struct sshkey *key,
ret = SSH_ERR_INVALID_FORMAT;
goto out;
}
if (sshbuf_len(sigbuf) != 0) {
ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
goto out;
}
if ((esig = ECDSA_SIG_new()) == NULL) {
ret = SSH_ERR_ALLOC_FAIL;
goto out;
@ -344,28 +442,26 @@ ssh_ecdsa_verify(const struct sshkey *key,
}
sig_r = sig_s = NULL; /* transferred */
if (sshbuf_len(sigbuf) != 0) {
ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
goto out;
}
if ((ret = ssh_digest_memory(hash_alg, data, dlen,
digest, sizeof(digest))) != 0)
goto out;
switch (ECDSA_do_verify(digest, hlen, esig, key->ecdsa)) {
case 1:
ret = 0;
break;
case 0:
ret = SSH_ERR_SIGNATURE_INVALID;
goto out;
default:
if ((len = i2d_ECDSA_SIG(esig, NULL)) <= 0) {
len = 0;
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
if ((sigb = calloc(1, len)) == NULL) {
ret = SSH_ERR_ALLOC_FAIL;
goto out;
}
cp = sigb; /* ASN1_item_i2d increments the pointer past the object */
if (i2d_ECDSA_SIG(esig, &cp) != len) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
if ((ret = sshkey_pkey_digest_verify(key->pkey, hash_alg,
data, dlen, sigb, len)) != 0)
goto out;
/* success */
out:
explicit_bzero(digest, sizeof(digest));
freezero(sigb, len);
sshbuf_free(sigbuf);
sshbuf_free(b);
ECDSA_SIG_free(esig);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keygen.c,v 1.472 2024/01/11 01:45:36 djm Exp $ */
/* $OpenBSD: ssh-keygen.c,v 1.473 2024/08/15 00:51:51 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -375,7 +375,8 @@ do_convert_to_pkcs8(struct sshkey *k)
{
switch (sshkey_type_plain(k->type)) {
case KEY_RSA:
if (!PEM_write_RSA_PUBKEY(stdout, k->rsa))
if (!PEM_write_RSA_PUBKEY(stdout,
EVP_PKEY_get0_RSA(k->pkey)))
fatal("PEM_write_RSA_PUBKEY failed");
break;
#ifdef WITH_DSA
@ -386,7 +387,8 @@ do_convert_to_pkcs8(struct sshkey *k)
#endif
#ifdef OPENSSL_HAS_ECC
case KEY_ECDSA:
if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
if (!PEM_write_EC_PUBKEY(stdout,
EVP_PKEY_get0_EC_KEY(k->pkey)))
fatal("PEM_write_EC_PUBKEY failed");
break;
#endif
@ -401,7 +403,8 @@ do_convert_to_pem(struct sshkey *k)
{
switch (sshkey_type_plain(k->type)) {
case KEY_RSA:
if (!PEM_write_RSAPublicKey(stdout, k->rsa))
if (!PEM_write_RSAPublicKey(stdout,
EVP_PKEY_get0_RSA(k->pkey)))
fatal("PEM_write_RSAPublicKey failed");
break;
#ifdef WITH_DSA
@ -412,7 +415,8 @@ do_convert_to_pem(struct sshkey *k)
#endif
#ifdef OPENSSL_HAS_ECC
case KEY_ECDSA:
if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
if (!PEM_write_EC_PUBKEY(stdout,
EVP_PKEY_get0_EC_KEY(k->pkey)))
fatal("PEM_write_EC_PUBKEY failed");
break;
#endif
@ -490,6 +494,8 @@ do_convert_private_ssh2(struct sshbuf *b)
#endif
BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL;
BIGNUM *rsa_p = NULL, *rsa_q = NULL, *rsa_iqmp = NULL;
BIGNUM *rsa_dmp1 = NULL, *rsa_dmq1 = NULL;
RSA *rsa = NULL;
if ((r = sshbuf_get_u32(b, &magic)) != 0)
fatal_fr(r, "parse magic");
@ -584,15 +590,25 @@ do_convert_private_ssh2(struct sshbuf *b)
buffer_get_bignum_bits(b, rsa_iqmp);
buffer_get_bignum_bits(b, rsa_q);
buffer_get_bignum_bits(b, rsa_p);
if (!RSA_set0_key(key->rsa, rsa_n, rsa_e, rsa_d))
if ((r = ssh_rsa_complete_crt_parameters(rsa_d, rsa_p, rsa_q,
rsa_iqmp, &rsa_dmp1, &rsa_dmq1)) != 0)
fatal_fr(r, "generate RSA CRT parameters");
if ((key->pkey = EVP_PKEY_new()) == NULL)
fatal_f("EVP_PKEY_new failed");
if ((rsa = RSA_new()) == NULL)
fatal_f("RSA_new failed");
if (!RSA_set0_key(rsa, rsa_n, rsa_e, rsa_d))
fatal_f("RSA_set0_key failed");
rsa_n = rsa_e = rsa_d = NULL; /* transferred */
if (!RSA_set0_factors(key->rsa, rsa_p, rsa_q))
if (!RSA_set0_factors(rsa, rsa_p, rsa_q))
fatal_f("RSA_set0_factors failed");
rsa_p = rsa_q = NULL; /* transferred */
if ((r = ssh_rsa_complete_crt_parameters(key, rsa_iqmp)) != 0)
fatal_fr(r, "generate RSA parameters");
BN_clear_free(rsa_iqmp);
if (RSA_set0_crt_params(rsa, rsa_dmp1, rsa_dmq1, rsa_iqmp) != 1)
fatal_f("RSA_set0_crt_params failed");
rsa_dmp1 = rsa_dmq1 = rsa_iqmp = NULL;
if (EVP_PKEY_set1_RSA(key->pkey, rsa) != 1)
fatal_f("EVP_PKEY_set1_RSA failed");
RSA_free(rsa);
alg = "rsa-sha2-256";
break;
}
@ -712,7 +728,8 @@ do_convert_from_pkcs8(struct sshkey **k, int *private)
if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
fatal("sshkey_new failed");
(*k)->type = KEY_RSA;
(*k)->rsa = EVP_PKEY_get1_RSA(pubkey);
(*k)->pkey = pubkey;
pubkey = NULL;
break;
#ifdef WITH_DSA
case EVP_PKEY_DSA:
@ -726,9 +743,11 @@ do_convert_from_pkcs8(struct sshkey **k, int *private)
case EVP_PKEY_EC:
if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
fatal("sshkey_new failed");
if (((*k)->ecdsa_nid = sshkey_ecdsa_fixup_group(pubkey)) == -1)
fatal("sshkey_ecdsa_fixup_group failed");
(*k)->type = KEY_ECDSA;
(*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey);
(*k)->ecdsa_nid = sshkey_ecdsa_key_to_nid((*k)->ecdsa);
(*k)->pkey = pubkey;
pubkey = NULL;
break;
#endif
default:
@ -750,8 +769,12 @@ do_convert_from_pem(struct sshkey **k, int *private)
if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) {
if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
fatal("sshkey_new failed");
if (((*k)->pkey = EVP_PKEY_new()) == NULL)
fatal("EVP_PKEY_new failed");
(*k)->type = KEY_RSA;
(*k)->rsa = rsa;
if (EVP_PKEY_set1_RSA((*k)->pkey, rsa) != 1)
fatal("EVP_PKEY_set1_RSA failed");
RSA_free(rsa);
fclose(fp);
return;
}
@ -799,13 +822,15 @@ do_convert_from(struct passwd *pw)
#endif
#ifdef OPENSSL_HAS_ECC
case KEY_ECDSA:
ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL,
NULL, 0, NULL, NULL);
ok = PEM_write_ECPrivateKey(stdout,
EVP_PKEY_get0_EC_KEY(k->pkey), NULL, NULL, 0,
NULL, NULL);
break;
#endif
case KEY_RSA:
ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL,
NULL, 0, NULL, NULL);
ok = PEM_write_RSAPrivateKey(stdout,
EVP_PKEY_get0_RSA(k->pkey), NULL, NULL, 0,
NULL, NULL);
break;
default:
fatal_f("unsupported key type %s", sshkey_type(k));

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-pkcs11-client.c,v 1.19 2023/12/18 14:46:56 djm Exp $ */
/* $OpenBSD: ssh-pkcs11-client.c,v 1.20 2024/08/15 00:51:51 djm Exp $ */
/*
* Copyright (c) 2010 Markus Friedl. All rights reserved.
* Copyright (c) 2014 Pedro Martelletto. All rights reserved.
@ -264,14 +264,17 @@ rsa_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, int padding)
debug3_f("signing with PKCS11 provider %s", helper->path);
if (padding != RSA_PKCS1_PADDING)
goto fail;
key = sshkey_new(KEY_UNSPEC);
if (key == NULL) {
if ((key = sshkey_new(KEY_UNSPEC)) == NULL) {
error_f("sshkey_new failed");
goto fail;
}
if ((key->pkey = EVP_PKEY_new()) == NULL ||
EVP_PKEY_set1_RSA(key->pkey, rsa) != 1) {
error_f("pkey setup failed");
goto fail;
}
key->type = KEY_RSA;
RSA_up_ref(rsa);
key->rsa = rsa;
if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) {
error_fr(r, "encode key");
goto fail;
@ -339,21 +342,22 @@ ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
if ((helper = helper_by_ec(ec)) == NULL || helper->fd == -1)
fatal_f("no helper for PKCS11 key");
debug3_f("signing with PKCS11 provider %s", helper->path);
nid = sshkey_ecdsa_key_to_nid(ec);
if (nid < 0) {
error_f("couldn't get curve nid");
goto fail;
}
key = sshkey_new(KEY_UNSPEC);
if (key == NULL) {
if ((key = sshkey_new(KEY_UNSPEC)) == NULL) {
error_f("sshkey_new failed");
goto fail;
}
key->ecdsa = ec;
if ((key->pkey = EVP_PKEY_new()) == NULL ||
EVP_PKEY_set1_EC_KEY(key->pkey, ec) != 1) {
error("pkey setup failed");
goto fail;
}
if ((nid = sshkey_ecdsa_pkey_to_nid(key->pkey)) < 0) {
error("couldn't get curve nid");
goto fail;
}
key->ecdsa_nid = nid;
key->type = KEY_ECDSA;
EC_KEY_up_ref(ec);
if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) {
error_fr(r, "encode key");
@ -408,16 +412,31 @@ ecdsa_do_finish(EC_KEY *ec)
static void
wrap_key(struct helper *helper, struct sshkey *k)
{
RSA *rsa = NULL;
EC_KEY *ecdsa = NULL;
debug3_f("wrap %s for provider %s", sshkey_type(k), helper->path);
if (k->type == KEY_RSA) {
RSA_set_method(k->rsa, helper->rsa_meth);
if ((rsa = EVP_PKEY_get1_RSA(k->pkey)) == NULL)
fatal_f("no RSA key");
if (RSA_set_method(rsa, helper->rsa_meth) != 1)
fatal_f("RSA_set_method failed");
if (helper->nrsa++ >= INT_MAX)
fatal_f("RSA refcount error");
if (EVP_PKEY_set1_RSA(k->pkey, rsa) != 1)
fatal_f("EVP_PKEY_set1_RSA failed");
RSA_free(rsa);
#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW)
} else if (k->type == KEY_ECDSA) {
EC_KEY_set_method(k->ecdsa, helper->ec_meth);
if ((ecdsa = EVP_PKEY_get1_EC_KEY(k->pkey)) == NULL)
fatal_f("no ECDSA key");
if (EC_KEY_set_method(ecdsa, helper->ec_meth) != 1)
fatal_f("EC_KEY_set_method failed");
if (helper->nec++ >= INT_MAX)
fatal_f("EC refcount error");
if (EVP_PKEY_set1_EC_KEY(k->pkey, ecdsa) != 1)
fatal_f("EVP_PKEY_set1_EC_KEY failed");
EC_KEY_free(ecdsa);
#endif
} else
fatal_f("unknown key type");
@ -437,6 +456,10 @@ pkcs11_make_cert(const struct sshkey *priv,
struct helper *helper = NULL;
struct sshkey *ret;
int r;
RSA *rsa_priv = NULL, *rsa_cert = NULL;
#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW)
EC_KEY *ec_priv = NULL, *ec_cert = NULL;
#endif
debug3_f("private key type %s cert type %s", sshkey_type(priv),
sshkey_type(certpub));
@ -449,24 +472,42 @@ pkcs11_make_cert(const struct sshkey *priv,
}
*certprivp = NULL;
if (priv->type == KEY_RSA) {
if ((helper = helper_by_rsa(priv->rsa)) == NULL ||
if ((rsa_priv = EVP_PKEY_get1_RSA(priv->pkey)) == NULL)
fatal_f("no RSA pkey");
if ((helper = helper_by_rsa(rsa_priv)) == NULL ||
helper->fd == -1)
fatal_f("no helper for PKCS11 RSA key");
if ((r = sshkey_from_private(priv, &ret)) != 0)
fatal_fr(r, "copy key");
RSA_set_method(ret->rsa, helper->rsa_meth);
if ((rsa_cert = EVP_PKEY_get1_RSA(ret->pkey)) == NULL)
fatal_f("no RSA cert pkey");
if (RSA_set_method(rsa_cert, helper->rsa_meth) != 1)
fatal_f("RSA_set_method failed");
if (helper->nrsa++ >= INT_MAX)
fatal_f("RSA refcount error");
if (EVP_PKEY_set1_RSA(ret->pkey, rsa_cert) != 1)
fatal_f("EVP_PKEY_set1_RSA failed");
RSA_free(rsa_priv);
RSA_free(rsa_cert);
#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW)
} else if (priv->type == KEY_ECDSA) {
if ((helper = helper_by_ec(priv->ecdsa)) == NULL ||
if ((ec_priv = EVP_PKEY_get1_EC_KEY(priv->pkey)) == NULL)
fatal_f("no EC pkey");
if ((helper = helper_by_ec(ec_priv)) == NULL ||
helper->fd == -1)
fatal_f("no helper for PKCS11 EC key");
if ((r = sshkey_from_private(priv, &ret)) != 0)
fatal_fr(r, "copy key");
EC_KEY_set_method(ret->ecdsa, helper->ec_meth);
if ((ec_cert = EVP_PKEY_get1_EC_KEY(ret->pkey)) == NULL)
fatal_f("no EC cert pkey");
if (EC_KEY_set_method(ec_cert, helper->ec_meth) != 1)
fatal_f("EC_KEY_set_method failed");
if (helper->nec++ >= INT_MAX)
fatal_f("EC refcount error");
if (EVP_PKEY_set1_EC_KEY(ret->pkey, ec_cert) != 1)
fatal_f("EVP_PKEY_set1_EC_KEY failed");
EC_KEY_free(ec_priv);
EC_KEY_free(ec_cert);
#endif
} else
fatal_f("unknown key type %s", sshkey_type(priv));
@ -485,7 +526,7 @@ pkcs11_make_cert(const struct sshkey *priv,
static int
pkcs11_start_helper_methods(struct helper *helper)
{
RSA_METHOD *rsa_meth;
RSA_METHOD *rsa_meth = NULL;
EC_KEY_METHOD *ec_meth = NULL;
#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW)
int (*ec_init)(EC_KEY *key);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-pkcs11-helper.c,v 1.26 2021/11/18 03:31:44 djm Exp $ */
/* $OpenBSD: ssh-pkcs11-helper.c,v 1.27 2024/08/15 00:51:51 djm Exp $ */
/*
* Copyright (c) 2010 Markus Friedl. All rights reserved.
*
@ -45,6 +45,9 @@
#ifdef ENABLE_PKCS11
#ifdef WITH_OPENSSL
#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/rsa.h>
/* borrows code from sftp-server and ssh-agent */
@ -185,10 +188,13 @@ static void
process_sign(void)
{
u_char *blob, *data, *signature = NULL;
size_t blen, dlen, slen = 0;
int r, ok = -1;
struct sshkey *key, *found;
size_t blen, dlen;
u_int slen = 0;
int len, r, ok = -1;
struct sshkey *key = NULL, *found;
struct sshbuf *msg;
RSA *rsa = NULL;
EC_KEY *ecdsa = NULL;
/* XXX support SHA2 signature flags */
if ((r = sshbuf_get_string(iqueue, &blob, &blen)) != 0 ||
@ -198,41 +204,47 @@ process_sign(void)
if ((r = sshkey_from_blob(blob, blen, &key)) != 0)
fatal_fr(r, "decode key");
else {
if ((found = lookup_key(key)) != NULL) {
if ((found = lookup_key(key)) == NULL)
goto reply;
/* XXX use pkey API properly for signing */
switch (key->type) {
#ifdef WITH_OPENSSL
int ret;
if (key->type == KEY_RSA) {
slen = RSA_size(key->rsa);
signature = xmalloc(slen);
ret = RSA_private_encrypt(dlen, data, signature,
found->rsa, RSA_PKCS1_PADDING);
if (ret != -1) {
slen = ret;
ok = 0;
}
#ifdef OPENSSL_HAS_ECC
} else if (key->type == KEY_ECDSA) {
u_int xslen = ECDSA_size(key->ecdsa);
signature = xmalloc(xslen);
/* "The parameter type is ignored." */
ret = ECDSA_sign(-1, data, dlen, signature,
&xslen, found->ecdsa);
if (ret != 0)
ok = 0;
else
error_f("ECDSA_sign returned %d", ret);
slen = xslen;
#endif /* OPENSSL_HAS_ECC */
} else
error_f("don't know how to sign with key "
"type %d", (int)key->type);
#endif /* WITH_OPENSSL */
case KEY_RSA:
if ((rsa = EVP_PKEY_get1_RSA(found->pkey)) == NULL)
fatal_f("no RSA in pkey");
if ((len = RSA_size(rsa)) < 0)
fatal_f("bad RSA length");
signature = xmalloc(len);
if ((len = RSA_private_encrypt(dlen, data, signature,
rsa, RSA_PKCS1_PADDING)) < 0) {
error_f("RSA_private_encrypt failed");
goto reply;
}
sshkey_free(key);
slen = (u_int)len;
break;
#ifdef OPENSSL_HAS_ECC
case KEY_ECDSA:
if ((ecdsa = EVP_PKEY_get1_EC_KEY(found->pkey)) == NULL)
fatal_f("no ECDSA in pkey");
if ((len = ECDSA_size(ecdsa)) < 0)
fatal_f("bad ECDSA length");
slen = (u_int)len;
signature = xmalloc(slen);
/* "The parameter type is ignored." */
if (!ECDSA_sign(-1, data, dlen, signature, &slen, ecdsa)) {
error_f("ECDSA_sign failed");
goto reply;
}
break;
#endif /* OPENSSL_HAS_ECC */
#endif /* WITH_OPENSSL */
default:
fatal_f("unsupported key type %d", key->type);
}
/* success */
ok = 0;
reply:
if ((msg = sshbuf_new()) == NULL)
fatal_f("sshbuf_new failed");
if (ok == 0) {
@ -243,6 +255,9 @@ process_sign(void)
if ((r = sshbuf_put_u8(msg, SSH2_AGENT_FAILURE)) != 0)
fatal_fr(r, "compose failure response");
}
sshkey_free(key);
RSA_free(rsa);
EC_KEY_free(ecdsa);
free(data);
free(blob);
free(signature);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-pkcs11.c,v 1.62 2024/04/02 12:22:38 deraadt Exp $ */
/* $OpenBSD: ssh-pkcs11.c,v 1.63 2024/08/15 00:51:51 djm Exp $ */
/*
* Copyright (c) 2010 Markus Friedl. All rights reserved.
* Copyright (c) 2014 Pedro Martelletto. All rights reserved.
@ -502,8 +502,10 @@ pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
}
RSA_set_method(rsa, rsa_method);
RSA_set_ex_data(rsa, rsa_idx, k11);
if (RSA_set_method(rsa, rsa_method) != 1)
fatal_f("RSA_set_method failed");
if (RSA_set_ex_data(rsa, rsa_idx, k11) != 1)
fatal_f("RSA_set_ex_data failed");
return (0);
}
@ -615,8 +617,10 @@ pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
k11->keyid = xmalloc(k11->keyid_len);
memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
}
EC_KEY_set_method(ec, ec_key_method);
EC_KEY_set_ex_data(ec, ec_key_idx, k11);
if (EC_KEY_set_method(ec, ec_key_method) != 1)
fatal_f("EC_KEY_set_method failed");
if (EC_KEY_set_ex_data(ec, ec_key_idx, k11) != 1)
fatal_f("EC_KEY_set_ex_data failed");
return (0);
}
@ -803,11 +807,14 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
goto fail;
}
key->ecdsa = ec;
EVP_PKEY_free(key->pkey);
if ((key->pkey = EVP_PKEY_new()) == NULL)
fatal("EVP_PKEY_new failed");
if (EVP_PKEY_set1_EC_KEY(key->pkey, ec) != 1)
fatal("EVP_PKEY_set1_EC_KEY failed");
key->ecdsa_nid = nid;
key->type = KEY_ECDSA;
key->flags |= SSHKEY_FLAG_EXT;
ec = NULL; /* now owned by key */
fail:
for (i = 0; i < 3; i++)
@ -899,10 +906,13 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
goto fail;
}
key->rsa = rsa;
EVP_PKEY_free(key->pkey);
if ((key->pkey = EVP_PKEY_new()) == NULL)
fatal("EVP_PKEY_new failed");
if (EVP_PKEY_set1_RSA(key->pkey, rsa) != 1)
fatal("EVP_PKEY_set1_RSA failed");
key->type = KEY_RSA;
key->flags |= SSHKEY_FLAG_EXT;
rsa = NULL; /* now owned by key */
fail:
for (i = 0; i < 3; i++)
@ -1014,10 +1024,13 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
goto out;
}
key->rsa = rsa;
EVP_PKEY_free(key->pkey);
if ((key->pkey = EVP_PKEY_new()) == NULL)
fatal("EVP_PKEY_new failed");
if (EVP_PKEY_set1_RSA(key->pkey, rsa) != 1)
fatal("EVP_PKEY_set1_RSA failed");
key->type = KEY_RSA;
key->flags |= SSHKEY_FLAG_EXT;
rsa = NULL; /* now owned by key */
#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW)
} else if (EVP_PKEY_base_id(evp) == EVP_PKEY_EC) {
if (EVP_PKEY_get0_EC_KEY(evp) == NULL) {
@ -1044,11 +1057,14 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
goto out;
}
key->ecdsa = ec;
EVP_PKEY_free(key->pkey);
if ((key->pkey = EVP_PKEY_new()) == NULL)
fatal("EVP_PKEY_new failed");
if (EVP_PKEY_set1_EC_KEY(key->pkey, ec) != 1)
fatal("EVP_PKEY_set1_EC_KEY failed");
key->ecdsa_nid = nid;
key->type = KEY_ECDSA;
key->flags |= SSHKEY_FLAG_EXT;
ec = NULL; /* now owned by key */
#endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */
} else {
error("unknown certificate key type");

389
ssh-rsa.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-rsa.c,v 1.79 2023/03/05 05:34:09 dtucker Exp $ */
/* $OpenBSD: ssh-rsa.c,v 1.80 2024/08/15 00:51:51 djm Exp $ */
/*
* Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org>
*
@ -36,23 +36,18 @@
#include "openbsd-compat/openssl-compat.h"
static int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *);
static u_int
ssh_rsa_size(const struct sshkey *key)
ssh_rsa_size(const struct sshkey *k)
{
const BIGNUM *rsa_n;
if (key->rsa == NULL)
if (k->pkey == NULL)
return 0;
RSA_get0_key(key->rsa, &rsa_n, NULL, NULL);
return BN_num_bits(rsa_n);
return EVP_PKEY_bits(k->pkey);
}
static int
ssh_rsa_alloc(struct sshkey *k)
{
if ((k->rsa = RSA_new()) == NULL)
if ((k->pkey = EVP_PKEY_new()) == NULL)
return SSH_ERR_ALLOC_FAIL;
return 0;
}
@ -60,29 +55,16 @@ ssh_rsa_alloc(struct sshkey *k)
static void
ssh_rsa_cleanup(struct sshkey *k)
{
RSA_free(k->rsa);
k->rsa = NULL;
EVP_PKEY_free(k->pkey);
k->pkey = NULL;
}
static int
ssh_rsa_equal(const struct sshkey *a, const struct sshkey *b)
{
const BIGNUM *rsa_e_a, *rsa_n_a;
const BIGNUM *rsa_e_b, *rsa_n_b;
if (a->rsa == NULL || b->rsa == NULL)
if (a->pkey == NULL || b->pkey == NULL)
return 0;
RSA_get0_key(a->rsa, &rsa_n_a, &rsa_e_a, NULL);
RSA_get0_key(b->rsa, &rsa_n_b, &rsa_e_b, NULL);
if (rsa_e_a == NULL || rsa_e_b == NULL)
return 0;
if (rsa_n_a == NULL || rsa_n_b == NULL)
return 0;
if (BN_cmp(rsa_e_a, rsa_e_b) != 0)
return 0;
if (BN_cmp(rsa_n_a, rsa_n_b) != 0)
return 0;
return 1;
return EVP_PKEY_cmp(a->pkey, b->pkey) == 1;
}
static int
@ -91,10 +73,14 @@ ssh_rsa_serialize_public(const struct sshkey *key, struct sshbuf *b,
{
int r;
const BIGNUM *rsa_n, *rsa_e;
const RSA *rsa;
if (key->rsa == NULL)
if (key->pkey == NULL)
return SSH_ERR_INVALID_ARGUMENT;
RSA_get0_key(key->rsa, &rsa_n, &rsa_e, NULL);
if ((rsa = EVP_PKEY_get0_RSA(key->pkey)) == NULL)
return SSH_ERR_LIBCRYPTO_ERROR;
RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL);
if ((r = sshbuf_put_bignum2(b, rsa_e)) != 0 ||
(r = sshbuf_put_bignum2(b, rsa_n)) != 0)
return r;
@ -108,10 +94,13 @@ ssh_rsa_serialize_private(const struct sshkey *key, struct sshbuf *b,
{
int r;
const BIGNUM *rsa_n, *rsa_e, *rsa_d, *rsa_iqmp, *rsa_p, *rsa_q;
const RSA *rsa;
RSA_get0_key(key->rsa, &rsa_n, &rsa_e, &rsa_d);
RSA_get0_factors(key->rsa, &rsa_p, &rsa_q);
RSA_get0_crt_params(key->rsa, NULL, NULL, &rsa_iqmp);
if ((rsa = EVP_PKEY_get0_RSA(key->pkey)) == NULL)
return SSH_ERR_LIBCRYPTO_ERROR;
RSA_get0_key(rsa, &rsa_n, &rsa_e, &rsa_d);
RSA_get0_factors(rsa, &rsa_p, &rsa_q);
RSA_get0_crt_params(rsa, NULL, NULL, &rsa_iqmp);
if (!sshkey_is_cert(key)) {
/* Note: can't reuse ssh_rsa_serialize_public: e, n vs. n, e */
@ -131,28 +120,36 @@ ssh_rsa_serialize_private(const struct sshkey *key, struct sshbuf *b,
static int
ssh_rsa_generate(struct sshkey *k, int bits)
{
RSA *private = NULL;
BIGNUM *f4 = NULL;
EVP_PKEY_CTX *ctx = NULL;
EVP_PKEY *res = NULL;
int ret = SSH_ERR_INTERNAL_ERROR;
if (bits < SSH_RSA_MINIMUM_MODULUS_SIZE ||
bits > SSHBUF_MAX_BIGNUM * 8)
return SSH_ERR_KEY_LENGTH;
if ((private = RSA_new()) == NULL || (f4 = BN_new()) == NULL) {
if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL)) == NULL) {
ret = SSH_ERR_ALLOC_FAIL;
goto out;
}
if (!BN_set_word(f4, RSA_F4) ||
!RSA_generate_key_ex(private, bits, f4, NULL)) {
if (EVP_PKEY_keygen_init(ctx) <= 0) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
k->rsa = private;
private = NULL;
if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0) {
ret = SSH_ERR_KEY_LENGTH;
goto out;
}
if (EVP_PKEY_keygen(ctx, &res) <= 0 || res == NULL) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
/* success */
k->pkey = res;
ret = 0;
out:
RSA_free(private);
BN_free(f4);
EVP_PKEY_CTX_free(ctx);
return ret;
}
@ -162,21 +159,33 @@ ssh_rsa_copy_public(const struct sshkey *from, struct sshkey *to)
const BIGNUM *rsa_n, *rsa_e;
BIGNUM *rsa_n_dup = NULL, *rsa_e_dup = NULL;
int r = SSH_ERR_INTERNAL_ERROR;
const RSA *rsa_from;
RSA *rsa_to = NULL;
RSA_get0_key(from->rsa, &rsa_n, &rsa_e, NULL);
if ((rsa_from = EVP_PKEY_get0_RSA(from->pkey)) == NULL ||
(rsa_to = RSA_new()) == NULL)
return SSH_ERR_LIBCRYPTO_ERROR;
RSA_get0_key(rsa_from, &rsa_n, &rsa_e, NULL);
if ((rsa_n_dup = BN_dup(rsa_n)) == NULL ||
(rsa_e_dup = BN_dup(rsa_e)) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if (!RSA_set0_key(to->rsa, rsa_n_dup, rsa_e_dup, NULL)) {
if (!RSA_set0_key(rsa_to, rsa_n_dup, rsa_e_dup, NULL)) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
rsa_n_dup = rsa_e_dup = NULL; /* transferred */
if (EVP_PKEY_set1_RSA(to->pkey, rsa_to) != 1) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
/* success */
r = 0;
out:
RSA_free(rsa_to);
BN_clear_free(rsa_n_dup);
BN_clear_free(rsa_e_dup);
return r;
@ -188,25 +197,34 @@ ssh_rsa_deserialize_public(const char *ktype, struct sshbuf *b,
{
int ret = SSH_ERR_INTERNAL_ERROR;
BIGNUM *rsa_n = NULL, *rsa_e = NULL;
RSA *rsa = NULL;
if ((rsa = RSA_new()) == NULL)
return SSH_ERR_LIBCRYPTO_ERROR;
if (sshbuf_get_bignum2(b, &rsa_e) != 0 ||
sshbuf_get_bignum2(b, &rsa_n) != 0) {
ret = SSH_ERR_INVALID_FORMAT;
goto out;
}
if (!RSA_set0_key(key->rsa, rsa_n, rsa_e, NULL)) {
if (!RSA_set0_key(rsa, rsa_n, rsa_e, NULL)) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
rsa_n = rsa_e = NULL; /* transferred */
if (EVP_PKEY_set1_RSA(key->pkey, rsa) != 1) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
if ((ret = sshkey_check_rsa_length(key, 0)) != 0)
goto out;
#ifdef DEBUG_PK
RSA_print_fp(stderr, key->rsa, 8);
RSA_print_fp(stderr, rsa, 8);
#endif
/* success */
ret = 0;
out:
RSA_free(rsa);
BN_clear_free(rsa_n);
BN_clear_free(rsa_e);
return ret;
@ -219,13 +237,25 @@ ssh_rsa_deserialize_private(const char *ktype, struct sshbuf *b,
int r;
BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL;
BIGNUM *rsa_iqmp = NULL, *rsa_p = NULL, *rsa_q = NULL;
BIGNUM *rsa_dmp1 = NULL, *rsa_dmq1 = NULL;
RSA *rsa = NULL;
/* Note: can't reuse ssh_rsa_deserialize_public: e, n vs. n, e */
if (!sshkey_is_cert(key)) {
if (sshkey_is_cert(key)) {
/* sshkey_private_deserialize already has pubkey from cert */
if ((rsa = EVP_PKEY_get1_RSA(key->pkey)) == NULL) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
} else {
if ((rsa = RSA_new()) == NULL) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
/* Note: can't reuse ssh_rsa_deserialize_public: e,n vs. n,e */
if ((r = sshbuf_get_bignum2(b, &rsa_n)) != 0 ||
(r = sshbuf_get_bignum2(b, &rsa_e)) != 0)
goto out;
if (!RSA_set0_key(key->rsa, rsa_n, rsa_e, NULL)) {
if (!RSA_set0_key(rsa, rsa_n, rsa_e, NULL)) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
@ -236,33 +266,46 @@ ssh_rsa_deserialize_private(const char *ktype, struct sshbuf *b,
(r = sshbuf_get_bignum2(b, &rsa_p)) != 0 ||
(r = sshbuf_get_bignum2(b, &rsa_q)) != 0)
goto out;
if (!RSA_set0_key(key->rsa, NULL, NULL, rsa_d)) {
if ((r = ssh_rsa_complete_crt_parameters(rsa_d, rsa_p, rsa_q,
rsa_iqmp, &rsa_dmp1, &rsa_dmq1)) != 0)
goto out;
if (!RSA_set0_key(rsa, NULL, NULL, rsa_d)) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
rsa_d = NULL; /* transferred */
if (!RSA_set0_factors(key->rsa, rsa_p, rsa_q)) {
if (!RSA_set0_factors(rsa, rsa_p, rsa_q)) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
rsa_p = rsa_q = NULL; /* transferred */
if ((r = sshkey_check_rsa_length(key, 0)) != 0)
goto out;
if ((r = ssh_rsa_complete_crt_parameters(key, rsa_iqmp)) != 0)
goto out;
if (RSA_blinding_on(key->rsa, NULL) != 1) {
if (!RSA_set0_crt_params(rsa, rsa_dmp1, rsa_dmq1, rsa_iqmp)) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
rsa_dmp1 = rsa_dmq1 = rsa_iqmp = NULL;
if (RSA_blinding_on(rsa, NULL) != 1) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
if (EVP_PKEY_set1_RSA(key->pkey, rsa) != 1) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
if ((r = sshkey_check_rsa_length(key, 0)) != 0)
goto out;
/* success */
r = 0;
out:
RSA_free(rsa);
BN_clear_free(rsa_n);
BN_clear_free(rsa_e);
BN_clear_free(rsa_d);
BN_clear_free(rsa_p);
BN_clear_free(rsa_q);
BN_clear_free(rsa_iqmp);
BN_clear_free(rsa_dmp1);
BN_clear_free(rsa_dmq1);
return r;
}
@ -317,45 +360,23 @@ rsa_hash_id_from_keyname(const char *alg)
return -1;
}
static int
rsa_hash_alg_nid(int type)
{
switch (type) {
case SSH_DIGEST_SHA1:
return NID_sha1;
case SSH_DIGEST_SHA256:
return NID_sha256;
case SSH_DIGEST_SHA512:
return NID_sha512;
default:
return -1;
}
}
int
ssh_rsa_complete_crt_parameters(struct sshkey *key, const BIGNUM *iqmp)
ssh_rsa_complete_crt_parameters(const BIGNUM *rsa_d, const BIGNUM *rsa_p,
const BIGNUM *rsa_q, const BIGNUM *rsa_iqmp, BIGNUM **rsa_dmp1,
BIGNUM **rsa_dmq1)
{
const BIGNUM *rsa_p, *rsa_q, *rsa_d;
BIGNUM *aux = NULL, *d_consttime = NULL;
BIGNUM *rsa_dmq1 = NULL, *rsa_dmp1 = NULL, *rsa_iqmp = NULL;
BN_CTX *ctx = NULL;
int r;
if (key == NULL || key->rsa == NULL ||
sshkey_type_plain(key->type) != KEY_RSA)
return SSH_ERR_INVALID_ARGUMENT;
RSA_get0_key(key->rsa, NULL, NULL, &rsa_d);
RSA_get0_factors(key->rsa, &rsa_p, &rsa_q);
*rsa_dmq1 = *rsa_dmp1 = NULL;
if ((ctx = BN_CTX_new()) == NULL)
return SSH_ERR_ALLOC_FAIL;
if ((aux = BN_new()) == NULL ||
(rsa_dmq1 = BN_new()) == NULL ||
(rsa_dmp1 = BN_new()) == NULL)
(*rsa_dmq1 = BN_new()) == NULL ||
(*rsa_dmp1 = BN_new()) == NULL)
return SSH_ERR_ALLOC_FAIL;
if ((d_consttime = BN_dup(rsa_d)) == NULL ||
(rsa_iqmp = BN_dup(iqmp)) == NULL) {
if ((d_consttime = BN_dup(rsa_d)) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
@ -363,25 +384,17 @@ ssh_rsa_complete_crt_parameters(struct sshkey *key, const BIGNUM *iqmp)
BN_set_flags(d_consttime, BN_FLG_CONSTTIME);
if ((BN_sub(aux, rsa_q, BN_value_one()) == 0) ||
(BN_mod(rsa_dmq1, d_consttime, aux, ctx) == 0) ||
(BN_mod(*rsa_dmq1, d_consttime, aux, ctx) == 0) ||
(BN_sub(aux, rsa_p, BN_value_one()) == 0) ||
(BN_mod(rsa_dmp1, d_consttime, aux, ctx) == 0)) {
(BN_mod(*rsa_dmp1, d_consttime, aux, ctx) == 0)) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
if (!RSA_set0_crt_params(key->rsa, rsa_dmp1, rsa_dmq1, rsa_iqmp)) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
rsa_dmp1 = rsa_dmq1 = rsa_iqmp = NULL; /* transferred */
/* success */
r = 0;
out:
BN_clear_free(aux);
BN_clear_free(d_consttime);
BN_clear_free(rsa_dmp1);
BN_clear_free(rsa_dmq1);
BN_clear_free(rsa_iqmp);
BN_CTX_free(ctx);
return r;
}
@ -393,11 +406,10 @@ ssh_rsa_sign(struct sshkey *key,
const u_char *data, size_t datalen,
const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
{
const BIGNUM *rsa_n;
u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL;
size_t slen = 0;
u_int hlen, len;
int nid, hash_alg, ret = SSH_ERR_INTERNAL_ERROR;
u_char *sig = NULL;
size_t diff, len = 0;
int slen = 0;
int hash_alg, ret = SSH_ERR_INTERNAL_ERROR;
struct sshbuf *b = NULL;
if (lenp != NULL)
@ -409,41 +421,28 @@ ssh_rsa_sign(struct sshkey *key,
hash_alg = SSH_DIGEST_SHA1;
else
hash_alg = rsa_hash_id_from_keyname(alg);
if (key == NULL || key->rsa == NULL || hash_alg == -1 ||
if (key == NULL || key->pkey == NULL || hash_alg == -1 ||
sshkey_type_plain(key->type) != KEY_RSA)
return SSH_ERR_INVALID_ARGUMENT;
RSA_get0_key(key->rsa, &rsa_n, NULL, NULL);
if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
return SSH_ERR_KEY_LENGTH;
slen = RSA_size(key->rsa);
slen = EVP_PKEY_size(key->pkey);
if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM)
return SSH_ERR_INVALID_ARGUMENT;
if (EVP_PKEY_bits(key->pkey) < SSH_RSA_MINIMUM_MODULUS_SIZE)
return SSH_ERR_KEY_LENGTH;
/* hash the data */
nid = rsa_hash_alg_nid(hash_alg);
if ((hlen = ssh_digest_bytes(hash_alg)) == 0)
return SSH_ERR_INTERNAL_ERROR;
if ((ret = ssh_digest_memory(hash_alg, data, datalen,
digest, sizeof(digest))) != 0)
if ((ret = sshkey_pkey_digest_sign(key->pkey, hash_alg, &sig, &len,
data, datalen)) < 0)
goto out;
if ((sig = malloc(slen)) == NULL) {
ret = SSH_ERR_ALLOC_FAIL;
goto out;
}
if (RSA_sign(nid, digest, hlen, sig, &len, key->rsa) != 1) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
if (len < slen) {
size_t diff = slen - len;
if (len < (size_t)slen) {
diff = slen - len;
memmove(sig + diff, sig, len);
explicit_bzero(sig, diff);
} else if (len > slen) {
} else if (len > (size_t)slen) {
ret = SSH_ERR_INTERNAL_ERROR;
goto out;
}
/* encode signature */
if ((b = sshbuf_new()) == NULL) {
ret = SSH_ERR_ALLOC_FAIL;
@ -464,7 +463,6 @@ ssh_rsa_sign(struct sshkey *key,
*lenp = len;
ret = 0;
out:
explicit_bzero(digest, sizeof(digest));
freezero(sig, slen);
sshbuf_free(b);
return ret;
@ -476,19 +474,17 @@ ssh_rsa_verify(const struct sshkey *key,
const u_char *data, size_t dlen, const char *alg, u_int compat,
struct sshkey_sig_details **detailsp)
{
const BIGNUM *rsa_n;
char *sigtype = NULL;
int hash_alg, want_alg, ret = SSH_ERR_INTERNAL_ERROR;
size_t len = 0, diff, modlen, hlen;
size_t len = 0, diff, modlen, rsasize;
struct sshbuf *b = NULL;
u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL;
if (key == NULL || key->rsa == NULL ||
if (key == NULL || key->pkey == NULL ||
sshkey_type_plain(key->type) != KEY_RSA ||
sig == NULL || siglen == 0)
return SSH_ERR_INVALID_ARGUMENT;
RSA_get0_key(key->rsa, &rsa_n, NULL, NULL);
if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
if (EVP_PKEY_bits(key->pkey) < SSH_RSA_MINIMUM_MODULUS_SIZE)
return SSH_ERR_KEY_LENGTH;
if ((b = sshbuf_from(sig, siglen)) == NULL)
@ -524,7 +520,7 @@ ssh_rsa_verify(const struct sshkey *key,
goto out;
}
/* RSA_verify expects a signature of RSA_size */
modlen = RSA_size(key->rsa);
modlen = EVP_PKEY_size(key->pkey);
if (len > modlen) {
ret = SSH_ERR_KEY_BITS_MISMATCH;
goto out;
@ -540,16 +536,16 @@ ssh_rsa_verify(const struct sshkey *key,
explicit_bzero(sigblob, diff);
len = modlen;
}
if ((hlen = ssh_digest_bytes(hash_alg)) == 0) {
ret = SSH_ERR_INTERNAL_ERROR;
rsasize = EVP_PKEY_size(key->pkey);
if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM ||
len == 0 || len > rsasize) {
ret = SSH_ERR_INVALID_ARGUMENT;
goto out;
}
if ((ret = ssh_digest_memory(hash_alg, data, dlen,
digest, sizeof(digest))) != 0)
goto out;
ret = sshkey_pkey_digest_verify(key->pkey, hash_alg, data, dlen,
sigblob, len);
ret = openssh_RSA_verify(hash_alg, digest, hlen, sigblob, len,
key->rsa);
out:
freezero(sigblob, len);
free(sigtype);
@ -558,125 +554,6 @@ ssh_rsa_verify(const struct sshkey *key,
return ret;
}
/*
* See:
* http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/
* ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn
*/
/*
* id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
* oiw(14) secsig(3) algorithms(2) 26 }
*/
static const u_char id_sha1[] = {
0x30, 0x21, /* type Sequence, length 0x21 (33) */
0x30, 0x09, /* type Sequence, length 0x09 */
0x06, 0x05, /* type OID, length 0x05 */
0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */
0x05, 0x00, /* NULL */
0x04, 0x14 /* Octet string, length 0x14 (20), followed by sha1 hash */
};
/*
* See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html
* id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840)
* organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2)
* id-sha256(1) }
*/
static const u_char id_sha256[] = {
0x30, 0x31, /* type Sequence, length 0x31 (49) */
0x30, 0x0d, /* type Sequence, length 0x0d (13) */
0x06, 0x09, /* type OID, length 0x09 */
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* id-sha256 */
0x05, 0x00, /* NULL */
0x04, 0x20 /* Octet string, length 0x20 (32), followed by sha256 hash */
};
/*
* See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html
* id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840)
* organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2)
* id-sha256(3) }
*/
static const u_char id_sha512[] = {
0x30, 0x51, /* type Sequence, length 0x51 (81) */
0x30, 0x0d, /* type Sequence, length 0x0d (13) */
0x06, 0x09, /* type OID, length 0x09 */
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, /* id-sha512 */
0x05, 0x00, /* NULL */
0x04, 0x40 /* Octet string, length 0x40 (64), followed by sha512 hash */
};
static int
rsa_hash_alg_oid(int hash_alg, const u_char **oidp, size_t *oidlenp)
{
switch (hash_alg) {
case SSH_DIGEST_SHA1:
*oidp = id_sha1;
*oidlenp = sizeof(id_sha1);
break;
case SSH_DIGEST_SHA256:
*oidp = id_sha256;
*oidlenp = sizeof(id_sha256);
break;
case SSH_DIGEST_SHA512:
*oidp = id_sha512;
*oidlenp = sizeof(id_sha512);
break;
default:
return SSH_ERR_INVALID_ARGUMENT;
}
return 0;
}
static int
openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen,
u_char *sigbuf, size_t siglen, RSA *rsa)
{
size_t rsasize = 0, oidlen = 0, hlen = 0;
int ret, len, oidmatch, hashmatch;
const u_char *oid = NULL;
u_char *decrypted = NULL;
if ((ret = rsa_hash_alg_oid(hash_alg, &oid, &oidlen)) != 0)
return ret;
ret = SSH_ERR_INTERNAL_ERROR;
hlen = ssh_digest_bytes(hash_alg);
if (hashlen != hlen) {
ret = SSH_ERR_INVALID_ARGUMENT;
goto done;
}
rsasize = RSA_size(rsa);
if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM ||
siglen == 0 || siglen > rsasize) {
ret = SSH_ERR_INVALID_ARGUMENT;
goto done;
}
if ((decrypted = malloc(rsasize)) == NULL) {
ret = SSH_ERR_ALLOC_FAIL;
goto done;
}
if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa,
RSA_PKCS1_PADDING)) < 0) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto done;
}
if (len < 0 || (size_t)len != hlen + oidlen) {
ret = SSH_ERR_INVALID_FORMAT;
goto done;
}
oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0;
hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0;
if (!oidmatch || !hashmatch) {
ret = SSH_ERR_SIGNATURE_INVALID;
goto done;
}
ret = 0;
done:
freezero(decrypted, rsasize);
return ret;
}
static const struct sshkey_impl_funcs sshkey_rsa_funcs = {
/* .size = */ ssh_rsa_size,
/* .alloc = */ ssh_rsa_alloc,

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-sk.c,v 1.40 2023/07/19 14:02:27 djm Exp $ */
/* $OpenBSD: ssh-sk.c,v 1.41 2024/08/15 00:51:51 djm Exp $ */
/*
* Copyright (c) 2019 Google LLC
*
@ -32,6 +32,7 @@
#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
#include <openssl/objects.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
#include "log.h"
@ -207,7 +208,9 @@ sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
{
struct sshkey *key = NULL;
struct sshbuf *b = NULL;
EC_KEY *ecdsa = NULL;
EC_POINT *q = NULL;
const EC_GROUP *g = NULL;
int r;
*keyp = NULL;
@ -217,8 +220,9 @@ sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
goto out;
}
key->ecdsa_nid = NID_X9_62_prime256v1;
if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL ||
(q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL ||
if ((ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL ||
(g = EC_KEY_get0_group(ecdsa)) == NULL ||
(q = EC_POINT_new(g)) == NULL ||
(b = sshbuf_new()) == NULL) {
error_f("allocation failed");
r = SSH_ERR_ALLOC_FAIL;
@ -229,30 +233,41 @@ sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
error_fr(r, "sshbuf_put_string");
goto out;
}
if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) {
if ((r = sshbuf_get_ec(b, q, g)) != 0) {
error_fr(r, "parse");
r = SSH_ERR_INVALID_FORMAT;
goto out;
}
if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) {
if (sshkey_ec_validate_public(g, q) != 0) {
error("Authenticator returned invalid ECDSA key");
r = SSH_ERR_KEY_INVALID_EC_VALUE;
goto out;
}
if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
if (EC_KEY_set_public_key(ecdsa, q) != 1) {
/* XXX assume it is a allocation error */
error_f("allocation failed");
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if ((key->pkey = EVP_PKEY_new()) == NULL) {
error_f("allocation failed");
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if (EVP_PKEY_set1_EC_KEY(key->pkey, ecdsa) != 1) {
error_f("Assigning EC_KEY failed");
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
/* success */
*keyp = key;
key = NULL; /* transferred */
r = 0;
out:
EC_POINT_free(q);
sshkey_free(key);
sshbuf_free(b);
EC_KEY_free(ecdsa);
EC_POINT_free(q);
return r;
}
#endif /* WITH_OPENSSL */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshbuf-getput-crypto.c,v 1.11 2024/02/01 02:37:33 djm Exp $ */
/* $OpenBSD: sshbuf-getput-crypto.c,v 1.12 2024/08/15 00:51:51 djm Exp $ */
/*
* Copyright (c) 2011 Damien Miller
*
@ -177,4 +177,13 @@ sshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v)
EC_KEY_get0_group(v));
}
#endif /* OPENSSL_HAS_ECC */
int
sshbuf_put_ec_pkey(struct sshbuf *buf, EVP_PKEY *pkey)
{
const EC_KEY *ec;
if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL)
return SSH_ERR_LIBCRYPTO_ERROR;
return sshbuf_put_eckey(buf, ec);
}
#endif /* WITH_OPENSSL */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshbuf.h,v 1.28 2022/12/02 04:40:27 djm Exp $ */
/* $OpenBSD: sshbuf.h,v 1.29 2024/08/15 00:51:51 djm Exp $ */
/*
* Copyright (c) 2011 Damien Miller
*
@ -23,6 +23,7 @@
#include <stdio.h>
#ifdef WITH_OPENSSL
# include <openssl/bn.h>
# include <openssl/evp.h>
# ifdef OPENSSL_HAS_ECC
# include <openssl/ec.h>
# endif /* OPENSSL_HAS_ECC */
@ -223,6 +224,7 @@ int sshbuf_get_ec(struct sshbuf *buf, EC_POINT *v, const EC_GROUP *g);
int sshbuf_get_eckey(struct sshbuf *buf, EC_KEY *v);
int sshbuf_put_ec(struct sshbuf *buf, const EC_POINT *v, const EC_GROUP *g);
int sshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v);
int sshbuf_put_ec_pkey(struct sshbuf *buf, EVP_PKEY *pkey);
# endif /* OPENSSL_HAS_ECC */
#endif /* WITH_OPENSSL */

231
sshkey.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.c,v 1.142 2024/01/11 01:45:36 djm Exp $ */
/* $OpenBSD: sshkey.c,v 1.143 2024/08/15 00:51:51 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Alexander von Gernler. All rights reserved.
@ -481,6 +481,98 @@ sshkey_type_certified(int type)
}
#ifdef WITH_OPENSSL
static const EVP_MD *
ssh_digest_to_md(int hash_alg)
{
switch (hash_alg) {
case SSH_DIGEST_SHA1:
return EVP_sha1();
case SSH_DIGEST_SHA256:
return EVP_sha256();
case SSH_DIGEST_SHA384:
return EVP_sha384();
case SSH_DIGEST_SHA512:
return EVP_sha512();
}
return NULL;
}
int
sshkey_pkey_digest_sign(EVP_PKEY *pkey, int hash_alg, u_char **sigp,
size_t *lenp, const u_char *data, size_t datalen)
{
EVP_MD_CTX *ctx = NULL;
u_char *sig = NULL;
int ret;
size_t slen;
const EVP_MD *evpmd;
*sigp = NULL;
*lenp = 0;
slen = EVP_PKEY_size(pkey);
if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM ||
(evpmd = ssh_digest_to_md(hash_alg)) == NULL)
return SSH_ERR_INVALID_ARGUMENT;
if ((sig = malloc(slen)) == NULL)
return SSH_ERR_ALLOC_FAIL;
if ((ctx = EVP_MD_CTX_new()) == NULL) {
ret = SSH_ERR_ALLOC_FAIL;
goto out;
}
if (EVP_DigestSignInit(ctx, NULL, evpmd, NULL, pkey) != 1 ||
EVP_DigestSign(ctx, sig, &slen, data, datalen) != 1) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
*sigp = sig;
*lenp = slen;
/* Now owned by the caller */
sig = NULL;
ret = 0;
out:
EVP_MD_CTX_free(ctx);
free(sig);
return ret;
}
int
sshkey_pkey_digest_verify(EVP_PKEY *pkey, int hash_alg, const u_char *data,
size_t datalen, u_char *sigbuf, size_t siglen)
{
EVP_MD_CTX *ctx = NULL;
int ret = SSH_ERR_INTERNAL_ERROR;
const EVP_MD *evpmd;
if ((evpmd = ssh_digest_to_md(hash_alg)) == NULL)
return SSH_ERR_INVALID_ARGUMENT;
if ((ctx = EVP_MD_CTX_new()) == NULL)
return SSH_ERR_ALLOC_FAIL;
if (EVP_DigestVerifyInit(ctx, NULL, evpmd, NULL, pkey) != 1) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
switch (EVP_DigestVerify(ctx, sigbuf, siglen, data, datalen)) {
case 1:
ret = 0;
break;
case 0:
ret = SSH_ERR_SIGNATURE_INVALID;
break;
default:
ret = SSH_ERR_LIBCRYPTO_ERROR;
break;
}
out:
EVP_MD_CTX_free(ctx);
return ret;
}
/* XXX: these are really begging for a table-driven approach */
int
sshkey_curve_name_to_nid(const char *name)
@ -1331,14 +1423,12 @@ int
sshkey_check_rsa_length(const struct sshkey *k, int min_size)
{
#ifdef WITH_OPENSSL
const BIGNUM *rsa_n;
int nbits;
if (k == NULL || k->rsa == NULL ||
if (k == NULL || k->pkey == NULL ||
(k->type != KEY_RSA && k->type != KEY_RSA_CERT))
return 0;
RSA_get0_key(k->rsa, &rsa_n, NULL, NULL);
nbits = BN_num_bits(rsa_n);
nbits = EVP_PKEY_bits(k->pkey);
if (nbits < SSH_RSA_MINIMUM_MODULUS_SIZE ||
(min_size > 0 && nbits < min_size))
return SSH_ERR_KEY_LENGTH;
@ -1346,53 +1436,26 @@ sshkey_check_rsa_length(const struct sshkey *k, int min_size)
return 0;
}
#ifdef WITH_OPENSSL
# ifdef OPENSSL_HAS_ECC
#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
int
sshkey_ecdsa_key_to_nid(EC_KEY *k)
sshkey_ecdsa_key_to_nid(const EC_KEY *k)
{
EC_GROUP *eg;
int nids[] = {
NID_X9_62_prime256v1,
NID_secp384r1,
# ifdef OPENSSL_HAS_NISTP521
NID_secp521r1,
# endif /* OPENSSL_HAS_NISTP521 */
-1
};
const EC_GROUP *g;
int nid;
u_int i;
const EC_GROUP *g = EC_KEY_get0_group(k);
/*
* The group may be stored in a ASN.1 encoded private key in one of two
* ways: as a "named group", which is reconstituted by ASN.1 object ID
* or explicit group parameters encoded into the key blob. Only the
* "named group" case sets the group NID for us, but we can figure
* it out for the other case by comparing against all the groups that
* are supported.
*/
if ((nid = EC_GROUP_get_curve_name(g)) > 0)
return nid;
for (i = 0; nids[i] != -1; i++) {
if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL)
return -1;
if (EC_GROUP_cmp(g, eg, NULL) == 0)
break;
EC_GROUP_free(eg);
}
if (nids[i] != -1) {
/* Use the group with the NID attached */
EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE);
if (EC_KEY_set_group(k, eg) != 1) {
EC_GROUP_free(eg);
return -1;
}
}
return nids[i];
if (k == NULL || (g = EC_KEY_get0_group(k)) == NULL)
return -1;
if ((nid = EC_GROUP_get_curve_name(g)) <= 0)
return -1;
return nid;
}
# endif /* OPENSSL_HAS_ECC */
#endif /* WITH_OPENSSL */
int
sshkey_ecdsa_pkey_to_nid(EVP_PKEY *pkey)
{
return sshkey_ecdsa_key_to_nid(EVP_PKEY_get0_EC_KEY(pkey));
}
#endif /* defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) */
int
sshkey_generate(int type, u_int bits, struct sshkey **keyp)
@ -3226,10 +3289,6 @@ sshkey_private_to_blob_pem_pkcs8(struct sshkey *key, struct sshbuf *buf,
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if (format == SSHKEY_PRIVATE_PKCS8 && (pkey = EVP_PKEY_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if ((r = sshkey_unshield_private(key)) != 0)
goto out;
@ -3240,6 +3299,10 @@ sshkey_private_to_blob_pem_pkcs8(struct sshkey *key, struct sshbuf *buf,
success = PEM_write_bio_DSAPrivateKey(bio, key->dsa,
cipher, passphrase, len, NULL, NULL);
} else {
if ((pkey = EVP_PKEY_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
success = EVP_PKEY_set1_DSA(pkey, key->dsa);
}
break;
@ -3247,19 +3310,25 @@ sshkey_private_to_blob_pem_pkcs8(struct sshkey *key, struct sshbuf *buf,
#ifdef OPENSSL_HAS_ECC
case KEY_ECDSA:
if (format == SSHKEY_PRIVATE_PEM) {
success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa,
success = PEM_write_bio_ECPrivateKey(bio,
EVP_PKEY_get0_EC_KEY(key->pkey),
cipher, passphrase, len, NULL, NULL);
} else {
success = EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa);
pkey = key->pkey;
EVP_PKEY_up_ref(key->pkey);
success = 1;
}
break;
#endif
case KEY_RSA:
if (format == SSHKEY_PRIVATE_PEM) {
success = PEM_write_bio_RSAPrivateKey(bio, key->rsa,
success = PEM_write_bio_RSAPrivateKey(bio,
EVP_PKEY_get0_RSA(key->pkey),
cipher, passphrase, len, NULL, NULL);
} else {
success = EVP_PKEY_set1_RSA(pkey, key->rsa);
pkey = key->pkey;
EVP_PKEY_up_ref(key->pkey);
success = 1;
}
break;
default:
@ -3428,6 +3497,8 @@ sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
struct sshkey *prv = NULL;
BIO *bio = NULL;
int r;
RSA *rsa = NULL;
EC_KEY *ecdsa = NULL;
if (keyp != NULL)
*keyp = NULL;
@ -3461,15 +3532,21 @@ sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
prv->rsa = EVP_PKEY_get1_RSA(pk);
prv->type = KEY_RSA;
#ifdef DEBUG_PK
RSA_print_fp(stderr, prv->rsa, 8);
#endif
if (RSA_blinding_on(prv->rsa, NULL) != 1) {
if ((rsa = EVP_PKEY_get1_RSA(pk)) == NULL) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
prv->type = KEY_RSA;
#ifdef DEBUG_PK
RSA_print_fp(stderr, rsa, 8);
#endif
if (RSA_blinding_on(rsa, NULL) != 1 ||
EVP_PKEY_set1_RSA(pk, rsa) != 1) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
EVP_PKEY_up_ref(pk);
prv->pkey = pk;
if ((r = sshkey_check_rsa_length(prv, 0)) != 0)
goto out;
#ifdef WITH_DSA
@ -3492,21 +3569,25 @@ sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk);
if ((prv->ecdsa_nid = sshkey_ecdsa_fixup_group(pk)) == -1 ||
(ecdsa = EVP_PKEY_get1_EC_KEY(pk)) == NULL) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
prv->type = KEY_ECDSA;
prv->ecdsa_nid = sshkey_ecdsa_key_to_nid(prv->ecdsa);
if (prv->ecdsa_nid == -1 ||
sshkey_curve_nid_to_name(prv->ecdsa_nid) == NULL ||
sshkey_ec_validate_public(EC_KEY_get0_group(prv->ecdsa),
EC_KEY_get0_public_key(prv->ecdsa)) != 0 ||
sshkey_ec_validate_private(prv->ecdsa) != 0) {
if (sshkey_curve_nid_to_name(prv->ecdsa_nid) == NULL ||
sshkey_ec_validate_public(EC_KEY_get0_group(ecdsa),
EC_KEY_get0_public_key(ecdsa)) != 0 ||
sshkey_ec_validate_private(ecdsa) != 0) {
r = SSH_ERR_INVALID_FORMAT;
goto out;
}
# ifdef DEBUG_PK
if (prv != NULL && prv->ecdsa != NULL)
sshkey_dump_ec_key(prv->ecdsa);
# endif
EVP_PKEY_up_ref(pk);
prv->pkey = pk;
#ifdef DEBUG_PK
if (prv != NULL && prv->pkey != NULL)
sshkey_dump_ec_key(EVP_PKEY_get0_EC_KEY(prv->pkey));
#endif
#endif /* OPENSSL_HAS_ECC */
#ifdef OPENSSL_HAS_ED25519
} else if (EVP_PKEY_base_id(pk) == EVP_PKEY_ED25519 &&
@ -3541,9 +3622,9 @@ sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
/* Append the public key to our private key */
memcpy(prv->ed25519_sk + (ED25519_SK_SZ - ED25519_PK_SZ),
prv->ed25519_pk, ED25519_PK_SZ);
# ifdef DEBUG_PK
#ifdef DEBUG_PK
sshbuf_dump_data(prv->ed25519_sk, ED25519_SK_SZ, stderr);
# endif
#endif
#endif /* OPENSSL_HAS_ED25519 */
} else {
r = SSH_ERR_INVALID_FORMAT;
@ -3557,6 +3638,8 @@ sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
out:
BIO_free(bio);
EVP_PKEY_free(pk);
RSA_free(rsa);
EC_KEY_free(ecdsa);
sshkey_free(prv);
return r;
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.h,v 1.63 2024/05/17 06:42:04 jsg Exp $ */
/* $OpenBSD: sshkey.h,v 1.64 2024/08/15 00:51:51 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@ -31,6 +31,7 @@
#ifdef WITH_OPENSSL
#include <openssl/rsa.h>
#include <openssl/dsa.h>
#include <openssl/evp.h>
# ifdef OPENSSL_HAS_ECC
# include <openssl/ec.h>
# include <openssl/ecdsa.h>
@ -47,6 +48,7 @@
# define EC_KEY void
# define EC_GROUP void
# define EC_POINT void
# define EVP_PKEY void
#define SSH_OPENSSL_VERSION "without OpenSSL"
#endif /* WITH_OPENSSL */
@ -125,13 +127,12 @@ struct sshkey_cert {
struct sshkey {
int type;
int flags;
/* KEY_RSA */
RSA *rsa;
/* KEY_DSA */
DSA *dsa;
/* KEY_ECDSA and KEY_ECDSA_SK */
int ecdsa_nid; /* NID of curve */
EC_KEY *ecdsa;
/* libcrypto-backed keys */
EVP_PKEY *pkey;
/* KEY_ED25519 and KEY_ED25519_SK */
u_char *ed25519_sk;
u_char *ed25519_pk;
@ -258,7 +259,8 @@ int sshkey_curve_name_to_nid(const char *);
const char * sshkey_curve_nid_to_name(int);
u_int sshkey_curve_nid_to_bits(int);
int sshkey_ecdsa_bits_to_nid(int);
int sshkey_ecdsa_key_to_nid(EC_KEY *);
int sshkey_ecdsa_key_to_nid(const EC_KEY *);
int sshkey_ecdsa_pkey_to_nid(EVP_PKEY *);
int sshkey_ec_nid_to_hash_alg(int nid);
int sshkey_ec_validate_public(const EC_GROUP *, const EC_POINT *);
int sshkey_ec_validate_private(const EC_KEY *);
@ -287,6 +289,12 @@ int sshkey_check_sigtype(const u_char *, size_t, const char *);
const char *sshkey_sigalg_by_name(const char *);
int sshkey_get_sigtype(const u_char *, size_t, char **);
/* Signing and verification backend for libcrypto-backed keys */
int sshkey_pkey_digest_sign(EVP_PKEY*, int, u_char **,
size_t *, const u_char *, size_t);
int sshkey_pkey_digest_verify(EVP_PKEY *, int, const u_char *,
size_t, u_char *, size_t);
/* for debug */
void sshkey_dump_ec_point(const EC_GROUP *, const EC_POINT *);
void sshkey_dump_ec_key(const EC_KEY *);
@ -310,7 +318,8 @@ int sshkey_parse_pubkey_from_private_fileblob_type(struct sshbuf *blob,
int sshkey_check_rsa_length(const struct sshkey *, int);
/* XXX should be internal, but used by ssh-keygen */
int ssh_rsa_complete_crt_parameters(struct sshkey *, const BIGNUM *);
int ssh_rsa_complete_crt_parameters(const BIGNUM *, const BIGNUM *,
const BIGNUM *, const BIGNUM *, BIGNUM **, BIGNUM **);
/* stateful keys (e.g. XMSS) */
int sshkey_set_filename(struct sshkey *, const char *);
@ -321,6 +330,10 @@ int sshkey_private_serialize_maxsign(struct sshkey *key,
void sshkey_sig_details_free(struct sshkey_sig_details *);
#ifdef WITH_OPENSSL
int sshkey_ecdsa_fixup_group(EVP_PKEY *k); /* ssh-ecdsa.c */
#endif
#ifdef SSHKEY_INTERNAL
int sshkey_sk_fields_equal(const struct sshkey *a, const struct sshkey *b);
void sshkey_sk_cleanup(struct sshkey *k);
@ -341,6 +354,7 @@ int check_rsa_length(const RSA *rsa); /* XXX remove */
# undef EC_KEY
# undef EC_GROUP
# undef EC_POINT
# undef EVP_PKEY
#elif !defined(OPENSSL_HAS_ECC)
# undef EC_KEY
# undef EC_GROUP