LibCrypto+LibWeb: Replace RSA_OAEP-EME implementation

This replaces the old `OAEP` implementation with one backed by OpenSSL.
The changes also include some added modularity to the RSA class by
making the `RSA_EME` and `RSA_EMSE` for encryption/decryption and
signing/verifying respectively.
This commit is contained in:
devgianlu 2024-12-25 22:40:50 +01:00 committed by Ali Mohammad Pur
parent 70bc26e32a
commit a59b48cffc
Notes: github-actions[bot] 2025-01-13 16:01:20 +00:00
3 changed files with 60 additions and 238 deletions

View file

@ -360,79 +360,6 @@ void RSA::import_public_key(ReadonlyBytes bytes, bool pem)
m_public_key = maybe_key.release_value().public_key;
}
ErrorOr<ByteBuffer> RSA_PKCS1_EME::encrypt(ReadonlyBytes in)
{
auto mod_len = (m_public_key.modulus().trimmed_length() * sizeof(u32) * 8 + 7) / 8;
dbgln_if(CRYPTO_DEBUG, "key size: {}", mod_len);
if (in.size() > mod_len - 11)
return Error::from_string_literal("Message too long");
auto out = TRY(ByteBuffer::create_uninitialized(mod_len));
auto ps_length = mod_len - in.size() - 3;
Vector<u8, 8096> ps;
ps.resize(ps_length);
fill_with_secure_random(ps);
// since fill_with_random can create zeros (shocking!)
// we have to go through and un-zero the zeros
for (size_t i = 0; i < ps_length; ++i) {
while (!ps[i])
ps[i] = get_random<u8>();
}
u8 paddings[] { 0x00, 0x02 };
out.overwrite(0, paddings, 2);
out.overwrite(2, ps.data(), ps_length);
out.overwrite(2 + ps_length, paddings, 1);
out.overwrite(3 + ps_length, in.data(), in.size());
out.trim(3 + ps_length + in.size(), true); // should be a single block
dbgln_if(CRYPTO_DEBUG, "padded output size: {} buffer size: {}", 3 + ps_length + in.size(), out.size());
return TRY(RSA::encrypt(out));
}
ErrorOr<ByteBuffer> RSA_PKCS1_EME::decrypt(ReadonlyBytes in)
{
auto mod_len = (m_public_key.modulus().trimmed_length() * sizeof(u32) * 8 + 7) / 8;
if (in.size() != mod_len)
return Error::from_string_literal("Invalid input size");
auto out = TRY(RSA::decrypt(in));
if (out.size() < RSA::output_size())
return Error::from_string_literal("Not enough data after decryption");
if (out[0] != 0x00 || out[1] != 0x02)
return Error::from_string_literal("Invalid padding");
size_t offset = 2;
while (offset < out.size() && out[offset])
++offset;
if (offset == out.size())
return Error::from_string_literal("Garbage data, no zero to split padding");
++offset;
if (offset - 3 < 8)
return Error::from_string_literal("PS too small");
return out.slice(offset, out.size() - offset);
}
ErrorOr<ByteBuffer> RSA_PKCS1_EME::sign(ReadonlyBytes)
{
return Error::from_string_literal("FIXME: RSA_PKCS_EME::sign");
}
ErrorOr<bool> RSA_PKCS1_EME::verify(ReadonlyBytes, ReadonlyBytes)
{
return Error::from_string_literal("FIXME: RSA_PKCS_EME::verify");
}
ErrorOr<EVP_MD const*> hash_kind_to_hash_type(Hash::HashKind hash_kind)
{
switch (hash_kind) {
@ -455,7 +382,7 @@ ErrorOr<EVP_MD const*> hash_kind_to_hash_type(Hash::HashKind hash_kind)
}
}
ErrorOr<bool> RSA_PKCS1_EMSA::verify(ReadonlyBytes message, ReadonlyBytes signature)
ErrorOr<bool> RSA_EMSA::verify(ReadonlyBytes message, ReadonlyBytes signature)
{
auto key = TRY(public_key_to_openssl_pkey(m_public_key));
auto const* hash_type = TRY(hash_kind_to_hash_type(m_hash_kind));
@ -477,7 +404,7 @@ ErrorOr<bool> RSA_PKCS1_EMSA::verify(ReadonlyBytes message, ReadonlyBytes signat
VERIFY_NOT_REACHED();
}
ErrorOr<ByteBuffer> RSA_PKCS1_EMSA::sign(ReadonlyBytes message)
ErrorOr<ByteBuffer> RSA_EMSA::sign(ReadonlyBytes message)
{
auto key = TRY(private_key_to_openssl_pkey(m_private_key));
auto const* hash_type = TRY(hash_kind_to_hash_type(m_hash_kind));
@ -510,4 +437,21 @@ ErrorOr<void> RSA_PKCS1_EMSA::configure(OpenSSL_PKEY_CTX& ctx)
return {};
}
ErrorOr<void> RSA_OAEP_EME::configure(OpenSSL_PKEY_CTX& ctx)
{
OPENSSL_TRY(EVP_PKEY_CTX_set_rsa_padding(ctx.ptr(), RSA_PKCS1_OAEP_PADDING));
OPENSSL_TRY(EVP_PKEY_CTX_set_rsa_oaep_md(ctx.ptr(), TRY(hash_kind_to_hash_type(m_hash_kind))));
OPENSSL_TRY(EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.ptr(), TRY(hash_kind_to_hash_type(m_hash_kind))));
if (m_label.has_value() && !m_label->is_empty()) {
// https://docs.openssl.org/3.0/man3/EVP_PKEY_CTX_ctrl/#rsa-parameters
// The library takes ownership of the label so the caller should not free the original memory pointed to by label.
auto* label = OPENSSL_malloc(m_label->size());
memcpy(label, m_label->data(), m_label->size());
OPENSSL_TRY(EVP_PKEY_CTX_set0_rsa_oaep_label(ctx.ptr(), label, m_label->size()));
}
return {};
}
}

