From 3662fc13a4c4bccadb40a55218d4e71a491f1e5b Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Thu, 29 Aug 2024 19:14:28 -0400 Subject: [PATCH] Add EVP_PKEY_asn1_* functions (#1751) --- crypto/dilithium/p_dilithium3_asn1.c | 4 ++ crypto/evp_extra/evp_asn1.c | 70 ++++++++++++++++++++++++++++ crypto/evp_extra/evp_test.cc | 64 ++++++++++++++++++++++++- crypto/evp_extra/internal.h | 2 + crypto/evp_extra/p_dsa_asn1.c | 3 ++ crypto/evp_extra/p_ec_asn1.c | 3 ++ crypto/evp_extra/p_ed25519_asn1.c | 2 + crypto/evp_extra/p_hmac_asn1.c | 4 ++ crypto/evp_extra/p_kem_asn1.c | 4 ++ crypto/evp_extra/p_methods.c | 3 +- crypto/evp_extra/p_rsa_asn1.c | 6 +++ crypto/evp_extra/p_x25519_asn1.c | 4 ++ crypto/fipsmodule/evp/internal.h | 5 +- include/openssl/base.h | 1 + include/openssl/evp.h | 37 +++++++++++++++ 15 files changed, 209 insertions(+), 3 deletions(-) diff --git a/crypto/dilithium/p_dilithium3_asn1.c b/crypto/dilithium/p_dilithium3_asn1.c index b2afd26a02..019f260cd3 100644 --- a/crypto/dilithium/p_dilithium3_asn1.c +++ b/crypto/dilithium/p_dilithium3_asn1.c @@ -221,6 +221,10 @@ 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, + + "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 ff4b951979..fb6d815639 100644 --- a/crypto/evp_extra/evp_asn1.c +++ b/crypto/evp_extra/evp_asn1.c @@ -575,3 +575,73 @@ 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_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 longest_pem_str_len = 10; // "DILITHIUM3" + + const size_t pem_str_len = + OPENSSL_strnlen(ameth->pem_str, longest_pem_str_len); + + // 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_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 3fe3f308e0..5a5950174b 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)) @@ -1404,3 +1406,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 d3103f38b3..1623c89c68 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 69530b14f8..9d927897b1 100644 --- a/crypto/evp_extra/p_dsa_asn1.c +++ b/crypto/evp_extra/p_dsa_asn1.c @@ -260,6 +260,9 @@ const EVP_PKEY_ASN1_METHOD dsa_asn1_meth = { // 1.2.840.10040.4.1 {0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x01}, 7, + "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 6e5a8cc0d2..de364a6f4f 100644 --- a/crypto/evp_extra/p_ec_asn1.c +++ b/crypto/evp_extra/p_ec_asn1.c @@ -253,6 +253,9 @@ const EVP_PKEY_ASN1_METHOD ec_asn1_meth = { // 1.2.840.10045.2.1 {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01}, 7, + "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 37b6cd00d9..14c4cfdf8e 100644 --- a/crypto/evp_extra/p_ed25519_asn1.c +++ b/crypto/evp_extra/p_ed25519_asn1.c @@ -261,6 +261,8 @@ const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth = { EVP_PKEY_ED25519, {0x2b, 0x65, 0x70}, 3, + "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 bea66838b3..153ced0b68 100644 --- a/crypto/evp_extra/p_hmac_asn1.c +++ b/crypto/evp_extra/p_hmac_asn1.c @@ -121,6 +121,10 @@ const EVP_PKEY_ASN1_METHOD hmac_asn1_meth = { EVP_PKEY_HMAC, {0xff} /* placeholder oid */, 0 /* oid_len */, + + "HMAC", + "OpenSSL 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 b57b703113..d6ca05676e 100644 --- a/crypto/evp_extra/p_kem_asn1.c +++ b/crypto/evp_extra/p_kem_asn1.c @@ -119,6 +119,10 @@ 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, + + "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 63679e8459..2c5d7a840d 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 d44f71e64c..04235950cc 100644 --- a/crypto/evp_extra/p_rsa_asn1.c +++ b/crypto/evp_extra/p_rsa_asn1.c @@ -224,6 +224,9 @@ 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, + "RSA", + "OpenSSL RSA method", + rsa_pub_decode, rsa_pub_encode, rsa_pub_cmp, @@ -252,6 +255,9 @@ 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, + "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 5149f8da4f..b6963a8228 100644 --- a/crypto/evp_extra/p_x25519_asn1.c +++ b/crypto/evp_extra/p_x25519_asn1.c @@ -252,6 +252,10 @@ const EVP_PKEY_ASN1_METHOD x25519_asn1_meth = { EVP_PKEY_X25519, {0x2b, 0x65, 0x6e}, 3, + + "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 8191582431..0465d6cd66 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,9 @@ struct evp_pkey_asn1_method_st { uint8_t oid[11]; uint8_t oid_len; + 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/base.h b/include/openssl/base.h index 220256a277..b9f6444d4f 100644 --- a/include/openssl/base.h +++ b/include/openssl/base.h @@ -343,6 +343,7 @@ typedef struct evp_hpke_key_st EVP_HPKE_KEY; typedef struct evp_kem_st EVP_KEM; typedef struct kem_key_st KEM_KEY; typedef struct evp_pkey_ctx_st EVP_PKEY_CTX; +typedef struct evp_pkey_asn1_method_st EVP_PKEY_ASN1_METHOD; typedef struct evp_pkey_st EVP_PKEY; typedef struct hmac_ctx_st HMAC_CTX; typedef struct md4_state_st MD4_CTX; diff --git a/include/openssl/evp.h b/include/openssl/evp.h index 72a7beba21..c3cd1a0df0 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -928,6 +928,43 @@ 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 + +// 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. Value will be the same +// as |ppkey_id|. +// |ppkey_flags| is not supported. Value is set to 0 if 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