From bcbd1f9472d2e75522bd8f629ab9423aefbf4b8c Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Wed, 7 Aug 2024 16:26:20 -0400 Subject: [PATCH] Add EVP_PKEY_asn1_* functions --- crypto/dilithium/p_dilithium3_asn1.c | 5 +++ crypto/evp_extra/evp_asn1.c | 67 ++++++++++++++++++++++++++++ crypto/evp_extra/evp_test.cc | 64 +++++++++++++++++++++++++- crypto/evp_extra/internal.h | 2 + crypto/evp_extra/p_dsa_asn1.c | 4 ++ crypto/evp_extra/p_ec_asn1.c | 4 ++ crypto/evp_extra/p_ed25519_asn1.c | 3 ++ crypto/evp_extra/p_hmac_asn1.c | 5 +++ crypto/evp_extra/p_kem_asn1.c | 5 +++ crypto/evp_extra/p_methods.c | 3 +- crypto/evp_extra/p_rsa_asn1.c | 8 ++++ crypto/evp_extra/p_x25519_asn1.c | 5 +++ crypto/fipsmodule/evp/internal.h | 6 ++- include/openssl/evp.h | 38 ++++++++++++++++ 14 files changed, 216 insertions(+), 3 deletions(-) diff --git a/crypto/dilithium/p_dilithium3_asn1.c b/crypto/dilithium/p_dilithium3_asn1.c index b2afd26a02d..cf554a3d547 100644 --- a/crypto/dilithium/p_dilithium3_asn1.c +++ b/crypto/dilithium/p_dilithium3_asn1.c @@ -221,6 +221,11 @@ const EVP_PKEY_ASN1_METHOD dilithium3_asn1_meth = { // as we await NIST to release OIDs. {0x2B, 0x06, 0x01, 0x04, 0x01, 0x02, 0x82, 0x0B, 0x07, 0x06, 0x05}, 11, + + EVP_PKEY_DILITHIUM3, + "DILITHIUM3", + "AWS-LC DILITHIUM3 method", + dilithium3_pub_decode, dilithium3_pub_encode, dilithium3_pub_cmp, diff --git a/crypto/evp_extra/evp_asn1.c b/crypto/evp_extra/evp_asn1.c index ff4b9519796..c67a0be3b36 100644 --- a/crypto/evp_extra/evp_asn1.c +++ b/crypto/evp_extra/evp_asn1.c @@ -575,3 +575,70 @@ int i2d_EC_PUBKEY(const EC_KEY *ec_key, uint8_t **outp) { EVP_PKEY_free(pkey); return ret; } + +int EVP_PKEY_asn1_get_count(void) { return asn1_evp_pkey_methods_size; } + +const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_get0(int idx) { + if (idx < 0 || idx >= EVP_PKEY_asn1_get_count()) { + return NULL; + } + return asn1_evp_pkey_methods[idx]; +} + +const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_find(ENGINE **_pe, int type) { + for (size_t i = 0; i < (size_t)EVP_PKEY_asn1_get_count(); i++) { + const EVP_PKEY_ASN1_METHOD *ameth = EVP_PKEY_asn1_get0(i); + if (ameth->pkey_base_id == type) { + return ameth; + } + } + return NULL; +} + +const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_find_str(ENGINE **_pe, + const char *name, int len) { + if (len < 0) { + return NULL; + } + // OPENSSL_strnlen returns an i, where str[i] == 0 + const size_t name_len = OPENSSL_strnlen(name, len); + + for (size_t i = 0; i < (size_t)EVP_PKEY_asn1_get_count(); i++) { + const EVP_PKEY_ASN1_METHOD *ameth = EVP_PKEY_asn1_get0(i); + + const size_t pem_str_len = OPENSSL_strnlen(ameth->pem_str, 16); + + // OPENSSL_strncasecmp(a, b, n) compares up to index n-1 + const size_t cmp_len = + 1 + ((name_len < pem_str_len) ? name_len : pem_str_len); + if (0 == OPENSSL_strncasecmp(ameth->pem_str, name, cmp_len)) { + return ameth; + } + } + return NULL; +} + +int EVP_PKEY_asn1_get0_info(int *ppkey_id, int *pkey_base_id, int *ppkey_flags, + const char **pinfo, const char **ppem_str, + const EVP_PKEY_ASN1_METHOD *ameth) { + if (!ameth) { + return 0; + } + if (ppkey_id) { + *ppkey_id = ameth->pkey_id; + } + if (pkey_base_id) { + *pkey_base_id = ameth->pkey_base_id; + } + // This value is not supported. + if (ppkey_flags) { + *ppkey_flags = 0; + } + if (pinfo) { + *pinfo = ameth->info; + } + if (ppem_str) { + *ppem_str = ameth->pem_str; + } + return 1; +} diff --git a/crypto/evp_extra/evp_test.cc b/crypto/evp_extra/evp_test.cc index fb87e498189..84686470736 100644 --- a/crypto/evp_extra/evp_test.cc +++ b/crypto/evp_extra/evp_test.cc @@ -56,11 +56,13 @@ #include #include -#include #include +#include #include #include +#include "../fipsmodule/evp/internal.h" + OPENSSL_MSVC_PRAGMA(warning(push)) OPENSSL_MSVC_PRAGMA(warning(disable: 4702)) @@ -1394,3 +1396,63 @@ TEST(EVPTest, ECTLSEncodedPoint) { ERR_GET_REASON(ERR_peek_last_error())); ERR_clear_error(); } + +TEST(EVPTest, PKEY_asn1_find) { + int pkey_id, pkey_base_id, pkey_flags; + const char *pinfo, *pem_str; + + /* Test case 1: Find RSA algorithm */ + const EVP_PKEY_ASN1_METHOD* ameth = EVP_PKEY_asn1_find(NULL, EVP_PKEY_RSA); + ASSERT_TRUE(ameth); + ASSERT_TRUE(EVP_PKEY_asn1_get0_info(&pkey_id, &pkey_base_id, &pkey_flags, &pinfo, &pem_str, ameth)); + ASSERT_EQ(pkey_id, EVP_PKEY_RSA); + ASSERT_EQ(pkey_base_id, EVP_PKEY_RSA); + ASSERT_EQ(0, pkey_flags); + ASSERT_STREQ("RSA", pem_str); + ASSERT_STREQ("OpenSSL RSA method", pinfo); + + /* Test case 2: Find EC algorithm */ + ameth = EVP_PKEY_asn1_find(NULL, EVP_PKEY_EC); + ASSERT_TRUE(ameth); + ASSERT_TRUE(EVP_PKEY_asn1_get0_info(&pkey_id, &pkey_base_id, &pkey_flags, &pinfo, &pem_str, ameth)); + ASSERT_EQ(pkey_id, EVP_PKEY_EC); + ASSERT_EQ(pkey_base_id, EVP_PKEY_EC); + ASSERT_EQ(0, pkey_flags); + ASSERT_STREQ("EC", pem_str); + ASSERT_STREQ("OpenSSL EC algorithm", pinfo); + + /* Test case 3: Find non-existent algorithm */ + ameth = EVP_PKEY_asn1_find(NULL, EVP_PKEY_NONE); + ASSERT_FALSE(ameth); + ASSERT_FALSE(EVP_PKEY_asn1_get0_info(&pkey_id, &pkey_base_id, &pkey_flags, &pinfo, &pem_str, ameth)); +} + +TEST(EVPTest, PKEY_asn1_find_str) { + int pkey_id, pkey_base_id, pkey_flags; + const char *pinfo, *pem_str; + + /* Test case 1: Find RSA algorithm */ + const EVP_PKEY_ASN1_METHOD* ameth = EVP_PKEY_asn1_find_str(NULL, "RSA", 3); + ASSERT_TRUE(ameth); + ASSERT_TRUE(EVP_PKEY_asn1_get0_info(&pkey_id, &pkey_base_id, &pkey_flags, &pinfo, &pem_str, ameth)); + ASSERT_EQ(pkey_id, EVP_PKEY_RSA); + ASSERT_EQ(pkey_base_id, EVP_PKEY_RSA); + ASSERT_EQ(0, pkey_flags); + ASSERT_STREQ("RSA", pem_str); + ASSERT_STREQ("OpenSSL RSA method", pinfo); + + /* Test case 2: Find EC algorithm */ + ameth = EVP_PKEY_asn1_find_str(NULL, "EC", 2); + ASSERT_TRUE(ameth); + ASSERT_TRUE(EVP_PKEY_asn1_get0_info(&pkey_id, &pkey_base_id, &pkey_flags, &pinfo, &pem_str, ameth)); + ASSERT_EQ(pkey_id, EVP_PKEY_EC); + ASSERT_EQ(pkey_base_id, EVP_PKEY_EC); + ASSERT_EQ(0, pkey_flags); + ASSERT_STREQ("EC", pem_str); + ASSERT_STREQ("OpenSSL EC algorithm", pinfo); + + /* Test case 3: Find non-existent algorithm */ + ameth = EVP_PKEY_asn1_find_str(NULL, "Nonsense", 8); + ASSERT_FALSE(ameth); + ASSERT_FALSE(EVP_PKEY_asn1_get0_info(&pkey_id, &pkey_base_id, &pkey_flags, &pinfo, &pem_str, ameth)); +} diff --git a/crypto/evp_extra/internal.h b/crypto/evp_extra/internal.h index d3103f38b32..1623c89c682 100644 --- a/crypto/evp_extra/internal.h +++ b/crypto/evp_extra/internal.h @@ -37,6 +37,8 @@ typedef struct { #endif +extern const size_t asn1_evp_pkey_methods_size; +extern const EVP_PKEY_ASN1_METHOD *const asn1_evp_pkey_methods[]; extern const EVP_PKEY_ASN1_METHOD dsa_asn1_meth; extern const EVP_PKEY_ASN1_METHOD ec_asn1_meth; extern const EVP_PKEY_ASN1_METHOD rsa_asn1_meth; diff --git a/crypto/evp_extra/p_dsa_asn1.c b/crypto/evp_extra/p_dsa_asn1.c index 69530b14f89..9520397e137 100644 --- a/crypto/evp_extra/p_dsa_asn1.c +++ b/crypto/evp_extra/p_dsa_asn1.c @@ -260,6 +260,10 @@ const EVP_PKEY_ASN1_METHOD dsa_asn1_meth = { // 1.2.840.10040.4.1 {0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x01}, 7, + EVP_PKEY_DSA, + "DSA", + "OpenSSL DSA method", + dsa_pub_decode, dsa_pub_encode, dsa_pub_cmp, diff --git a/crypto/evp_extra/p_ec_asn1.c b/crypto/evp_extra/p_ec_asn1.c index 6e5a8cc0d29..31da8fb96de 100644 --- a/crypto/evp_extra/p_ec_asn1.c +++ b/crypto/evp_extra/p_ec_asn1.c @@ -253,6 +253,10 @@ const EVP_PKEY_ASN1_METHOD ec_asn1_meth = { // 1.2.840.10045.2.1 {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01}, 7, + EVP_PKEY_EC, + "EC", + "OpenSSL EC algorithm", + eckey_pub_decode, eckey_pub_encode, eckey_pub_cmp, diff --git a/crypto/evp_extra/p_ed25519_asn1.c b/crypto/evp_extra/p_ed25519_asn1.c index 37b6cd00d96..723f72a0218 100644 --- a/crypto/evp_extra/p_ed25519_asn1.c +++ b/crypto/evp_extra/p_ed25519_asn1.c @@ -261,6 +261,9 @@ const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth = { EVP_PKEY_ED25519, {0x2b, 0x65, 0x70}, 3, + EVP_PKEY_ED25519, + "ED25519", + "OpenSSL ED25519 algorithm", ed25519_pub_decode, ed25519_pub_encode, ed25519_pub_cmp, diff --git a/crypto/evp_extra/p_hmac_asn1.c b/crypto/evp_extra/p_hmac_asn1.c index bea66838b33..caa5052098f 100644 --- a/crypto/evp_extra/p_hmac_asn1.c +++ b/crypto/evp_extra/p_hmac_asn1.c @@ -121,6 +121,11 @@ const EVP_PKEY_ASN1_METHOD hmac_asn1_meth = { EVP_PKEY_HMAC, {0xff} /* placeholder oid */, 0 /* oid_len */, + + EVP_PKEY_HMAC, + "HMAC", + "AWS-LC HMAC method", + NULL /* pub_decode */, NULL /* pub_encode */, NULL /* pub_cmp */, diff --git a/crypto/evp_extra/p_kem_asn1.c b/crypto/evp_extra/p_kem_asn1.c index b57b7031131..19c1d7f237e 100644 --- a/crypto/evp_extra/p_kem_asn1.c +++ b/crypto/evp_extra/p_kem_asn1.c @@ -119,6 +119,11 @@ const EVP_PKEY_ASN1_METHOD kem_asn1_meth = { // TODO(awslc): this is a placeholder OID. Do we need OID for KEM at all? {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 11, + + EVP_PKEY_KEM, + "KEM", + "AWS-LC KEM method", + NULL, // pub_decode NULL, // pub_encode kem_pub_cmp, diff --git a/crypto/evp_extra/p_methods.c b/crypto/evp_extra/p_methods.c index 63679e84596..2c5d7a840d2 100644 --- a/crypto/evp_extra/p_methods.c +++ b/crypto/evp_extra/p_methods.c @@ -16,7 +16,7 @@ static const EVP_PKEY_METHOD *const non_fips_pkey_evp_methods[] = { &kem_pkey_meth, }; -static const EVP_PKEY_ASN1_METHOD *const asn1_evp_pkey_methods[] = { +const EVP_PKEY_ASN1_METHOD *const asn1_evp_pkey_methods[] = { &rsa_asn1_meth, &rsa_pss_asn1_meth, &ec_asn1_meth, @@ -29,6 +29,7 @@ static const EVP_PKEY_ASN1_METHOD *const asn1_evp_pkey_methods[] = { &kem_asn1_meth, &hmac_asn1_meth }; +const size_t asn1_evp_pkey_methods_size = sizeof(asn1_evp_pkey_methods)/sizeof(asn1_evp_pkey_methods[0]); OPENSSL_STATIC_ASSERT( NON_FIPS_EVP_PKEY_METHODS == OPENSSL_ARRAY_SIZE(non_fips_pkey_evp_methods), diff --git a/crypto/evp_extra/p_rsa_asn1.c b/crypto/evp_extra/p_rsa_asn1.c index d44f71e64c9..7e614fafe58 100644 --- a/crypto/evp_extra/p_rsa_asn1.c +++ b/crypto/evp_extra/p_rsa_asn1.c @@ -224,6 +224,10 @@ const EVP_PKEY_ASN1_METHOD rsa_asn1_meth = { // 1.2.840.113549.1.1.1 {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01}, 9, + EVP_PKEY_RSA, + "RSA", + "OpenSSL RSA method", + rsa_pub_decode, rsa_pub_encode, rsa_pub_cmp, @@ -252,6 +256,10 @@ const EVP_PKEY_ASN1_METHOD rsa_pss_asn1_meth = { // 1.2.840.113549.1.1.10 {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a}, 9, + EVP_PKEY_RSA_PSS, + "RSA-PSS", + "OpenSSL RSA-PSS method", + rsa_pss_pub_decode, NULL /* pub_encode */, rsa_pub_cmp, diff --git a/crypto/evp_extra/p_x25519_asn1.c b/crypto/evp_extra/p_x25519_asn1.c index 5149f8da4f1..85fcf048651 100644 --- a/crypto/evp_extra/p_x25519_asn1.c +++ b/crypto/evp_extra/p_x25519_asn1.c @@ -252,6 +252,11 @@ const EVP_PKEY_ASN1_METHOD x25519_asn1_meth = { EVP_PKEY_X25519, {0x2b, 0x65, 0x6e}, 3, + + EVP_PKEY_X25519, + "X25519", + "OpenSSL X25519 algorithm", + x25519_pub_decode, x25519_pub_encode, x25519_pub_cmp, diff --git a/crypto/fipsmodule/evp/internal.h b/crypto/fipsmodule/evp/internal.h index 81915824312..27dcddeb61c 100644 --- a/crypto/fipsmodule/evp/internal.h +++ b/crypto/fipsmodule/evp/internal.h @@ -61,6 +61,7 @@ #include #include +#include #if defined(__cplusplus) extern "C" { @@ -77,7 +78,6 @@ extern "C" { // This is an implementation detail of |EVP_PKEY_HMAC|. #define EVP_MD_CTX_HMAC 0x0800 -typedef struct evp_pkey_asn1_method_st EVP_PKEY_ASN1_METHOD; typedef struct evp_pkey_method_st EVP_PKEY_METHOD; struct evp_pkey_asn1_method_st { @@ -85,6 +85,10 @@ struct evp_pkey_asn1_method_st { uint8_t oid[11]; uint8_t oid_len; + int pkey_base_id; + const char *pem_str; + const char *info; + // pub_decode decodes |params| and |key| as a SubjectPublicKeyInfo // and writes the result into |out|. It returns one on success and zero on // error. |params| is the AlgorithmIdentifier after the OBJECT IDENTIFIER diff --git a/include/openssl/evp.h b/include/openssl/evp.h index eea831786f9..cc08e43ca9a 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -928,6 +928,44 @@ OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_kem_new_raw_key(int nid, // to the secret key in |key|. OPENSSL_EXPORT int EVP_PKEY_kem_check_key(EVP_PKEY *key); +// ASN1 functions + +typedef struct evp_pkey_asn1_method_st EVP_PKEY_ASN1_METHOD; + +// EVP_PKEY_asn1_get_count returns the number of available +// |EVP_PKEY_ASN1_METHOD| structures. +OPENSSL_EXPORT int EVP_PKEY_asn1_get_count(void); + +// EVP_PKEY_asn1_get0 +// Returns a pointer to an EVP_PKEY_ASN1_METHOD structure. |idx| is the index +// value, which must be a non-negative value smaller than the return value of +// |EVP_PKEY_asn1_get_count|. +OPENSSL_EXPORT const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_get0(int idx); + +// EVP_PKEY_asn1_find finds an |EVP_PKEY_ASN1_METHOD| structure for the given +// key |type|, e.g. |EVP_PKEY_EC| or |EVP_PKEY_RSA|. |pe| is ignored. +OPENSSL_EXPORT const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_find(ENGINE **_pe, + int type); + +// EVP_PKEY_asn1_find_str finds an |EVP_PKEY_ASN1_METHOD| structure by name. +// |pe| is ignored. +// |name| is the name of the key type to find, e.g, "RSA" or "EC". +// |len| is the length of the name. +OPENSSL_EXPORT const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_find_str( + ENGINE **_pe, const char *name, int len); + +// EVP_PKEY_asn1_get0_info retrieves information about an |EVP_PKEY_ASN1_METHOD| +// structure. |ppkey_id| is a pointer to get the key type identifier. +// |pkey_base_id| is a pointer to get the base key type. +// |ppkey_flags| is not supported. Value is set to 0 is pointer is not |NULL|. +// |pinfo| is a pointer to get a text description. +// |ppem_str| is a pointer to get the PEM string name. +// |ameth| is a pointer to the EVP_PKEY_ASN1_METHOD structure. +OPENSSL_EXPORT int EVP_PKEY_asn1_get0_info(int *ppkey_id, int *pkey_base_id, + int *ppkey_flags, const char **pinfo, + const char **ppem_str, + const EVP_PKEY_ASN1_METHOD *ameth); + // Deprecated functions. // EVP_PKEY_RSA2 was historically an alternate form for RSA public keys (OID