View file

@ -29,7 +29,6 @@
#include <LibCrypto/Hash/SHA1.h>
#include <LibCrypto/Hash/SHA2.h>
#include <LibCrypto/PK/RSA.h>
#include <LibCrypto/Padding/OAEP.h>
#include <LibCrypto/SecureRandom.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/ArrayBuffer.h>
@ -665,30 +664,27 @@ WebIDL::ExceptionOr<GC::Ref<JS::ArrayBuffer>> RSAOAEP::encrypt(AlgorithmParams c
// 3. Perform the encryption operation defined in Section 7.1 of [RFC3447] with the key represented by key as the recipient's RSA public key,
// the contents of plaintext as the message to be encrypted, M and label as the label, L, and with the hash function specified by the hash attribute
// of the [[algorithm]] internal slot of key as the Hash option and MGF1 (defined in Section B.2.1 of [RFC3447]) as the MGF option.
auto error_message = MUST(String::formatted("Invalid hash function '{}'", hash));
ErrorOr<ByteBuffer> maybe_padding = Error::from_string_view(error_message.bytes_as_string_view());
if (hash == "SHA-1") {
maybe_padding = ::Crypto::Padding::OAEP::eme_encode<::Crypto::Hash::SHA1, ::Crypto::Hash::MGF>(plaintext, label, public_key.length());
} else if (hash == "SHA-256") {
maybe_padding = ::Crypto::Padding::OAEP::eme_encode<::Crypto::Hash::SHA256, ::Crypto::Hash::MGF>(plaintext, label, public_key.length());
} else if (hash == "SHA-384") {
maybe_padding = ::Crypto::Padding::OAEP::eme_encode<::Crypto::Hash::SHA384, ::Crypto::Hash::MGF>(plaintext, label, public_key.length());
} else if (hash == "SHA-512") {
maybe_padding = ::Crypto::Padding::OAEP::eme_encode<::Crypto::Hash::SHA512, ::Crypto::Hash::MGF>(plaintext, label, public_key.length());
}
Optional<::Crypto::Hash::HashKind> hash_kind = {};
if (hash == "SHA-1")
hash_kind = ::Crypto::Hash::HashKind::SHA1;
else if (hash == "SHA-256")
hash_kind = ::Crypto::Hash::HashKind::SHA256;
else if (hash == "SHA-384")
hash_kind = ::Crypto::Hash::HashKind::SHA384;
else if (hash == "SHA-512")
hash_kind = ::Crypto::Hash::HashKind::SHA512;
// 4. If performing the operation results in an error, then throw an OperationError.
if (maybe_padding.is_error()) {
auto error_message = MUST(String::from_utf8(maybe_padding.error().string_literal()));
if (!hash_kind.has_value()) {
auto error_message = MUST(String::formatted("Invalid hash function '{}'", hash));
return WebIDL::OperationError::create(realm, error_message);
}
auto padding = maybe_padding.release_value();
// 5. Let ciphertext be the value C that results from performing the operation.
auto rsa = ::Crypto::PK::RSA { public_key };
auto maybe_ciphertext = rsa.encrypt(padding);
auto rsa = ::Crypto::PK::RSA_OAEP_EME { *hash_kind, public_key };
rsa.set_label(label);
auto maybe_ciphertext = rsa.encrypt(plaintext);
if (maybe_ciphertext.is_error())
return WebIDL::OperationError::create(realm, "Failed to encrypt"_string);
@ -717,36 +713,32 @@ WebIDL::ExceptionOr<GC::Ref<JS::ArrayBuffer>> RSAOAEP::decrypt(AlgorithmParams c
// 3. Perform the decryption operation defined in Section 7.1 of [RFC3447] with the key represented by key as the recipient's RSA private key,
// the contents of ciphertext as the ciphertext to be decrypted, C, and label as the label, L, and with the hash function specified by the hash attribute
// of the [[algorithm]] internal slot of key as the Hash option and MGF1 (defined in Section B.2.1 of [RFC3447]) as the MGF option.
auto rsa = ::Crypto::PK::RSA { private_key };
u32 private_key_length = private_key.length();
auto maybe_padding = rsa.decrypt(ciphertext);
if (maybe_padding.is_error())
return WebIDL::OperationError::create(realm, "Failed to encrypt"_string);
auto error_message = MUST(String::formatted("Invalid hash function '{}'", hash));
ErrorOr<ByteBuffer> maybe_plaintext = Error::from_string_view(error_message.bytes_as_string_view());
if (hash == "SHA-1") {
maybe_plaintext = ::Crypto::Padding::OAEP::eme_decode<::Crypto::Hash::SHA1, ::Crypto::Hash::MGF>(maybe_padding.release_value(), label, private_key_length);
} else if (hash == "SHA-256") {
maybe_plaintext = ::Crypto::Padding::OAEP::eme_decode<::Crypto::Hash::SHA256, ::Crypto::Hash::MGF>(maybe_padding.release_value(), label, private_key_length);
} else if (hash == "SHA-384") {
maybe_plaintext = ::Crypto::Padding::OAEP::eme_decode<::Crypto::Hash::SHA384, ::Crypto::Hash::MGF>(maybe_padding.release_value(), label, private_key_length);
} else if (hash == "SHA-512") {
maybe_plaintext = ::Crypto::Padding::OAEP::eme_decode<::Crypto::Hash::SHA512, ::Crypto::Hash::MGF>(maybe_padding.release_value(), label, private_key_length);
}
Optional<::Crypto::Hash::HashKind> hash_kind = {};
if (hash == "SHA-1")
hash_kind = ::Crypto::Hash::HashKind::SHA1;
else if (hash == "SHA-256")
hash_kind = ::Crypto::Hash::HashKind::SHA256;
else if (hash == "SHA-384")
hash_kind = ::Crypto::Hash::HashKind::SHA384;
else if (hash == "SHA-512")
hash_kind = ::Crypto::Hash::HashKind::SHA512;
// 4. If performing the operation results in an error, then throw an OperationError.
if (maybe_plaintext.is_error()) {
auto error_message = MUST(String::from_utf8(maybe_plaintext.error().string_literal()));
if (!hash_kind.has_value()) {
auto error_message = MUST(String::formatted("Invalid hash function '{}'", hash));
return WebIDL::OperationError::create(realm, error_message);
}
// 5. Let plaintext the value M that results from performing the operation.
auto plaintext = maybe_plaintext.release_value();
auto rsa = ::Crypto::PK::RSA_OAEP_EME { *hash_kind, private_key };
rsa.set_label(label);
auto maybe_plaintext = rsa.decrypt(ciphertext);
if (maybe_plaintext.is_error())
return WebIDL::OperationError::create(realm, "Failed to encrypt"_string);
// 6. Return the result of creating an ArrayBuffer containing plaintext.
return JS::ArrayBuffer::create(realm, move(plaintext));
return JS::ArrayBuffer::create(realm, maybe_plaintext.release_value());
}
// https://w3c.github.io/webcrypto/#rsa-oaep-operations

View file

@ -1,138 +1,24 @@
/*
* Copyright (c) 2024, stelar7 <dudedbz@gmail.com>
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/ByteBuffer.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
#include <LibCrypto/Hash/MGF.h>
#include <LibCrypto/Hash/SHA1.h>
#include <LibCrypto/Hash/SHA2.h>
#include <LibCrypto/PK/RSA.h>
#include <LibCrypto/Padding/OAEP.h>
#include <LibTest/TestCase.h>
// https://www.inf.pucrs.br/~calazans/graduate/TPVLSI_I/RSA-oaep_spec.pdf
TEST_CASE(test_oaep)
{
u8 message_raw[16] {
0xd4, 0x36, 0xe9, 0x95, 0x69, 0xfd, 0x32, 0xa7, 0xc8, 0xa0, 0x5b, 0xbc, 0x90, 0xd3, 0x2c, 0x49
};
auto message = ReadonlyBytes { message_raw, 16 };
auto msg = "WellHelloFriendsWellHelloFriendsWellHelloFriendsWellHelloFriends"sv.bytes();
u8 params_raw[0] {};
auto params = ReadonlyBytes { params_raw, 0 };
auto keypair = TRY_OR_FAIL(Crypto::PK::RSA::generate_key_pair(1024));
auto rsa = Crypto::PK::RSA_OAEP_EME(Crypto::Hash::HashKind::SHA1, keypair);
rsa.set_label("LABEL"sv.bytes());
u8 expected_raw[127] {
0xeb, 0x7a, 0x19, 0xac, 0xe9, 0xe3, 0x00, 0x63,
0x50, 0xe3, 0x29, 0x50, 0x4b, 0x45, 0xe2, 0xca,
0x82, 0x31, 0x0b, 0x26, 0xdc, 0xd8, 0x7d, 0x5c,
0x68, 0xf1, 0xee, 0xa8, 0xf5, 0x52, 0x67, 0xc3,
0x1b, 0x2e, 0x8b, 0xb4, 0x25, 0x1f, 0x84, 0xd7,
0xe0, 0xb2, 0xc0, 0x46, 0x26, 0xf5, 0xaf, 0xf9,
0x3e, 0xdc, 0xfb, 0x25, 0xc9, 0xc2, 0xb3, 0xff,
0x8a, 0xe1, 0x0e, 0x83, 0x9a, 0x2d, 0xdb, 0x4c,
0xdc, 0xfe, 0x4f, 0xf4, 0x77, 0x28, 0xb4, 0xa1,
0xb7, 0xc1, 0x36, 0x2b, 0xaa, 0xd2, 0x9a, 0xb4,
0x8d, 0x28, 0x69, 0xd5, 0x02, 0x41, 0x21, 0x43,
0x58, 0x11, 0x59, 0x1b, 0xe3, 0x92, 0xf9, 0x82,
0xfb, 0x3e, 0x87, 0xd0, 0x95, 0xae, 0xb4, 0x04,
0x48, 0xdb, 0x97, 0x2f, 0x3a, 0xc1, 0x4f, 0x7b,
0xc2, 0x75, 0x19, 0x52, 0x81, 0xce, 0x32, 0xd2,
0xf1, 0xb7, 0x6d, 0x4d, 0x35, 0x3e, 0x2d
};
auto expected = ReadonlyBytes { expected_raw, 127 };
u8 seed_data[20] {
0xaa, 0xfd, 0x12, 0xf6, 0x59, 0xca, 0xe6, 0x34,
0x89, 0xb4, 0x79, 0xe5, 0x07, 0x6d, 0xde, 0xc2,
0xf0, 0x6c, 0xb5, 0x8f
};
auto maybe_result = Crypto::Padding::OAEP::encode<Crypto::Hash::SHA1, Crypto::Hash::MGF>(
message,
params,
127,
[&](auto buffer) {
memcpy(buffer.data(), seed_data, 20);
});
auto result = maybe_result.release_value();
EXPECT_EQ(expected, result);
u8 n_bytes[128] {
0xbb, 0xf8, 0x2f, 0x09, 0x06, 0x82, 0xce, 0x9c, 0x23, 0x38, 0xac, 0x2b, 0x9d, 0xa8, 0x71, 0xf7,
0x36, 0x8d, 0x07, 0xee, 0xd4, 0x10, 0x43, 0xa4, 0x40, 0xd6, 0xb6, 0xf0, 0x74, 0x54, 0xf5, 0x1f,
0xb8, 0xdf, 0xba, 0xaf, 0x03, 0x5c, 0x02, 0xab, 0x61, 0xea, 0x48, 0xce, 0xeb, 0x6f, 0xcd, 0x48,
0x76, 0xed, 0x52, 0x0d, 0x60, 0xe1, 0xec, 0x46, 0x19, 0x71, 0x9d, 0x8a, 0x5b, 0x8b, 0x80, 0x7f,
0xaf, 0xb8, 0xe0, 0xa3, 0xdf, 0xc7, 0x37, 0x72, 0x3e, 0xe6, 0xb4, 0xb7, 0xd9, 0x3a, 0x25, 0x84,
0xee, 0x6a, 0x64, 0x9d, 0x06, 0x09, 0x53, 0x74, 0x88, 0x34, 0xb2, 0x45, 0x45, 0x98, 0x39, 0x4e,
0xe0, 0xaa, 0xb1, 0x2d, 0x7b, 0x61, 0xa5, 0x1f, 0x52, 0x7a, 0x9a, 0x41, 0xf6, 0xc1, 0x68, 0x7f,
0xe2, 0x53, 0x72, 0x98, 0xca, 0x2a, 0x8f, 0x59, 0x46, 0xf8, 0xe5, 0xfd, 0x09, 0x1d, 0xbd, 0xcb
};
u8 e_bytes[1] { 0x11 };
u8 p_bytes[64] {
0xee, 0xcf, 0xae, 0x81, 0xb1, 0xb9, 0xb3, 0xc9, 0x08, 0x81, 0x0b, 0x10, 0xa1, 0xb5, 0x60, 0x01,
0x99, 0xeb, 0x9f, 0x44, 0xae, 0xf4, 0xfd, 0xa4, 0x93, 0xb8, 0x1a, 0x9e, 0x3d, 0x84, 0xf6, 0x32,
0x12, 0x4e, 0xf0, 0x23, 0x6e, 0x5d, 0x1e, 0x3b, 0x7e, 0x28, 0xfa, 0xe7, 0xaa, 0x04, 0x0a, 0x2d,
0x5b, 0x25, 0x21, 0x76, 0x45, 0x9d, 0x1f, 0x39, 0x75, 0x41, 0xba, 0x2a, 0x58, 0xfb, 0x65, 0x99
};
u8 q_bytes[64] {
0xc9, 0x7f, 0xb1, 0xf0, 0x27, 0xf4, 0x53, 0xf6, 0x34, 0x12, 0x33, 0xea, 0xaa, 0xd1, 0xd9, 0x35,
0x3f, 0x6c, 0x42, 0xd0, 0x88, 0x66, 0xb1, 0xd0, 0x5a, 0x0f, 0x20, 0x35, 0x02, 0x8b, 0x9d, 0x86,
0x98, 0x40, 0xb4, 0x16, 0x66, 0xb4, 0x2e, 0x92, 0xea, 0x0d, 0xa3, 0xb4, 0x32, 0x04, 0xb5, 0xcf,
0xce, 0x33, 0x52, 0x52, 0x4d, 0x04, 0x16, 0xa5, 0xa4, 0x41, 0xe7, 0x00, 0xaf, 0x46, 0x15, 0x03
};
u8 dp_bytes[64] {
0x54, 0x49, 0x4c, 0xa6, 0x3e, 0xba, 0x03, 0x37, 0xe4, 0xe2, 0x40, 0x23, 0xfc, 0xd6, 0x9a, 0x5a,
0xeb, 0x07, 0xdd, 0xdc, 0x01, 0x83, 0xa4, 0xd0, 0xac, 0x9b, 0x54, 0xb0, 0x51, 0xf2, 0xb1, 0x3e,
0xd9, 0x49, 0x09, 0x75, 0xea, 0xb7, 0x74, 0x14, 0xff, 0x59, 0xc1, 0xf7, 0x69, 0x2e, 0x9a, 0x2e,
0x20, 0x2b, 0x38, 0xfc, 0x91, 0x0a, 0x47, 0x41, 0x74, 0xad, 0xc9, 0x3c, 0x1f, 0x67, 0xc9, 0x81
};
u8 dq_bytes[64] {
0x47, 0x1e, 0x02, 0x90, 0xff, 0x0a, 0xf0, 0x75, 0x03, 0x51, 0xb7, 0xf8, 0x78, 0x86, 0x4c, 0xa9,
0x61, 0xad, 0xbd, 0x3a, 0x8a, 0x7e, 0x99, 0x1c, 0x5c, 0x05, 0x56, 0xa9, 0x4c, 0x31, 0x46, 0xa7,
0xf9, 0x80, 0x3f, 0x8f, 0x6f, 0x8a, 0xe3, 0x42, 0xe9, 0x31, 0xfd, 0x8a, 0xe4, 0x7a, 0x22, 0x0d,
0x1b, 0x99, 0xa4, 0x95, 0x84, 0x98, 0x07, 0xfe, 0x39, 0xf9, 0x24, 0x5a, 0x98, 0x36, 0xda, 0x3d
};
u8 qinv_bytes[64] {
0xb0, 0x6c, 0x4f, 0xda, 0xbb, 0x63, 0x01, 0x19, 0x8d, 0x26, 0x5b, 0xdb, 0xae, 0x94, 0x23, 0xb3,
0x80, 0xf2, 0x71, 0xf7, 0x34, 0x53, 0x88, 0x50, 0x93, 0x07, 0x7f, 0xcd, 0x39, 0xe2, 0x11, 0x9f,
0xc9, 0x86, 0x32, 0x15, 0x4f, 0x58, 0x83, 0xb1, 0x67, 0xa9, 0x67, 0xbf, 0x40, 0x2b, 0x4e, 0x9e,
0x2e, 0x0f, 0x96, 0x56, 0xe6, 0x98, 0xea, 0x36, 0x66, 0xed, 0xfb, 0x25, 0x79, 0x80, 0x39, 0xf7
};
u8 expected_rsa_value_bytes[128] {
0x12, 0x53, 0xe0, 0x4d, 0xc0, 0xa5, 0x39, 0x7b, 0xb4, 0x4a, 0x7a, 0xb8, 0x7e, 0x9b, 0xf2, 0xa0,
0x39, 0xa3, 0x3d, 0x1e, 0x99, 0x6f, 0xc8, 0x2a, 0x94, 0xcc, 0xd3, 0x00, 0x74, 0xc9, 0x5d, 0xf7,
0x63, 0x72, 0x20, 0x17, 0x06, 0x9e, 0x52, 0x68, 0xda, 0x5d, 0x1c, 0x0b, 0x4f, 0x87, 0x2c, 0xf6,
0x53, 0xc1, 0x1d, 0xf8, 0x23, 0x14, 0xa6, 0x79, 0x68, 0xdf, 0xea, 0xe2, 0x8d, 0xef, 0x04, 0xbb,
0x6d, 0x84, 0xb1, 0xc3, 0x1d, 0x65, 0x4a, 0x19, 0x70, 0xe5, 0x78, 0x3b, 0xd6, 0xeb, 0x96, 0xa0,
0x24, 0xc2, 0xca, 0x2f, 0x4a, 0x90, 0xfe, 0x9f, 0x2e, 0xf5, 0xc9, 0xc1, 0x40, 0xe5, 0xbb, 0x48,
0xda, 0x95, 0x36, 0xad, 0x87, 0x00, 0xc8, 0x4f, 0xc9, 0x13, 0x0a, 0xde, 0xa7, 0x4e, 0x55, 0x8d,
0x51, 0xa7, 0x4d, 0xdf, 0x85, 0xd8, 0xb5, 0x0d, 0xe9, 0x68, 0x38, 0xd6, 0x06, 0x3e, 0x09, 0x55
};
auto expected_rsa_value = ReadonlyBytes { expected_rsa_value_bytes, 128 };
auto n = Crypto::UnsignedBigInteger::import_data(n_bytes, 128);
auto e = Crypto::UnsignedBigInteger::import_data(e_bytes, 1);
auto p = Crypto::UnsignedBigInteger::import_data(p_bytes, 64);
auto q = Crypto::UnsignedBigInteger::import_data(q_bytes, 64);
auto dp = Crypto::UnsignedBigInteger::import_data(dp_bytes, 64);
auto dq = Crypto::UnsignedBigInteger::import_data(dq_bytes, 64);
auto qinv = Crypto::UnsignedBigInteger::import_data(qinv_bytes, 64);
auto private_key = Crypto::PK::RSAPrivateKey<>::from_crt(n, e, p, q, dp, dq, qinv);
auto public_key = Crypto::PK::RSAPublicKey(n, e);
auto rsa = Crypto::PK::RSA(public_key, private_key);
auto enc = TRY_OR_FAIL(rsa.encrypt(result));
EXPECT_EQ(expected_rsa_value, enc);
auto enc = TRY_OR_FAIL(rsa.encrypt(msg));
auto dec = TRY_OR_FAIL(rsa.decrypt(enc));
EXPECT_EQ(msg, dec);
}