[ruby/openssl] pkey: use EVP_PKEY_new_raw_{private,public}_key_ex() if available

Algorithms implemented only in OpenSSL 3 providers may not have a
corresponding NID. The *_ex() variants have been added in OpenSSL 3.0
to handle such algorithms, by taking algorithm names as a string.

e730e457cc
This commit is contained in:
Kazuki Yamaguchi 2025-06-06 02:44:09 +09:00 committed by git
parent 0c6075bd42
commit a1996b32a9
2 changed files with 60 additions and 16 deletions

View file

@ -635,6 +635,29 @@ ossl_pkey_initialize_copy(VALUE self, VALUE other)
}
#endif
#ifndef OSSL_USE_PROVIDER
static int
lookup_pkey_type(VALUE type)
{
const EVP_PKEY_ASN1_METHOD *ameth;
int pkey_id;
StringValue(type);
/*
* XXX: EVP_PKEY_asn1_find_str() looks up a PEM type string. Should we use
* OBJ_txt2nid() instead (and then somehow check if the NID is an acceptable
* EVP_PKEY type)?
* It is probably fine, though, since it can handle all algorithms that
* support raw keys in 1.1.1: { X25519, X448, ED25519, ED448, HMAC }.
*/
ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type));
if (!ameth)
ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type);
EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
return pkey_id;
}
#endif
/*
* call-seq:
* OpenSSL::PKey.new_raw_private_key(algo, string) -> PKey
@ -646,22 +669,23 @@ static VALUE
ossl_pkey_new_raw_private_key(VALUE self, VALUE type, VALUE key)
{
EVP_PKEY *pkey;
const EVP_PKEY_ASN1_METHOD *ameth;
int pkey_id;
size_t keylen;
StringValue(type);
StringValue(key);
ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type));
if (!ameth)
ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type);
EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
keylen = RSTRING_LEN(key);
#ifdef OSSL_USE_PROVIDER
pkey = EVP_PKEY_new_raw_private_key_ex(NULL, StringValueCStr(type), NULL,
(unsigned char *)RSTRING_PTR(key),
keylen);
if (!pkey)
ossl_raise(ePKeyError, "EVP_PKEY_new_raw_private_key_ex");
#else
int pkey_id = lookup_pkey_type(type);
pkey = EVP_PKEY_new_raw_private_key(pkey_id, NULL, (unsigned char *)RSTRING_PTR(key), keylen);
if (!pkey)
ossl_raise(ePKeyError, "EVP_PKEY_new_raw_private_key");
#endif
return ossl_pkey_new(pkey);
}
@ -677,22 +701,23 @@ static VALUE
ossl_pkey_new_raw_public_key(VALUE self, VALUE type, VALUE key)
{
EVP_PKEY *pkey;
const EVP_PKEY_ASN1_METHOD *ameth;
int pkey_id;
size_t keylen;
StringValue(type);
StringValue(key);
ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type));
if (!ameth)
ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type);
EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
keylen = RSTRING_LEN(key);
#ifdef OSSL_USE_PROVIDER
pkey = EVP_PKEY_new_raw_public_key_ex(NULL, StringValueCStr(type), NULL,
(unsigned char *)RSTRING_PTR(key),
keylen);
if (!pkey)
ossl_raise(ePKeyError, "EVP_PKEY_new_raw_public_key_ex");
#else
int pkey_id = lookup_pkey_type(type);
pkey = EVP_PKEY_new_raw_public_key(pkey_id, NULL, (unsigned char *)RSTRING_PTR(key), keylen);
if (!pkey)
ossl_raise(ePKeyError, "EVP_PKEY_new_raw_public_key");
#endif
return ossl_pkey_new(pkey);
}

View file

@ -161,6 +161,25 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
bob.raw_public_key.unpack1("H*")
end
def test_ml_dsa
# AWS-LC also supports ML-DSA, but it's implemented in a different way
return unless openssl?(3, 5, 0)
pkey = OpenSSL::PKey.generate_key("ML-DSA-44")
assert_match(/type_name=ML-DSA-44/, pkey.inspect)
sig = pkey.sign(nil, "data")
assert_equal(2420, sig.bytesize)
assert_equal(true, pkey.verify(nil, sig, "data"))
pub2 = OpenSSL::PKey.read(pkey.public_to_der)
assert_equal(true, pub2.verify(nil, sig, "data"))
raw_public_key = pkey.raw_public_key
assert_equal(1312, raw_public_key.bytesize)
pub3 = OpenSSL::PKey.new_raw_public_key("ML-DSA-44", raw_public_key)
assert_equal(true, pub3.verify(nil, sig, "data"))
end
def test_raw_initialize_errors
assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_private_key("foo123", "xxx") }
assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_private_key("ED25519", "xxx") }