Skip to content

Commit 3f29525

Browse files
sylph01bdewater
andauthored
Add support for raw private/public keys (#646)
Add OpenSSL::PKey.new_raw_private_key, #raw_private_key and public equivalents. These methods are useful for importing and exporting keys that support "raw private/public key". Currently, OpenSSL implements X25519/X448 and Ed25519/Ed448 keys. [rhe: rewrote commit message] Co-authored-by: Bart de Water <[email protected]>
1 parent 97fb410 commit 3f29525

File tree

2 files changed

+171
-0
lines changed

2 files changed

+171
-0
lines changed

ext/openssl/ossl_pkey.c

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,72 @@ ossl_pkey_initialize_copy(VALUE self, VALUE other)
628628
}
629629
#endif
630630

631+
#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
632+
/*
633+
* call-seq:
634+
* OpenSSL::PKey.new_raw_private_key(algo, string) -> PKey
635+
*
636+
* See the OpenSSL documentation for EVP_PKEY_new_raw_private_key()
637+
*/
638+
639+
static VALUE
640+
ossl_pkey_new_raw_private_key(VALUE self, VALUE type, VALUE key)
641+
{
642+
EVP_PKEY *pkey;
643+
const EVP_PKEY_ASN1_METHOD *ameth;
644+
int pkey_id;
645+
size_t keylen;
646+
647+
StringValue(type);
648+
StringValue(key);
649+
ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type));
650+
if (!ameth)
651+
ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type);
652+
EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
653+
654+
keylen = RSTRING_LEN(key);
655+
656+
pkey = EVP_PKEY_new_raw_private_key(pkey_id, NULL, (unsigned char *)RSTRING_PTR(key), keylen);
657+
if (!pkey)
658+
ossl_raise(ePKeyError, "EVP_PKEY_new_raw_private_key");
659+
660+
return ossl_pkey_new(pkey);
661+
}
662+
#endif
663+
664+
#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
665+
/*
666+
* call-seq:
667+
* OpenSSL::PKey.new_raw_public_key(algo, string) -> PKey
668+
*
669+
* See the OpenSSL documentation for EVP_PKEY_new_raw_public_key()
670+
*/
671+
672+
static VALUE
673+
ossl_pkey_new_raw_public_key(VALUE self, VALUE type, VALUE key)
674+
{
675+
EVP_PKEY *pkey;
676+
const EVP_PKEY_ASN1_METHOD *ameth;
677+
int pkey_id;
678+
size_t keylen;
679+
680+
StringValue(type);
681+
StringValue(key);
682+
ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type));
683+
if (!ameth)
684+
ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type);
685+
EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
686+
687+
keylen = RSTRING_LEN(key);
688+
689+
pkey = EVP_PKEY_new_raw_public_key(pkey_id, NULL, (unsigned char *)RSTRING_PTR(key), keylen);
690+
if (!pkey)
691+
ossl_raise(ePKeyError, "EVP_PKEY_new_raw_public_key");
692+
693+
return ossl_pkey_new(pkey);
694+
}
695+
#endif
696+
631697
/*
632698
* call-seq:
633699
* pkey.oid -> string
@@ -816,6 +882,35 @@ ossl_pkey_private_to_pem(int argc, VALUE *argv, VALUE self)
816882
return do_pkcs8_export(argc, argv, self, 0);
817883
}
818884

885+
#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
886+
/*
887+
* call-seq:
888+
* pkey.raw_private_key => string
889+
*
890+
* See the OpenSSL documentation for EVP_PKEY_get_raw_private_key()
891+
*/
892+
893+
static VALUE
894+
ossl_pkey_raw_private_key(VALUE self)
895+
{
896+
EVP_PKEY *pkey;
897+
VALUE str;
898+
size_t len;
899+
900+
GetPKey(self, pkey);
901+
if (EVP_PKEY_get_raw_private_key(pkey, NULL, &len) != 1)
902+
ossl_raise(ePKeyError, "EVP_PKEY_get_raw_private_key");
903+
str = rb_str_new(NULL, len);
904+
905+
if (EVP_PKEY_get_raw_private_key(pkey, (unsigned char *)RSTRING_PTR(str), &len) != 1)
906+
ossl_raise(ePKeyError, "EVP_PKEY_get_raw_private_key");
907+
908+
rb_str_set_len(str, len);
909+
910+
return str;
911+
}
912+
#endif
913+
819914
VALUE
820915
ossl_pkey_export_spki(VALUE self, int to_der)
821916
{
@@ -865,6 +960,35 @@ ossl_pkey_public_to_pem(VALUE self)
865960
return ossl_pkey_export_spki(self, 0);
866961
}
867962

