Skip to content

Commit e730e45

Browse files
committed
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.
1 parent bd3e322 commit e730e45

File tree

2 files changed

+60
-16
lines changed

2 files changed

+60
-16
lines changed

ext/openssl/ossl_pkey.c

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,29 @@ ossl_pkey_initialize_copy(VALUE self, VALUE other)
635635
}
636636
#endif
637637

638+
#ifndef OSSL_USE_PROVIDER
639+
static int
640+
lookup_pkey_type(VALUE type)
641+
{
642+
const EVP_PKEY_ASN1_METHOD *ameth;
643+
int pkey_id;
644+
645+
StringValue(type);
646+
/*
647+
* XXX: EVP_PKEY_asn1_find_str() looks up a PEM type string. Should we use
648+
* OBJ_txt2nid() instead (and then somehow check if the NID is an acceptable
649+
* EVP_PKEY type)?
650+
* It is probably fine, though, since it can handle all algorithms that
651+
* support raw keys in 1.1.1: { X25519, X448, ED25519, ED448, HMAC }.
652+
*/
653+
ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type));
654+
if (!ameth)
655+
ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type);
656+
EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
657+
return pkey_id;
658+
}
659+
#endif
660+
638661
/*
639662
* call-seq:
640663
* OpenSSL::PKey.new_raw_private_key(algo, string) -> PKey
@@ -646,22 +669,23 @@ static VALUE
646669
ossl_pkey_new_raw_private_key(VALUE self, VALUE type, VALUE key)
647670
{
648671
EVP_PKEY *pkey;
649-
const EVP_PKEY_ASN1_METHOD *ameth;
650-
int pkey_id;
651672
size_t keylen;
652673

653-
StringValue(type);
654674
StringValue(key);
655-
ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type));
656-
if (!ameth)
657-
ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type);
658-
EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
659-
660675
keylen = RSTRING_LEN(key);
661676

677+
#ifdef OSSL_USE_PROVIDER
678+
pkey = EVP_PKEY_new_raw_private_key_ex(NULL, StringValueCStr(type), NULL,
679+
(unsigned char *)RSTRING_PTR(key),
680+
keylen);
681+
if (!pkey)
682+
ossl_raise(ePKeyError, "EVP_PKEY_new_raw_private_key_ex");
683+
#else
684+
int pkey_id = lookup_pkey_type(type);
662685
pkey = EVP_PKEY_new_raw_private_key(pkey_id, NULL, (unsigned char *)RSTRING_PTR(key), keylen);
663686
if (!pkey)
664687
ossl_raise(ePKeyError, "EVP_PKEY_new_raw_private_key");
688+
#endif
665689

666690
return ossl_pkey_new(pkey);
667691
}
@@ -677,22 +701,23 @@ static VALUE
677701
ossl_pkey_new_raw_public_key(VALUE self, VALUE type, VALUE key)
678702
{
679703
EVP_PKEY *pkey;
680-
const EVP_PKEY_ASN1_METHOD *ameth;
681-
int pkey_id;
682704
size_t keylen;
683705

684-
StringValue(type);
685706
StringValue(key);
686-
ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type));
687-
if (!ameth)
688-
ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type);
689-
EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
690-
691707
keylen = RSTRING_LEN(key);
692708

709+
#ifdef OSSL_USE_PROVIDER
710+
pkey = EVP_PKEY_new_raw_public_key_ex(NULL, StringValueCStr(type), NULL,
711+
(unsigned char *)RSTRING_PTR(key),
712+
keylen);
713+
if (!pkey)
714+
ossl_raise(ePKeyError, "EVP_PKEY_new_raw_public_key_ex");
715+
#else
716+
int pkey_id = lookup_pkey_type(type);
693717
pkey = EVP_PKEY_new_raw_public_key(pkey_id, NULL, (unsigned char *)RSTRING_PTR(key), keylen);
694718
if (!pkey)
695719
ossl_raise(ePKeyError, "EVP_PKEY_new_raw_public_key");
720+
#endif
696721

697722
return ossl_pkey_new(pkey);
698723
}

test/openssl/test_pkey.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,25 @@ def test_x25519
161161
bob.raw_public_key.unpack1("H*")
162162
end
163163

164+
def test_ml_dsa
165+
# AWS-LC also supports ML-DSA, but it's implemented in a different way
166+
return unless openssl?(3, 5, 0)
167+
168+
pkey = OpenSSL::PKey.generate_key("ML-DSA-44")
169+
assert_match(/type_name=ML-DSA-44/, pkey.inspect)
170+
sig = pkey.sign(nil, "data")
171+
assert_equal(2420, sig.bytesize)
172+
assert_equal(true, pkey.verify(nil, sig, "data"))
173+
174+
pub2 = OpenSSL::PKey.read(pkey.public_to_der)
175+
assert_equal(true, pub2.verify(nil, sig, "data"))
176+
177+
raw_public_key = pkey.raw_public_key
178+
assert_equal(1312, raw_public_key.bytesize)
179+
pub3 = OpenSSL::PKey.new_raw_public_key("ML-DSA-44", raw_public_key)
180+
assert_equal(true, pub3.verify(nil, sig, "data"))
181+
end
182+
164183
def test_raw_initialize_errors
165184
assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_private_key("foo123", "xxx") }
166185
assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_private_key("ED25519", "xxx") }

0 commit comments

Comments
 (0)