# frozen_string_literal: true require_relative "utils" if defined?(OpenSSL) class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase def test_no_private_exp key = OpenSSL::PKey::RSA.new rsa = Fixtures.pkey("rsa-1") key.set_key(rsa.n, rsa.e, nil) key.set_factors(rsa.p, rsa.q) assert_raise(OpenSSL::PKey::PKeyError){ key.private_encrypt("foo") } assert_raise(OpenSSL::PKey::PKeyError){ key.private_decrypt("foo") } end if !openssl?(3, 0, 0) # Impossible state in OpenSSL 3.0 def test_private key = Fixtures.pkey("rsa-1") # Generated by DER key2 = OpenSSL::PKey::RSA.new(key.to_der) assert_true(key2.private?) # public key key3 = key.public_key assert_false(key3.private?) # Generated by public key DER key4 = OpenSSL::PKey::RSA.new(key3.to_der) assert_false(key4.private?) if !openssl?(3, 0, 0) # Generated by RSA#set_key key5 = OpenSSL::PKey::RSA.new key5.set_key(key.n, key.e, key.d) assert_true(key5.private?) # Generated by RSA#set_key, without d key6 = OpenSSL::PKey::RSA.new key6.set_key(key.n, key.e, nil) assert_false(key6.private?) end end def test_new key = OpenSSL::PKey::RSA.new(2048) assert_equal 2048, key.n.num_bits assert_equal 65537, key.e assert_not_nil key.d assert(key.private?) end def test_new_public_exponent # At least 2024-bits RSA key are required in FIPS. omit_on_fips # Specify public exponent key = OpenSSL::PKey::RSA.new(512, 3) assert_equal 512, key.n.num_bits assert_equal 3, key.e end def test_new_empty # pkeys are immutable with OpenSSL >= 3.0 if openssl?(3, 0, 0) assert_raise(ArgumentError) { OpenSSL::PKey::RSA.new } else key = OpenSSL::PKey::RSA.new assert_nil(key.n) end end def test_s_generate key1 = OpenSSL::PKey::RSA.generate(2048) assert_equal 2048, key1.n.num_bits assert_equal 65537, key1.e end def test_s_generate_public_exponent # At least 2024-bits RSA key are required in FIPS. omit_on_fips # Specify public exponent key = OpenSSL::PKey::RSA.generate(512, 3) assert_equal 512, key.n.num_bits assert_equal 3, key.e end def test_new_break assert_nil(OpenSSL::PKey::RSA.new(2048) { break }) assert_raise(RuntimeError) do OpenSSL::PKey::RSA.new(2048) { raise } end end def test_sign_verify rsa = Fixtures.pkey("rsa2048") data = "Sign me!" signature = rsa.sign("SHA256", data) assert_equal true, rsa.verify("SHA256", signature, data) signature0 = (<<~'end;').unpack1("m") ooy49i8aeFtkDYUU0RPDsEugGiNw4lZxpbQPnIwtdftEkka945IqKZ/MY3YSw7wKsvBZeaTy8GqL lSWLThsRFDV+UUS9zUBbQ9ygNIT8OjdV+tNL63ZpKGprczSnw4F05MQIpajNRud/8jiI9rf+Wysi WwXecjMl2FlXlLJHY4PFQZU5TiametB4VCQRMcjLo1uf26u/yRpiGaYyqn5vxs0SqNtUDM1UL6x4 NHCAdqLjuFRQPjYp1vGLD3eSl4061pS8x1NVap3YGbYfGUyzZO4VfwFwf1jPdhp/OX/uZw4dGB2H gSK+q1JiDFwEE6yym5tdKovL1g1NhFYHF6gkZg== end; assert_equal true, rsa.verify("SHA256", signature0, data) signature1 = signature0.succ assert_equal false, rsa.verify("SHA256", signature1, data) end def test_sign_verify_options key = Fixtures.pkey("rsa2048") data = "Sign me!" pssopts = { "rsa_padding_mode" => "pss", "rsa_pss_saltlen" => 20, "rsa_mgf1_md" => "SHA256" } sig_pss = key.sign("SHA256", data, pssopts) assert_equal 256, sig_pss.bytesize assert_equal true, key.verify("SHA256", sig_pss, data, pssopts) assert_equal true, key.verify_pss("SHA256", sig_pss, data, salt_length: 20, mgf1_hash: "SHA256") # Defaults to PKCS #1 v1.5 padding => verification failure assert_equal false, key.verify("SHA256", sig_pss, data) # option type check assert_raise_with_message(TypeError, /expected Hash/) { key.sign("SHA256", data, ["x"]) } end def test_sign_verify_raw key = Fixtures.pkey("rsa-1") data = "Sign me!" hash = OpenSSL::Digest.digest("SHA256", data) signature = key.sign_raw("SHA256", hash) assert_equal true, key.verify_raw("SHA256", signature, hash) assert_equal true, key.verify("SHA256", signature, data) # Too long data assert_raise(OpenSSL::PKey::PKeyError) { key.sign_raw("SHA1", "x" * (key.n.num_bytes + 1)) } # With options pssopts = { "rsa_padding_mode" => "pss", "rsa_pss_saltlen" => 20, "rsa_mgf1_md" => "SHA256" } sig_pss = key.sign_raw("SHA256", hash, pssopts) assert_equal true, key.verify("SHA256", sig_pss, data, pssopts) assert_equal true, key.verify_raw("SHA256", sig_pss, hash, pssopts) end def test_sign_verify_raw_legacy key = Fixtures.pkey("rsa-1") bits = key.n.num_bits # Need right size for raw mode plain0 = "x" * (bits/8) cipher = key.private_encrypt(plain0, OpenSSL::PKey::RSA::NO_PADDING) plain1 = key.public_decrypt(cipher, OpenSSL::PKey::RSA::NO_PADDING) assert_equal(plain0, plain1) # Need smaller size for pkcs1 mode plain0 = "x" * (bits/8 - 11) cipher1 = key.private_encrypt(plain0, OpenSSL::PKey::RSA::PKCS1_PADDING) plain1 = key.public_decrypt(cipher1, OpenSSL::PKey::RSA::PKCS1_PADDING) assert_equal(plain0, plain1) cipherdef = key.private_encrypt(plain0) # PKCS1_PADDING is default plain1 = key.public_decrypt(cipherdef) assert_equal(plain0, plain1) assert_equal(cipher1, cipherdef) # Failure cases assert_raise(ArgumentError){ key.private_encrypt() } assert_raise(ArgumentError){ key.private_encrypt("hi", 1, nil) } assert_raise(OpenSSL::PKey::PKeyError){ key.private_encrypt(plain0, 666) } end def test_verify_empty_rsa rsa = OpenSSL::PKey::RSA.new assert_raise(OpenSSL::PKey::PKeyError, "[Bug #12783]") { rsa.verify("SHA1", "a", "b") } end unless openssl?(3, 0, 0) # Empty RSA is not possible with OpenSSL >= 3.0 def test_sign_verify_pss key = Fixtures.pkey("rsa2048") data = "Sign me!" invalid_data = "Sign me?" signature = key.sign_pss("SHA256", data, salt_length: 20, mgf1_hash: "SHA256") assert_equal 256, signature.bytesize assert_equal true, key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA256") assert_equal true, key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA256") assert_equal false, key.verify_pss("SHA256", signature, invalid_data, salt_length: 20, mgf1_hash: "SHA256") signature = key.sign_pss("SHA256", data, salt_length: :digest, mgf1_hash: "SHA256") assert_equal true, key.verify_pss("SHA256", signature, data, salt_length: 32, mgf1_hash: "SHA256") assert_equal true, key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA256") assert_equal false, key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA256") # The sign_pss with `salt_length: :max` raises the "invalid salt length" # error in FIPS. We need to skip the tests in FIPS. # According to FIPS 186-5 section 5.4, the salt length shall be between zero # and the output block length of the digest function (inclusive). # # FIPS 186-5 section 5.4 PKCS #1 # https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf unless OpenSSL.fips_mode signature = key.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA256") # Should verify on the following salt_length (sLen). # sLen <= emLen (octat) - 2 - hLen (octet) = 2048 / 8 - 2 - 256 / 8 = 222 # https://datatracker.ietf.org/doc/html/rfc8017#section-9.1.1 assert_equal true, key.verify_pss("SHA256", signature, data, salt_length: 222, mgf1_hash: "SHA256") assert_equal true, key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA256") end assert_raise(OpenSSL::PKey::PKeyError) { key.sign_pss("SHA256", data, salt_length: 223, mgf1_hash: "SHA256") } end def test_encrypt_decrypt rsapriv = Fixtures.pkey("rsa-1") rsapub = OpenSSL::PKey.read(rsapriv.public_to_der) # Defaults to PKCS #1 v1.5 raw = "data" # According to the NIST SP 800-131A Rev. 2 section 6, PKCS#1 v1.5 padding is # not permitted for key agreement and key transport using RSA in FIPS. # https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf unless OpenSSL.fips_mode enc = rsapub.encrypt(raw) assert_equal raw, rsapriv.decrypt(enc) end # Invalid options assert_raise(OpenSSL::PKey::PKeyError) { rsapub.encrypt(raw, { "nonexistent" => "option" }) } end def test_encrypt_decrypt_legacy rsapriv = Fixtures.pkey("rsa-1") rsapub = OpenSSL::PKey.read(rsapriv.public_to_der) # Defaults to PKCS #1 v1.5 unless OpenSSL.fips_mode raw = "data" enc_legacy = rsapub.public_encrypt(raw) assert_equal raw, rsapriv.decrypt(enc_legacy) enc_new = rsapub.encrypt(raw) assert_equal raw, rsapriv.private_decrypt(enc_new) end # OAEP with default parameters raw = "data" enc_legacy = rsapub.public_encrypt(raw, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING) assert_equal raw, rsapriv.decrypt(enc_legacy, { "rsa_padding_mode" => "oaep" }) enc_new = rsapub.encrypt(raw, { "rsa_padding_mode" => "oaep" }) assert_equal raw, rsapriv.private_decrypt(enc_legacy, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING) end def test_export orig = Fixtures.pkey("rsa-1") pub = OpenSSL::PKey.read(orig.public_to_der) assert_not_equal orig.export, pub.export assert_equal orig.public_to_pem, pub.export # PKey is immutable in OpenSSL >= 3.0 if !openssl?(3, 0, 0) key = OpenSSL::PKey::RSA.new # key has only n, e and d key.set_key(orig.n, orig.e, orig.d) assert_equal orig.public_key.export, key.export # key has only n, e, d, p and q key.set_factors(orig.p, orig.q) assert_equal orig.public_key.export, key.export # key has n, e, d, p, q, dmp1, dmq1 and iqmp key.set_crt_params(orig.dmp1, orig.dmq1, orig.iqmp) assert_equal orig.export, key.export end end def test_to_der orig = Fixtures.pkey("rsa-1") pub = OpenSSL::PKey.read(orig.public_to_der) assert_not_equal orig.to_der, pub.to_der assert_equal orig.public_to_der, pub.to_der # PKey is immutable in OpenSSL >= 3.0 if !openssl?(3, 0, 0) key = OpenSSL::PKey::RSA.new # key has only n, e and d key.set_key(orig.n, orig.e, orig.d) assert_equal orig.public_key.to_der, key.to_der # key has only n, e, d, p and q key.set_factors(orig.p, orig.q) assert_equal orig.public_key.to_der, key.to_der # key has n, e, d, p, q, dmp1, dmq1 and iqmp key.set_crt_params(orig.dmp1, orig.dmq1, orig.iqmp) assert_equal orig.to_der, key.to_der end end def test_RSAPrivateKey rsa = Fixtures.pkey("rsa-1") asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(0), OpenSSL::ASN1::Integer(rsa.n), OpenSSL::ASN1::Integer(rsa.e), OpenSSL::ASN1::Integer(rsa.d), OpenSSL::ASN1::Integer(rsa.p), OpenSSL::ASN1::Integer(rsa.q), OpenSSL::ASN1::Integer(rsa.dmp1), OpenSSL::ASN1::Integer(rsa.dmq1), OpenSSL::ASN1::Integer(rsa.iqmp) ]) key = OpenSSL::PKey::RSA.new(asn1.to_der) assert_predicate key, :private? assert_same_rsa rsa, key pem = der_to_pem(asn1.to_der, "RSA PRIVATE KEY") key = OpenSSL::PKey::RSA.new(pem) assert_same_rsa rsa, key assert_equal asn1.to_der, rsa.to_der assert_equal pem, rsa.export # Unknown PEM prepended cert = issue_cert(OpenSSL::X509::Name.new([["CN", "nobody"]]), rsa, 1, [], nil, nil) str = cert.to_text + cert.to_pem + rsa.to_pem key = OpenSSL::PKey::RSA.new(str) assert_same_rsa rsa, key end def test_RSAPrivateKey_encrypted # PKCS #1 RSAPrivateKey with OpenSSL encryption omit_on_fips rsa = Fixtures.pkey("rsa2048") pem = der_to_encrypted_pem(rsa.to_der, "RSA PRIVATE KEY", "abcdef") key = OpenSSL::PKey::RSA.new(pem, "abcdef") assert_same_rsa rsa, key key = OpenSSL::PKey::RSA.new(pem) { "abcdef" } assert_same_rsa rsa, key cipher = OpenSSL::Cipher.new("aes-128-cbc") exported = rsa.to_pem(cipher, "abcdef\0\1") assert_same_rsa rsa, OpenSSL::PKey::RSA.new(exported, "abcdef\0\1") assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey::RSA.new(exported, "abcdef") } end def test_RSAPublicKey # PKCS #1 RSAPublicKey. Only decoding is supported orig = Fixtures.pkey("rsa-1") pub = OpenSSL::PKey::RSA.new(orig.public_to_der) asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(orig.n), OpenSSL::ASN1::Integer(orig.e) ]) key = OpenSSL::PKey::RSA.new(asn1.to_der) assert_not_predicate key, :private? assert_same_rsa pub, key pem = der_to_pem(asn1.to_der, "RSA PUBLIC KEY") key = OpenSSL::PKey::RSA.new(pem) assert_same_rsa pub, key end def test_PUBKEY orig = Fixtures.pkey("rsa-1") pub = OpenSSL::PKey::RSA.new(orig.public_to_der) asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::ObjectId("rsaEncryption"), OpenSSL::ASN1::Null(nil) ]), OpenSSL::ASN1::BitString( OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(orig.n), OpenSSL::ASN1::Integer(orig.e) ]).to_der ) ]) key = OpenSSL::PKey::RSA.new(asn1.to_der) assert_not_predicate key, :private? assert_same_rsa pub, key pem = der_to_pem(asn1.to_der, "PUBLIC KEY") key = OpenSSL::PKey::RSA.new(pem) assert_same_rsa pub, key assert_equal asn1.to_der, key.to_der assert_equal pem, key.export assert_equal asn1.to_der, orig.public_to_der assert_equal asn1.to_der, key.public_to_der assert_equal pem, orig.public_to_pem assert_equal pem, key.public_to_pem end def test_pem_passwd omit_on_fips key = Fixtures.pkey("rsa-1") pem3c = key.to_pem("aes-128-cbc", "key") assert_match (/ENCRYPTED/), pem3c assert_equal key.to_der, OpenSSL::PKey.read(pem3c, "key").to_der assert_equal key.to_der, OpenSSL::PKey.read(pem3c) { "key" }.to_der assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.read(pem3c) { nil } } end def test_private_encoding pkey = Fixtures.pkey("rsa-1") asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(0), OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::ObjectId("rsaEncryption"), OpenSSL::ASN1::Null(nil) ]), OpenSSL::ASN1::OctetString(pkey.to_der) ]) assert_equal asn1.to_der, pkey.private_to_der assert_same_rsa pkey, OpenSSL::PKey.read(asn1.to_der) pem = der_to_pem(asn1.to_der, "PRIVATE KEY") assert_equal pem, pkey.private_to_pem assert_same_rsa pkey, OpenSSL::PKey.read(pem) end def test_private_encoding_encrypted rsa = Fixtures.pkey("rsa2048") encoded = rsa.private_to_der("aes-128-cbc", "abcdef") asn1 = OpenSSL::ASN1.decode(encoded) # PKCS #8 EncryptedPrivateKeyInfo assert_kind_of OpenSSL::ASN1::Sequence, asn1 assert_equal 2, asn1.value.size assert_not_equal rsa.private_to_der, encoded assert_same_rsa rsa, OpenSSL::PKey.read(encoded, "abcdef") assert_same_rsa rsa, OpenSSL::PKey.read(encoded) { "abcdef" } assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.read(encoded, "abcxyz") } encoded = rsa.private_to_pem("aes-128-cbc", "abcdef") assert_match (/BEGIN ENCRYPTED PRIVATE KEY/), encoded.lines[0] assert_same_rsa rsa, OpenSSL::PKey.read(encoded, "abcdef") # Use openssl instead of certtool due to https://gitlab.com/gnutls/gnutls/-/issues/1632 # openssl pkcs8 -in test/openssl/fixtures/pkey/rsa2048.pem -topk8 -v2 aes-128-cbc -passout pass:abcdef pem = <<~EOF -----BEGIN ENCRYPTED PRIVATE KEY----- MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIay5V8CDQi5oCAggA MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBB6eyagcbsvdQlM1kPcH7kiBIIE 0Ng1apIyoPAZ4BfC4kMNeSmeAv3XspxqYi3uWzXiNyTcoE6390swrwM6WvdpXvLI /n/V06krxPZ9X4fBG2kLUzXt5f09lEvmQU1HW1wJGU5Sq3bNeXBrlJF4DzJE4WWd whVVvNMm44ghdzN/jGSw3z+6d717N+waa7vrpBDsHjhsPNwxpyzUvcFPFysTazxx kN/dziIBF6SRKi6w8VaJEMQ8czGu5T3jOc2e/1p3/AYhHLPS4NHhLR5OUh0TKqLK tANAqI9YqCAjhqcYCmN3mMQXY52VfOqG9hlX1x9ZQyqiH7l102EWbPqouk6bCBLQ wHepPg4uK99Wsdh65qEryNnXQ5ZmO6aGb6T3TFENCaNKmi8Nh+/5dr7J7YfhIwpo FqHvk0hrZ8r3EQlr8/td0Yb1/IKzeQ34638uXf9UxK7C6o+ilsmJDR4PHJUfZL23 Yb9qWJ0GEzd5AMsI7x6KuUxSuH9nKniv5Tzyty3Xmb4FwXUyADWE19cVuaT+HrFz GraKnA3UXbEgWAU48/l4K2HcAHyHDD2Kbp8k+o1zUkH0fWUdfE6OUGtx19Fv44Jh B7xDngK8K48C6nrj06/DSYfXlb2X7WQiapeG4jt6U57tLH2XAjHCkvu0IBZ+//+P yIWduEHQ3w8FBRcIsTNJo5CjkGk580TVQB/OBLWfX48Ay3oF9zgnomDIlVjl9D0n lKxw/KMCLkvB78rUeGbr1Kwj36FhGpTBw3FgcYGa5oWFZTlcOgMTXLqlbb9JnDlA Zs7Tu0WTyOTV/Dne9nEm39Dzu6wRojiIpmygTD4FI7rmOy3CYNvL3XPv7XQj0hny Ee/fLxugYlQnwPZSqOVEQY2HsG7AmEHRsvy4bIWIGt+yzAPZixt9MUdJh91ttRt7 QA/8J1pAsGqEuQpF6UUINZop3J7twfhO4zWYN/NNQ52eWNX2KLfjfGRhrvatzmZ0 BuCsCI9hwEeE6PTlhbX1Rs177MrDc3vlqz2V3Po0OrFjXAyg9DR/OC4iK5wOG2ZD 7StVSP8bzwQXsz3fJ0ardKXgnU2YDAP6Vykjgt+nFI09HV/S2faOc2g/UK4Y2khl J93u/GHMz/Kr3bKWGY1/6nPdIdFheQjsiNhd5gI4tWik2B3QwU9mETToZ2LSvDHU jYCys576xJLkdMM6nJdq72z4tCoES9IxyHVs4uLjHKIo/ZtKr+8xDo8IL4ax3U8+ NMhs/lwReHmPGahm1fu9zLRbNCVL7e0zrOqbjvKcSEftObpV/LLcPYXtEm+lZcck /PMw49HSE364anKEXCH1cyVWJwdZRpFUHvRpLIrpHru7/cthhiEMdLgK1/x8sLob DiyieLxH1DPeXT4X+z94ER4IuPVOcV5AXc/omghispEX6DNUnn5jC4e3WyabjUbw MuO9lVH9Wi2/ynExCqVmQkdbTXuLwjni1fJ27Q5zb0aCmhO8eq6P869NCjhJuiUj NI9XtGLP50YVWE0kL8KEJqnyFudky8Khzk4/dyixQFqin5GfT4vetrLunGHy7lRB 3LpnFrpMOr+0xr1RW1k9vlmjRsJSiojJfReYO7gH3B5swiww2azogoL+4jhF1Jxh OYLWdkKhP2jSVGqtIDtny0O4lBm2+hLpWjiI0mJQ7wdA -----END ENCRYPTED PRIVATE KEY----- EOF assert_same_rsa rsa, OpenSSL::PKey.read(pem, "abcdef") end def test_params key = Fixtures.pkey("rsa2048") assert_equal(2048, key.n.num_bits) assert_equal(key.n, key.params["n"]) assert_equal(65537, key.e) assert_equal(key.e, key.params["e"]) [:d, :p, :q, :dmp1, :dmq1, :iqmp].each do |name| assert_kind_of(OpenSSL::BN, key.send(name)) assert_equal(key.send(name), key.params[name.to_s]) end pubkey = OpenSSL::PKey.read(key.public_to_der) assert_equal(key.n, pubkey.n) assert_equal(key.e, pubkey.e) [:d, :p, :q, :dmp1, :dmq1, :iqmp].each do |name| assert_nil(pubkey.send(name)) assert_nil(pubkey.params[name.to_s]) end end def test_dup key = Fixtures.pkey("rsa-1") key2 = key.dup assert_equal key.params, key2.params # PKey is immutable in OpenSSL >= 3.0 if !openssl?(3, 0, 0) key2.set_key(key2.n, 3, key2.d) assert_not_equal key.params, key2.params end end def test_marshal key = Fixtures.pkey("rsa-1") deserialized = Marshal.load(Marshal.dump(key)) assert_equal key.to_der, deserialized.to_der end private def assert_same_rsa(expected, key) check_component(expected, key, [:n, :e, :d, :p, :q, :dmp1, :dmq1, :iqmp]) end end end