aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyo Kajiwara <sylph01@gmail.com>2023-07-12 22:06:46 +0900
committerGitHub <noreply@github.com>2023-07-12 22:06:46 +0900
commit3f2952561886f78f574edfcbe5afdcca3e618f11 (patch)
treec7543bfec0c30b7f44b6c65795607fea9b228542
parent97fb4102a19dbd2b56b7f5cccece10d6a199e0a8 (diff)
downloadruby-openssl-3f2952561886f78f574edfcbe5afdcca3e618f11.tar.gz
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 <bartdewater@gmail.com>
-rw-r--r--ext/openssl/ossl_pkey.c132
-rw-r--r--test/openssl/test_pkey.rb39
2 files changed, 171 insertions, 0 deletions
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
index 9caeb562..952a6851 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 2cd5290f..691dd74a 100644
--- a/test/openssl/test_pkey.rb
+++ b/test/openssl/test_pkey.rb
@@ -109,6 +109,19 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
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 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
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?