Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
pkey: use EVP_PKEY_new_raw_{private,public}_key_ex() if available
[ This is a backport to the 3.2 branch. ]

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.

(cherry picked from commit e730e45)
  • Loading branch information
rhenium committed May 13, 2026
commit 1dfaeee0721d547f657c2f635b93a8f58df1b861
58 changes: 42 additions & 16 deletions ext/openssl/ossl_pkey.c
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,30 @@ ossl_pkey_initialize_copy(VALUE self, VALUE other)
#endif

#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY

#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
Expand All @@ -647,22 +671,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);
}
Expand All @@ -680,22 +705,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);
}
Expand Down
19 changes: 19 additions & 0 deletions test/openssl/test_pkey.rb
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,25 @@ def test_x25519
bob_public_raw
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 raw_initialize
pend "Ed25519 is not implemented" unless openssl?(1, 1, 1) # >= v1.1.1

Expand Down