Skip to content

pkey: add support for OpenSSL 3 provider-only pkeys #898

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions ext/openssl/ossl.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@

#if OSSL_OPENSSL_PREREQ(3, 0, 0)
# define OSSL_USE_PROVIDER
# include <openssl/provider.h>
#endif

/*
Expand Down
81 changes: 60 additions & 21 deletions ext/openssl/ossl_pkey.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -711,6 +736,10 @@ ossl_pkey_oid(VALUE self)

GetPKey(self, pkey);
nid = EVP_PKEY_id(pkey);
#ifdef OSSL_USE_PROVIDER
if (nid == EVP_PKEY_KEYMGMT)
ossl_raise(ePKeyError, "EVP_PKEY_id");
#endif
return rb_str_new_cstr(OBJ_nid2sn(nid));
}

Expand All @@ -724,13 +753,23 @@ static VALUE
ossl_pkey_inspect(VALUE self)
{
EVP_PKEY *pkey;
int nid;

GetPKey(self, pkey);
nid = EVP_PKEY_id(pkey);
return rb_sprintf("#<%"PRIsVALUE":%p oid=%s>",
rb_class_name(CLASS_OF(self)), (void *)self,
OBJ_nid2sn(nid));
VALUE str = rb_sprintf("#<%"PRIsVALUE":%p",
rb_obj_class(self), (void *)self);
int nid = EVP_PKEY_id(pkey);
#ifdef OSSL_USE_PROVIDER
if (nid != EVP_PKEY_KEYMGMT)
#endif
rb_str_catf(str, " oid=%s", OBJ_nid2sn(nid));
#ifdef OSSL_USE_PROVIDER
rb_str_catf(str, " type_name=%s", EVP_PKEY_get0_type_name(pkey));
const OSSL_PROVIDER *prov = EVP_PKEY_get0_provider(pkey);
if (prov)
rb_str_catf(str, " provider=%s", OSSL_PROVIDER_get0_name(prov));
#endif
rb_str_catf(str, ">");
return str;
}

/*
Expand Down
2 changes: 0 additions & 2 deletions ext/openssl/ossl_provider.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
#include "ossl.h"

#ifdef OSSL_USE_PROVIDER
# include <openssl/provider.h>

#define NewProvider(klass) \
TypedData_Wrap_Struct((klass), &ossl_provider_type, 0)
#define SetProvider(obj, provider) do { \
Expand Down
32 changes: 22 additions & 10 deletions test/openssl/test_pkey.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,7 @@
assert_instance_of OpenSSL::PKey::RSA, rsa
assert_equal "rsaEncryption", rsa.oid
assert_match %r{oid=rsaEncryption}, rsa.inspect
end

def test_generic_oid_inspect_x25519
omit_on_fips

# X25519 private key
x25519 = OpenSSL::PKey.generate_key("X25519")
assert_instance_of OpenSSL::PKey::PKey, x25519
assert_equal "X25519", x25519.oid
assert_match %r{oid=X25519}, x25519.inspect
assert_match %r{type_name=RSA}, rsa.inspect if openssl?(3, 0, 0)
end

def test_s_generate_parameters
Expand Down Expand Up @@ -152,6 +143,8 @@
alice = OpenSSL::PKey.read(alice_pem)
bob = OpenSSL::PKey.read(bob_pem)
assert_instance_of OpenSSL::PKey::PKey, alice
assert_equal "X25519", alice.oid
assert_match %r{oid=X25519}, alice.inspect
assert_equal alice_pem, alice.private_to_pem
assert_equal bob_pem, bob.public_to_pem
assert_equal [shared_secret].pack("H*"), alice.derive(bob)
Expand All @@ -168,6 +161,25 @@
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")

Check failure on line 168 in test/openssl/test_pkey.rb

View workflow job for this annotation

GitHub Actions / windows-latest 3.2

Error

OpenSSL::PKey::PKeyError: EVP_PKEY_CTX_new_from_name: unsupported (Global default library context, Algorithm (ML-DSA-44 : 0), Properties (<null>)) D:/a/openssl/openssl/test/openssl/test_pkey.rb:168:in `generate_key' D:/a/openssl/openssl/test/openssl/test_pkey.rb:168:in `test_ml_dsa'

Check failure on line 168 in test/openssl/test_pkey.rb

View workflow job for this annotation

GitHub Actions / windows-latest mswin

Error

OpenSSL::PKey::PKeyError: EVP_PKEY_CTX_new_from_name: unsupported (Global default library context, Algorithm (ML-DSA-44 : 0), Properties (<null>)) D:/a/openssl/openssl/test/openssl/test_pkey.rb:168:in 'OpenSSL::PKey.generate_key' D:/a/openssl/openssl/test/openssl/test_pkey.rb:168:in 'OpenSSL::TestPKey#test_ml_dsa'
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") }
Expand Down
Loading