diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 9caeb5622a1ee4..952a6851e6febb 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -628,6 +628,72 @@ ossl_pkey_initialize_copy(VALUE self, VALUE other) } #endif +#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY +/* + * call-seq: + * OpenSSL::PKey.new_raw_private_key(algo, string) -> PKey + * + * See the OpenSSL documentation for EVP_PKEY_new_raw_private_key() + */ + +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); + + 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"); + + return ossl_pkey_new(pkey); +} +#endif + +#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY +/* + * call-seq: + * OpenSSL::PKey.new_raw_public_key(algo, string) -> PKey + * + * See the OpenSSL documentation for EVP_PKEY_new_raw_public_key() + */ + +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); + + 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"); + + return ossl_pkey_new(pkey); +} +#endif + /* * call-seq: * pkey.oid -> string @@ -816,6 +882,35 @@ ossl_pkey_private_to_pem(int argc, VALUE *argv, VALUE self) return do_pkcs8_export(argc, argv, self, 0); } +#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY +/* + * call-seq: + * pkey.raw_private_key => string + * + * See the OpenSSL documentation for EVP_PKEY_get_raw_private_key() + */ + +static VALUE +ossl_pkey_raw_private_key(VALUE self) +{ + EVP_PKEY *pkey; + VALUE str; + size_t len; + + GetPKey(self, pkey); + if (EVP_PKEY_get_raw_private_key(pkey, NULL, &len) != 1) + ossl_raise(ePKeyError, "EVP_PKEY_get_raw_private_key"); + str = rb_str_new(NULL, len); + + if (EVP_PKEY_get_raw_private_key(pkey, (unsigned char *)RSTRING_PTR(str), &len) != 1) + ossl_raise(ePKeyError, "EVP_PKEY_get_raw_private_key"); + + rb_str_set_len(str, len); + + return str; +} +#endif + VALUE ossl_pkey_export_spki(VALUE self, int to_der) { @@ -865,6 +960,35 @@ ossl_pkey_public_to_pem(VALUE self) return ossl_pkey_export_spki(self, 0); } +#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY +/* + * call-seq: + * pkey.raw_public_key => string + * + * See the OpenSSL documentation for EVP_PKEY_get_raw_public_key() + */ + +static VALUE +ossl_pkey_raw_public_key(VALUE self) +{ + EVP_PKEY *pkey; + VALUE str; + size_t len; + + GetPKey(self, pkey); + if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1) + ossl_raise(ePKeyError, "EVP_PKEY_get_raw_public_key"); + str = rb_str_new(NULL, len); + + if (EVP_PKEY_get_raw_public_key(pkey, (unsigned char *)RSTRING_PTR(str), &len) != 1) + ossl_raise(ePKeyError, "EVP_PKEY_get_raw_public_key"); + + rb_str_set_len(str, len); + + return str; +} +#endif + /* * call-seq: * pkey.compare?(another_pkey) -> true | false @@ -1602,6 +1726,10 @@ Init_ossl_pkey(void) rb_define_module_function(mPKey, "read", ossl_pkey_new_from_data, -1); rb_define_module_function(mPKey, "generate_parameters", ossl_pkey_s_generate_parameters, -1); rb_define_module_function(mPKey, "generate_key", ossl_pkey_s_generate_key, -1); +#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY + rb_define_module_function(mPKey, "new_raw_private_key", ossl_pkey_new_raw_private_key, 2); + rb_define_module_function(mPKey, "new_raw_public_key", ossl_pkey_new_raw_public_key, 2); +#endif rb_define_alloc_func(cPKey, ossl_pkey_alloc); rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0); @@ -1617,6 +1745,10 @@ Init_ossl_pkey(void) rb_define_method(cPKey, "private_to_pem", ossl_pkey_private_to_pem, -1); rb_define_method(cPKey, "public_to_der", ossl_pkey_public_to_der, 0); rb_define_method(cPKey, "public_to_pem", ossl_pkey_public_to_pem, 0); +#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY + rb_define_method(cPKey, "raw_private_key", ossl_pkey_raw_private_key, 0); + rb_define_method(cPKey, "raw_public_key", ossl_pkey_raw_public_key, 0); +#endif rb_define_method(cPKey, "compare?", ossl_pkey_compare, 1); rb_define_method(cPKey, "sign", ossl_pkey_sign, -1); diff --git a/test/openssl/test_pkey.rb b/test/openssl/test_pkey.rb index 2cd5290f49e515..691dd74aab5c91 100644 --- a/test/openssl/test_pkey.rb +++ b/test/openssl/test_pkey.rb @@ -109,6 +109,19 @@ def test_ed25519 assert_equal pub_pem, priv.public_to_pem assert_equal pub_pem, pub.public_to_pem + begin + assert_equal "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb", + priv.raw_private_key.unpack1("H*") + assert_equal OpenSSL::PKey.new_raw_private_key("ED25519", priv.raw_private_key).private_to_pem, + priv.private_to_pem + assert_equal "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c", + priv.raw_public_key.unpack1("H*") + assert_equal OpenSSL::PKey.new_raw_public_key("ED25519", priv.raw_public_key).public_to_pem, + pub.public_to_pem + rescue NoMethodError + pend "running OpenSSL version does not have raw public key support" + end + sig = [<<~EOF.gsub(/[^0-9a-f]/, "")].pack("H*") 92a009a9f0d4cab8720e820b5f642540 a2b27b5416503f8fb3762223ebdb69da @@ -155,6 +168,32 @@ def test_x25519 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) + begin + alice_private = OpenSSL::PKey.new_raw_private_key("X25519", alice.raw_private_key) + bob_public = OpenSSL::PKey.new_raw_public_key("X25519", bob.raw_public_key) + alice_private_raw = alice.raw_private_key.unpack1("H*") + bob_public_raw = bob.raw_public_key.unpack1("H*") + rescue NoMethodError + # OpenSSL < 1.1.1 + pend "running OpenSSL version does not have raw public key support" + end + assert_equal alice_private.private_to_pem, + alice.private_to_pem + assert_equal bob_public.public_to_pem, + bob.public_to_pem + assert_equal "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a", + alice_private_raw + assert_equal "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f", + bob_public_raw + end + + def raw_initialize + pend "Ed25519 is not implemented" unless OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10101000 && # >= v1.1.1 + + 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") } + assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_public_key("foo123", "xxx") } + assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_public_key("ED25519", "xxx") } end def test_compare?