# 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", "abcdefgh")
    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, "abcdefgh")
    assert_same_rsa rsa, OpenSSL::PKey.read(encoded) { "abcdefgh" }
    assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.read(encoded, "abcxyz") }

    encoded = rsa.private_to_pem("aes-128-cbc", "abcdefgh")
    assert_match (/BEGIN ENCRYPTED PRIVATE KEY/), encoded.lines[0]
    assert_same_rsa rsa, OpenSSL::PKey.read(encoded, "abcdefgh")

    # 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:abcdefgh
    pem = <<~EOF
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQ+Sg92Hgy8EgVPf7t
Hen1qwICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEB5UX2xdDO8/AKA8
+Y5CZyUEggTQkArh4mMPpnAe3xOcDKMz8KCn5lrLb/6Dla7Rp9LHKGkUfyI11EZt
m+OIriwy9oDQquKyVuLQVGAxXKk+3pyxMqLB0i3hLYamT3vzoPctyVwjuRuKoU3E
CbF0YhCoxvWMvjHsolwYzx00DbLXouE4BGKvPjnhw5hwtdoZ9Px0ZnCXCxVXi8z/
mlw7a2ptKEiHQVjuPPbttq+dA+ez7pbWonWVod5TMaPtyEZu5XfPD+0pMboceHZg
H8ehgUhV3mzEJiisFGg1q9hj+4BaFl5m4tvqp43inCCdShE78CNnOPzJ7WCjKJqi
jGvHjeMoVx3rZXHcZDAzfIZvDigp9uAfzjRJjpRG8sg5sDQVC7vdUhQDe5TorKT2
Vb0tdVYxoEpMJ3dhU6Ds5JxMR6GTLjsjTqOkAl6db3HxulwfEpr7YjOpfODR+ttA
BeIcUcMLsDHayIaQaMLIftHxOkfX7UxoFW9CMG5UMQf/m3eEgVUwgK/E5sUJRUTo
yhRzJ4NAP4fgc4YH9tbzvUrhfdCXCBEOn6IlDQL66SZr8Mm+Ggu4Ij4TnKWXLrXL
nSTDDa42kPOvtedKqxC/uXE7rrfh+uyw6J6OjSl6u86TIebndLuDo5DTdWKh8rsg
fvZZ6332dfMp8JC9/4YnYIJdI7acInSoyHp52OB+2+dgYCr5OrZFjjKS7nELVfo7
OxGy6uH3NHF9qyUEf3MN17TRHI7jP3zKbXcDTPSyxLQkWe/CU5B251CTmoTSidSW
EhKnPlGZYbpVQJ4KGEL5UeY8W9PXQo4Dl7TmXBGvuPqNF8kMB3XrPIph7GmihmX0
nlJqLk9eiRFmUETS0IdAyKJrm4R9Hf6rjYCbXlaApylyVUdSZ2BxgeoTY9BA6Kgf
3xlgMv01MoUkXMx2+OLIc9MzhButQiDxh3mfS012CjKqUFrJhRSa8DOpUfVgmXpq
/HP4drWamLWYJR8FsmJS11ZYc1EK/ctJTSpqfewvoUGOSHomhh7zXn1Acb6+9/3p
bcrJjoR5K8Jg6NlG4dSNkpY/x92I7bFLXFqELIH5tteDrlQen5eASjaiyPPAoOw8
IGfOmFS4VUPh1VP6g8Jtn5Hr2qXB3DoQoI6EvUZhJ6GJfi67mx5VKux6G9MzJkix
GU1cL4WzWK2DU0l39UxXjS+4TmOYbrqLVnVMjusX0fwb8LkDC/fVohbhLwhHNwu6
nSTSEpS9zSDrv1JXFtAtPv6XCSFs6ssPWJMwGSdThn7EfV0GEhG2mCzTyVhwxxQo
6U/Suqq4oMZoracPUCZx0E4u/bb4KBoFA/eBNPJENTR18IiV+D7wAxlxauO3N1t4
iJxwrrvSgQPmOGuxrh5LVD41UXYUWLtndzabnpByppFn2MbmvrqJgon0MSs84cTA
7scnbPu1V3PpKy/t67gtVw9Ue8hLjrskWB1JPFYr7vRWvJzYjfbflyroF+QEJ3TA
6rTfUC9+ePci6T+i9jF4xcmzqYzRtnGtp5nRUitJGw0uwBTDwzfI2WD6ltvvu7lc
pHuzvY5zEapuu1JhjHLUd+OE8rVVM999DUXo/IDLsWyRCphCiYfVXJNogd9rB0Ta
5AhVgpRhxkarBURZyLTYj7NRxCsbHq7XExJNrIdRG/KlBQfyEyIzZ7E=
-----END ENCRYPTED PRIVATE KEY-----
    EOF
    assert_same_rsa rsa, OpenSSL::PKey.read(pem, "abcdefgh")
  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