963+
#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
964+
/*
965+
* call-seq:
966+
* pkey.raw_public_key => string
967+
*
968+
* See the OpenSSL documentation for EVP_PKEY_get_raw_public_key()
969+
*/
970+
971+
static VALUE
972+
ossl_pkey_raw_public_key(VALUE self)
973+
{
974+
EVP_PKEY *pkey;
975+
VALUE str;
976+
size_t len;
977+
978+
GetPKey(self, pkey);
979+
if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1)
980+
ossl_raise(ePKeyError, "EVP_PKEY_get_raw_public_key");
981+
str = rb_str_new(NULL, len);
982+
983+
if (EVP_PKEY_get_raw_public_key(pkey, (unsigned char *)RSTRING_PTR(str), &len) != 1)
984+
ossl_raise(ePKeyError, "EVP_PKEY_get_raw_public_key");
985+
986+
rb_str_set_len(str, len);
987+
988+
return str;
989+
}
990+
#endif
991+
868992
/*
869993
* call-seq:
870994
* pkey.compare?(another_pkey) -> true | false
@@ -1602,6 +1726,10 @@ Init_ossl_pkey(void)
16021726
rb_define_module_function(mPKey, "read", ossl_pkey_new_from_data, -1);
16031727
rb_define_module_function(mPKey, "generate_parameters", ossl_pkey_s_generate_parameters, -1);
16041728
rb_define_module_function(mPKey, "generate_key", ossl_pkey_s_generate_key, -1);
1729+
#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
1730+
rb_define_module_function(mPKey, "new_raw_private_key", ossl_pkey_new_raw_private_key, 2);
1731+
rb_define_module_function(mPKey, "new_raw_public_key", ossl_pkey_new_raw_public_key, 2);
1732+
#endif
16051733

16061734
rb_define_alloc_func(cPKey, ossl_pkey_alloc);
16071735
rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0);
@@ -1617,6 +1745,10 @@ Init_ossl_pkey(void)
16171745
rb_define_method(cPKey, "private_to_pem", ossl_pkey_private_to_pem, -1);
16181746
rb_define_method(cPKey, "public_to_der", ossl_pkey_public_to_der, 0);
16191747
rb_define_method(cPKey, "public_to_pem", ossl_pkey_public_to_pem, 0);
1748+
#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
1749+
rb_define_method(cPKey, "raw_private_key", ossl_pkey_raw_private_key, 0);
1750+
rb_define_method(cPKey, "raw_public_key", ossl_pkey_raw_public_key, 0);
1751+
#endif
16201752
rb_define_method(cPKey, "compare?", ossl_pkey_compare, 1);
16211753

16221754
rb_define_method(cPKey, "sign", ossl_pkey_sign, -1);

test/openssl/test_pkey.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,19 @@ def test_ed25519
109109
assert_equal pub_pem, priv.public_to_pem
110110
assert_equal pub_pem, pub.public_to_pem
111111

112+
begin
113+
assert_equal "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
114+
priv.raw_private_key.unpack1("H*")
115+
assert_equal OpenSSL::PKey.new_raw_private_key("ED25519", priv.raw_private_key).private_to_pem,
116+
priv.private_to_pem
117+
assert_equal "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
118+
priv.raw_public_key.unpack1("H*")
119+
assert_equal OpenSSL::PKey.new_raw_public_key("ED25519", priv.raw_public_key).public_to_pem,
120+
pub.public_to_pem
121+
rescue NoMethodError
122+
pend "running OpenSSL version does not have raw public key support"
123+
end
124+
112125
sig = [<<~EOF.gsub(/[^0-9a-f]/, "")].pack("H*")
113126
92a009a9f0d4cab8720e820b5f642540
114127
a2b27b5416503f8fb3762223ebdb69da
@@ -155,6 +168,32 @@ def test_x25519
155168
assert_equal alice_pem, alice.private_to_pem
156169
assert_equal bob_pem, bob.public_to_pem
157170
assert_equal [shared_secret].pack("H*"), alice.derive(bob)
171+
begin
172+
alice_private = OpenSSL::PKey.new_raw_private_key("X25519", alice.raw_private_key)
173+
bob_public = OpenSSL::PKey.new_raw_public_key("X25519", bob.raw_public_key)
174+
alice_private_raw = alice.raw_private_key.unpack1("H*")
175+
bob_public_raw = bob.raw_public_key.unpack1("H*")
176+
rescue NoMethodError
177+
# OpenSSL < 1.1.1
178+
pend "running OpenSSL version does not have raw public key support"
179+
end
180+
assert_equal alice_private.private_to_pem,
181+
alice.private_to_pem
182+
assert_equal bob_public.public_to_pem,
183+
bob.public_to_pem
184+
assert_equal "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
185+
alice_private_raw
186+
assert_equal "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f",
187+
bob_public_raw
188+
end
189+
190+
def raw_initialize
191+
pend "Ed25519 is not implemented" unless OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10101000 && # >= v1.1.1
192+
193+
assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_private_key("foo123", "xxx") }
194+
assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_private_key("ED25519", "xxx") }
195+
assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_public_key("foo123", "xxx") }
196+
assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_public_key("ED25519", "xxx") }
158197
end
159198

160199
def test_compare?

0 commit comments

Comments
 (0)