Skip to content

Commit

Permalink
Replace ECDSA_METHOD with EC_KEY_METHOD and add the associated API (#…
Browse files Browse the repository at this point in the history
…1785)

### Description of changes: 
This is the second pull request in a series of three ([prev
PR](#1776)). This PR primarily focuses
on refactoring and enhancing the EC_KEY_METHOD API. The key changes are
as follows:

1. **Removal of ECDSA_METHOD**: ECDSA_METHOD support, including the
associated ENGINE functions, has been fully removed and replaced by
EC_KEY_METHOD. This aligns with the transition seen in OpenSSL 1.1.1 and
was also driven by customer requirements.
2. **Hiding Struct definition**: The EC_KEY_METHOD struct definition is
now internal. Unlike OpenSSL, where static allocation was possible,
AWS-LC now supports only dynamic allocation of this struct.
3. **Expansion of EC_KEY_METHOD Functions**: Added various
EC_KEY_METHOD_* functions to create, free, get/set, and manipulate the
EC_KEY_METHOD struct.

### Testing:
Tested with various fields initialized in EC_KEY_METHOD to ensure that
custom functionality is correctly invoked.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.
  • Loading branch information
smittals2 authored Aug 30, 2024
1 parent 9d2f9d2 commit b925242
Show file tree
Hide file tree
Showing 10 changed files with 404 additions and 76 deletions.
14 changes: 5 additions & 9 deletions crypto/ecdsa_extra/ecdsa_asn1.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,13 @@ size_t ECDSA_size(const EC_KEY *key) {
}

size_t group_order_size;
if (key->ecdsa_meth && key->ecdsa_meth->group_order_size) {
group_order_size = key->ecdsa_meth->group_order_size(key);
} else {
const EC_GROUP *group = EC_KEY_get0_group(key);
if (group == NULL) {
return 0;
}

group_order_size = BN_num_bytes(EC_GROUP_get0_order(group));
const EC_GROUP *group = EC_KEY_get0_group(key);
if (group == NULL) {
return 0;
}

group_order_size = BN_num_bytes(EC_GROUP_get0_order(group));

return ECDSA_SIG_max_len(group_order_size);
}

Expand Down
27 changes: 15 additions & 12 deletions crypto/engine/engine.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@
#include <openssl/thread.h>

#include "../internal.h"
#include "../crypto/fipsmodule/ec/internal.h"


struct engine_st {
RSA_METHOD *rsa_method;
ECDSA_METHOD *ecdsa_method;
EC_KEY_METHOD *eckey_method;
};

ENGINE *ENGINE_new(void) { return OPENSSL_zalloc(sizeof(ENGINE)); }
Expand All @@ -39,29 +40,31 @@ int ENGINE_free(ENGINE *engine) {
}

int ENGINE_set_RSA(ENGINE *engine, const RSA_METHOD *method) {
if(engine) {
engine->rsa_method = (RSA_METHOD *)method;
return 1;
if(!engine) {
OPENSSL_PUT_ERROR(ENGINE, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}

return 0;
engine->rsa_method = (RSA_METHOD *)method;
return 1;
}

const RSA_METHOD *ENGINE_get_RSA(const ENGINE *engine) {
return engine->rsa_method;
}

int ENGINE_set_ECDSA(ENGINE *engine, const ECDSA_METHOD *method) {
if(engine) {
engine->ecdsa_method = (ECDSA_METHOD *)method;
return 1;
int ENGINE_set_EC(ENGINE *engine, const EC_KEY_METHOD *method) {
if(!engine) {
OPENSSL_PUT_ERROR(ENGINE, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}

return 0;
engine->eckey_method = (EC_KEY_METHOD *)method;
return 1;
}

const ECDSA_METHOD *ENGINE_get_ECDSA(const ENGINE *engine) {
return engine->ecdsa_method;
const EC_KEY_METHOD *ENGINE_get_EC(const ENGINE *engine) {
return engine->eckey_method;
}

OPENSSL_DECLARE_ERROR_REASON(ENGINE, OPERATION_NOT_SUPPORTED)
Expand Down
102 changes: 97 additions & 5 deletions crypto/fipsmodule/ec/ec_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,20 @@ EC_KEY *EC_KEY_new_method(const ENGINE *engine) {
}

if (engine) {
ret->ecdsa_meth = ENGINE_get_ECDSA(engine);
// Cast away const
ret->eckey_method = (EC_KEY_METHOD *) ENGINE_get_EC(engine);
}

if(ret->eckey_method == NULL) {
ret->eckey_method = EC_KEY_get_default_method();
}

ret->conv_form = POINT_CONVERSION_UNCOMPRESSED;
ret->references = 1;

CRYPTO_new_ex_data(&ret->ex_data);

if (ret->ecdsa_meth && ret->ecdsa_meth->init && !ret->ecdsa_meth->init(ret)) {
if (ret->eckey_method && ret->eckey_method->init && !ret->eckey_method->init(ret)) {
CRYPTO_free_ex_data(g_ec_ex_data_class_bss_get(), ret, &ret->ex_data);
OPENSSL_free(ret);
return NULL;
Expand Down Expand Up @@ -150,8 +155,8 @@ void EC_KEY_free(EC_KEY *r) {
return;
}

if (r->ecdsa_meth && r->ecdsa_meth->finish) {
r->ecdsa_meth->finish(r);
if (r->eckey_method && r->eckey_method->finish) {
r->eckey_method->finish(r);
}

CRYPTO_free_ex_data(g_ec_ex_data_class_bss_get(), r, &r->ex_data);
Expand Down Expand Up @@ -195,7 +200,7 @@ int EC_KEY_up_ref(EC_KEY *r) {
}

int EC_KEY_is_opaque(const EC_KEY *key) {
return key->ecdsa_meth && (key->ecdsa_meth->flags & ECDSA_FLAG_OPAQUE);
return key->eckey_method && (key->eckey_method->flags & ECDSA_FLAG_OPAQUE);
}

const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key) { return key->group; }
Expand Down Expand Up @@ -562,3 +567,90 @@ void *EC_KEY_get_ex_data(const EC_KEY *d, int idx) {
}

void EC_KEY_set_asn1_flag(EC_KEY *key, int flag) {}

DEFINE_METHOD_FUNCTION(EC_KEY_METHOD, EC_KEY_OpenSSL) {
OPENSSL_memset(out, 0, sizeof(EC_KEY_METHOD));
}

const EC_KEY_METHOD *EC_KEY_get_default_method(void) {
return EC_KEY_OpenSSL();
}

EC_KEY_METHOD *EC_KEY_METHOD_new(const EC_KEY_METHOD *eckey_meth) {
EC_KEY_METHOD *ret;

ret = OPENSSL_zalloc(sizeof(EC_KEY_METHOD));
if(ret == NULL) {
OPENSSL_PUT_ERROR(EC, ERR_R_MALLOC_FAILURE);
return NULL;
}

if(eckey_meth) {
*ret = *eckey_meth;
}
return ret;
}

void EC_KEY_METHOD_free(EC_KEY_METHOD *eckey_meth) {
if(eckey_meth != NULL) {
OPENSSL_free(eckey_meth);
}
}

int EC_KEY_set_method(EC_KEY *ec, const EC_KEY_METHOD *meth) {
if(ec == NULL || meth == NULL) {
OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}

ec->eckey_method = meth;
return 1;
}

const EC_KEY_METHOD *EC_KEY_get_method(const EC_KEY *ec) {
if(ec == NULL) {
OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}

return ec->eckey_method;
}

void EC_KEY_METHOD_set_init_awslc(EC_KEY_METHOD *meth, int (*init)(EC_KEY *key),
void (*finish)(EC_KEY *key)) {
if(meth == NULL) {
OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
return;
}

meth->init = init;
meth->finish = finish;
}

void EC_KEY_METHOD_set_sign_awslc(EC_KEY_METHOD *meth,
int (*sign)(int type, const uint8_t *digest,
int digest_len, uint8_t *sig,
unsigned int *siglen, const BIGNUM *k_inv,
const BIGNUM *r, EC_KEY *eckey),
ECDSA_SIG *(*sign_sig)(const uint8_t *digest,
int digest_len,
const BIGNUM *in_kinv, const BIGNUM *in_r,
EC_KEY *eckey)) {

if(meth == NULL) {
OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
return;
}

meth->sign = sign;
meth->sign_sig = sign_sig;
}

int EC_KEY_METHOD_set_flags(EC_KEY_METHOD *meth, int flags) {
if(!meth || flags != ECDSA_FLAG_OPAQUE) {
return 0;
}

meth->flags |= flags;
return 1;
}
112 changes: 112 additions & 0 deletions crypto/fipsmodule/ec/ec_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
#include <openssl/crypto.h>
#include <openssl/ec.h>
#include <openssl/ec_key.h>
#include <openssl/ecdsa.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/mem.h>
#include <openssl/nid.h>
Expand Down Expand Up @@ -2413,3 +2416,112 @@ TEST(ECTest, FelemBytes) {
ASSERT_EQ(test_group.get()->field.N.width, expected_felem_words);
}
}

static ECDSA_SIG * ecdsa_sign_sig(const unsigned char *dgst, int dgstlen,
const BIGNUM *in_kinv, const BIGNUM *in_r,
EC_KEY *ec) {
// To track whether custom implementation was called
EC_KEY_set_ex_data(ec, 1, (void*)"ecdsa_sign_sig");
return nullptr;
}

static int ecdsa_sign(int type, const unsigned char *dgst, int dgstlen,
unsigned char *sig, unsigned int *siglen,
const BIGNUM *kinv, const BIGNUM *r, EC_KEY *ec) {

ECDSA_SIG *ret = ECDSA_do_sign(dgst, dgstlen, ec);
if (!ret) {
*siglen = 0;
return 0;
}

CBB cbb;
CBB_init_fixed(&cbb, sig, ECDSA_size(ec));
size_t len;
if (!ECDSA_SIG_marshal(&cbb, ret) ||
!CBB_finish(&cbb, nullptr, &len)) {
ECDSA_SIG_free(ret);
OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_ENCODE_ERROR);
*siglen = 0;
return 0;
}

*siglen = (unsigned)len;

// To track whether custom implementation was called
EC_KEY_set_ex_data(ec, 0, (void*)"ecdsa_sign");

ECDSA_SIG_free(ret);
return 1;
}

static void openvpn_extkey_ec_finish(EC_KEY *ec)
{
const EC_KEY_METHOD *ec_meth = EC_KEY_get_method(ec);
EC_KEY_METHOD_free((EC_KEY_METHOD *) ec_meth);
}

TEST(ECTest, ECKEYMETHOD) {
EC_KEY *ec = EC_KEY_new();
ASSERT_TRUE(ec);

EC_KEY_METHOD *ec_method;
ec_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL());
ASSERT_TRUE(ec_method);
// We zero initialize the default struct
ASSERT_FALSE(ec_method->finish && ec_method->sign);

// Can only set these fields
EC_KEY_METHOD_set_init(ec_method, NULL, openvpn_extkey_ec_finish,
NULL, NULL, NULL, NULL);
ASSERT_TRUE(ec_method->finish);
// Checking Sign
EC_KEY_METHOD_set_sign(ec_method, ecdsa_sign, NULL, NULL);
ASSERT_TRUE(ec_method->sign);

bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(NID_secp224r1));
ASSERT_TRUE(group.get());
ASSERT_TRUE(EC_KEY_set_group(ec, group.get()));
ASSERT_TRUE(EC_KEY_generate_key(ec));

// Should get freed with EC_KEY once assigned through
// |openvpn_extkey_ec_finish|
ASSERT_TRUE(EC_KEY_set_method(ec, ec_method));
ASSERT_TRUE(EC_KEY_check_key(ec));

bssl::UniquePtr<EVP_PKEY> ec_key(EVP_PKEY_new());
ASSERT_TRUE(ec_key.get());
EVP_PKEY_assign_EC_KEY(ec_key.get(), ec);
bssl::UniquePtr<EVP_PKEY_CTX> ec_key_ctx(EVP_PKEY_CTX_new(ec_key.get(), NULL));
ASSERT_TRUE(ec_key_ctx.get());

// Do a signature, should call custom openvpn_extkey_ec_finish
uint8_t digest[20];
ASSERT_TRUE(RAND_bytes(digest, 20));
CONSTTIME_DECLASSIFY(digest, 20);
std::vector<uint8_t> signature(ECDSA_size(ec));
size_t sig_len = ECDSA_size(ec);
ASSERT_TRUE(EVP_PKEY_sign_init(ec_key_ctx.get()));
ASSERT_TRUE(EVP_PKEY_sign(ec_key_ctx.get(), signature.data(),
&sig_len, digest, 20));
signature.resize(sig_len);

ASSERT_STREQ(static_cast<const char*>(EC_KEY_get_ex_data(ec, 0))
, "ecdsa_sign");
// Verify the signature
EXPECT_TRUE(ECDSA_verify(0, digest, 20, signature.data(), signature.size(),
ec));

// Now test the sign_sig pointer
EC_KEY_METHOD_set_sign(ec_method, NULL, NULL, ecdsa_sign_sig);
ASSERT_TRUE(ec_method->sign_sig && !ec_method->sign);

ECDSA_do_sign(digest, 20, ec);
ASSERT_STREQ(static_cast<const char*>(EC_KEY_get_ex_data(ec, 1)),
"ecdsa_sign_sig");

// Flags
ASSERT_FALSE(EC_KEY_is_opaque(ec));
EC_KEY_METHOD_set_flags(ec_method, ECDSA_FLAG_OPAQUE);
ASSERT_TRUE(EC_KEY_is_opaque(ec));
}
Loading

0 comments on commit b925242

Please sign in to comment.