summaryrefslogtreecommitdiffstats
path: root/ssh-ecdsa.c
diff options
context:
space:
mode:
Diffstat (limited to 'ssh-ecdsa.c')
-rw-r--r--ssh-ecdsa.c258
1 files changed, 177 insertions, 81 deletions
diff --git a/ssh-ecdsa.c b/ssh-ecdsa.c
index 341c324..695ed45 100644
--- a/ssh-ecdsa.c
+++ b/ssh-ecdsa.c
@@ -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 (EC_KEY_set_private_key(ec, exponent) != 1) {
r = SSH_ERR_LIBCRYPTO_ERROR;
goto out;
}
- if ((r = sshkey_ec_validate_private(key->ecdsa)) != 0)
+ 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;
+ if ((len = i2d_ECDSA_SIG(esig, NULL)) <= 0) {
+ len = 0;
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
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;
+ if ((sigb = calloc(1, len)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
goto out;
- default:
+ }
+ 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);