diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7eb1c0b85..627a7b5d8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,6 +36,20 @@ jobs: ruby-version: ${{ matrix.ruby }} bundler-cache: true # `bundle install` and cache + # See https://github.com/oneclick/rubyinstaller2/issues/60 + # The builtin DLLs are preferred over the mingw-w64/vcpkg DLLs. This is a + # temporary workaround until they update the DLLs to OpenSSL 3.5.x. + - name: Update RI2/mswin builtin DLLs + run: | + $dst = "$((Get-Item (Get-Command ruby).Definition).DirectoryName)\ruby_builtin_dlls" + if ("${{ matrix.ruby }}" -eq "mswin") { + $src = "C:\vcpkg\installed\x64-windows\bin" + } else { + $src = "$((Get-Item (Get-Command ruby).Definition).DirectoryName)\..\msys64\ucrt64\bin" + } + Copy-Item "$src\libcrypto-3-x64.dll", "$src\libssl-3-x64.dll" $dst + if: ${{ matrix.os == 'windows-latest' && (matrix.ruby == '3.2' || matrix.ruby == '3.3' || matrix.ruby == 'mswin') }} + # Enable the verbose option in mkmf.rb to print the compiling commands. - name: enable mkmf verbose run: echo "MAKEFLAGS=V=1" >> $GITHUB_ENV diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index 9b20829b3..22471d208 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -71,6 +71,7 @@ #if OSSL_OPENSSL_PREREQ(3, 0, 0) # define OSSL_USE_PROVIDER +# include #endif /* diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index dc83255f0..b00a3648d 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -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 @@ -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); } @@ -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); } @@ -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)); } @@ -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; } /* diff --git a/ext/openssl/ossl_provider.c b/ext/openssl/ossl_provider.c index d1f6c5d42..529a5e1c7 100644 --- a/ext/openssl/ossl_provider.c +++ b/ext/openssl/ossl_provider.c @@ -5,8 +5,6 @@ #include "ossl.h" #ifdef OSSL_USE_PROVIDER -# include - #define NewProvider(klass) \ TypedData_Wrap_Struct((klass), &ossl_provider_type, 0) #define SetProvider(obj, provider) do { \ diff --git a/test/openssl/test_pkey.rb b/test/openssl/test_pkey.rb index 8444cfdcd..71f5da81d 100644 --- a/test/openssl/test_pkey.rb +++ b/test/openssl/test_pkey.rb @@ -8,16 +8,7 @@ def test_generic_oid_inspect_rsa 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 @@ -152,6 +143,8 @@ def test_x25519 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) @@ -168,6 +161,25 @@ def test_x25519 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") + 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") }