diff --git a/aws-lc-rs-testing/tests/rsa_test.rs b/aws-lc-rs-testing/tests/rsa_test.rs new file mode 100644 index 00000000000..1d009b9d23e --- /dev/null +++ b/aws-lc-rs-testing/tests/rsa_test.rs @@ -0,0 +1,57 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +use aws_lc_rs::rsa::{ + Pkcs1PrivateDecryptingKey, Pkcs1PublicEncryptingKey, PrivateDecryptingKey, PublicEncryptingKey, +}; +use openssl::rsa::{Padding, Rsa}; + +#[test] +fn rsa2048_pkcs1_openssl_interop() { + const PKCS8_PRIVATE_KEY: &[u8] = + include_bytes!("../../aws-lc-rs/tests/data/rsa_test_private_key_2048.p8"); + const RSA_PRIVATE_KEY: &[u8] = + include_bytes!("../../aws-lc-rs/tests/data/rsa_test_private_key_2048.der"); + const PUBLIC_KEY: &[u8] = + include_bytes!("../../aws-lc-rs/tests/data/rsa_test_public_key_2048.x509"); + const MESSAGE: &[u8] = b"OpenSSL KAT"; + + let aws_public_key = PublicEncryptingKey::from_der(PUBLIC_KEY).expect("public key"); + let aws_public_key = Pkcs1PublicEncryptingKey::new(aws_public_key).expect("public key"); + + let mut ciphertext = vec![0u8; aws_public_key.ciphertext_size()]; + let ciphertext: &[u8] = aws_public_key + .encrypt(MESSAGE, &mut ciphertext) + .expect("encrypted"); + + assert_ne!(MESSAGE, ciphertext); + + let ossl_private_key = Rsa::private_key_from_der(RSA_PRIVATE_KEY).expect("private key"); + + let mut message = vec![0u8; ossl_private_key.size().try_into().expect("usize cast")]; + let message_len = ossl_private_key + .private_decrypt(ciphertext, &mut message, Padding::PKCS1) + .expect("decrypted"); + let message: &[u8] = &message[0..message_len]; + + assert_eq!(MESSAGE, message); + + let aws_private_key = PrivateDecryptingKey::from_pkcs8(PKCS8_PRIVATE_KEY).expect("private key"); + let aws_private_key = Pkcs1PrivateDecryptingKey::new(aws_private_key).expect("private key"); + let ossl_public_key = Rsa::public_key_from_der(PUBLIC_KEY).expect("public key"); + + let mut ciphertext = vec![0u8; ossl_public_key.size().try_into().expect("usize cast")]; + let ciphertext_len = ossl_public_key + .public_encrypt(MESSAGE, &mut ciphertext, Padding::PKCS1) + .expect("encrypted"); + let ciphertext: &[u8] = &ciphertext[0..ciphertext_len]; + + assert_ne!(MESSAGE, ciphertext); + + let mut plaintext = vec![0u8; aws_private_key.min_output_size()]; + let plaintext: &[u8] = aws_private_key + .decrypt(ciphertext, &mut plaintext) + .expect("decrypted"); + + assert_eq!(MESSAGE, plaintext); +} diff --git a/aws-lc-rs/src/rsa.rs b/aws-lc-rs/src/rsa.rs index 10abd0dca06..586a4e977a1 100644 --- a/aws-lc-rs/src/rsa.rs +++ b/aws-lc-rs/src/rsa.rs @@ -78,6 +78,7 @@ pub use self::{ OaepAlgorithm, OaepPrivateDecryptingKey, OaepPublicEncryptingKey, OAEP_SHA1_MGF1SHA1, OAEP_SHA256_MGF1SHA256, OAEP_SHA384_MGF1SHA384, OAEP_SHA512_MGF1SHA512, }, + pkcs1::{Pkcs1PrivateDecryptingKey, Pkcs1PublicEncryptingKey}, EncryptionAlgorithmId, PrivateDecryptingKey, PublicEncryptingKey, }, key::{KeyPair, KeySize, PublicKey, PublicKeyComponents}, diff --git a/aws-lc-rs/src/rsa/encryption.rs b/aws-lc-rs/src/rsa/encryption.rs index 246f76ffd27..8b071775f19 100644 --- a/aws-lc-rs/src/rsa/encryption.rs +++ b/aws-lc-rs/src/rsa/encryption.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR ISC pub(super) mod oaep; +pub(super) mod pkcs1; use super::{ encoding, diff --git a/aws-lc-rs/src/rsa/encryption/pkcs1.rs b/aws-lc-rs/src/rsa/encryption/pkcs1.rs new file mode 100644 index 00000000000..352b822a865 --- /dev/null +++ b/aws-lc-rs/src/rsa/encryption/pkcs1.rs @@ -0,0 +1,187 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +#![allow(clippy::module_name_repetitions)] + +use super::{PrivateDecryptingKey, PublicEncryptingKey}; +use crate::{error::Unspecified, fips::indicator_check, ptr::LcPtr}; +use aws_lc::{ + EVP_PKEY_CTX_new, EVP_PKEY_CTX_set_rsa_padding, EVP_PKEY_decrypt, EVP_PKEY_decrypt_init, + EVP_PKEY_encrypt, EVP_PKEY_encrypt_init, EVP_PKEY_CTX, RSA_PKCS1_PADDING, +}; +use core::{fmt::Debug, ptr::null_mut}; + +/// RSA PKCS1-v1.5 public key for encryption. +pub struct Pkcs1PublicEncryptingKey { + public_key: PublicEncryptingKey, +} + +impl Pkcs1PublicEncryptingKey { + /// Constructs an `Pkcs1PublicEncryptingKey` from a `PublicEncryptingKey`. + /// # Errors + /// * `Unspecified`: Any error that occurs while attempting to construct an RSA-OAEP public key. + pub fn new(public_key: PublicEncryptingKey) -> Result { + Ok(Self { public_key }) + } + + /// Encrypts the contents in `plaintext` and writes the corresponding ciphertext to `ciphertext`. + /// Returns the subslice of `ciphertext` containing the ciphertext output. + /// + /// # Max Plaintext Length + /// The provided length of `plaintext` must be at most [`Self::max_plaintext_size`]. + /// + /// # Sizing `output` + /// The length of `output` must be greater than or equal to [`Self::ciphertext_size`]. + /// + /// # Errors + /// * `Unspecified` for any error that occurs while encrypting `plaintext`. + pub fn encrypt<'ciphertext>( + &self, + plaintext: &[u8], + ciphertext: &'ciphertext mut [u8], + ) -> Result<&'ciphertext mut [u8], Unspecified> { + let pkey_ctx = LcPtr::new(unsafe { EVP_PKEY_CTX_new(*self.public_key.0, null_mut()) })?; + + if 1 != unsafe { EVP_PKEY_encrypt_init(*pkey_ctx) } { + return Err(Unspecified); + } + + configure_pkcs1_crypto_operation(&pkey_ctx)?; + + let mut out_len = ciphertext.len(); + + if 1 != indicator_check!(unsafe { + EVP_PKEY_encrypt( + *pkey_ctx, + ciphertext.as_mut_ptr(), + &mut out_len, + plaintext.as_ptr(), + plaintext.len(), + ) + }) { + return Err(Unspecified); + }; + + Ok(&mut ciphertext[..out_len]) + } + + /// Returns the RSA key size in bytes. + #[must_use] + pub fn key_size_bytes(&self) -> usize { + self.public_key.key_size_bytes() + } + + /// Returns the RSA key size in bits. + #[must_use] + pub fn key_size_bits(&self) -> usize { + self.public_key.key_size_bits() + } + + /// Returns the max plaintext that could be encrypted using this key. + #[must_use] + pub fn max_plaintext_size(&self) -> usize { + const RSA_PKCS1_PADDING_SIZE: usize = 11; // crypto/fipsmodule/rsa/internal.h + self.key_size_bytes() - RSA_PKCS1_PADDING_SIZE + } + + /// Returns the max ciphertext size that will be output by `Self::encrypt`. + #[must_use] + pub fn ciphertext_size(&self) -> usize { + self.key_size_bytes() + } +} + +impl Debug for Pkcs1PublicEncryptingKey { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Pkcs1PublicEncryptingKey") + .finish_non_exhaustive() + } +} + +/// RSA PKCS1-v1.5 private key for decryption. +pub struct Pkcs1PrivateDecryptingKey { + private_key: PrivateDecryptingKey, +} + +impl Pkcs1PrivateDecryptingKey { + /// Constructs an `Pkcs1PrivateDecryptingKey` from a `PrivateDecryptingKey`. + /// # Errors + /// * `Unspecified`: Any error that occurs while attempting to construct an RSA-OAEP public key. + pub fn new(private_key: PrivateDecryptingKey) -> Result { + Ok(Self { private_key }) + } + + /// Decrypts the contents in `ciphertext` and writes the corresponding plaintext to `plaintext`. + /// Returns the subslice of `plaintext` containing the plaintext output. + /// + /// # Max Ciphertext Length + /// The provided length of `ciphertext` must be [`Self::key_size_bytes`]. + /// + /// # Sizing `output` + /// The length of `output` must be greater than or equal to [`Self::min_output_size`]. + /// + /// # Errors + /// * `Unspecified` for any error that occurs while decrypting `ciphertext`. + pub fn decrypt<'plaintext>( + &self, + ciphertext: &[u8], + plaintext: &'plaintext mut [u8], + ) -> Result<&'plaintext mut [u8], Unspecified> { + let pkey_ctx = LcPtr::new(unsafe { EVP_PKEY_CTX_new(*self.private_key.0, null_mut()) })?; + + if 1 != unsafe { EVP_PKEY_decrypt_init(*pkey_ctx) } { + return Err(Unspecified); + } + + configure_pkcs1_crypto_operation(&pkey_ctx)?; + + let mut out_len = plaintext.len(); + + if 1 != indicator_check!(unsafe { + EVP_PKEY_decrypt( + *pkey_ctx, + plaintext.as_mut_ptr(), + &mut out_len, + ciphertext.as_ptr(), + ciphertext.len(), + ) + }) { + return Err(Unspecified); + }; + + Ok(&mut plaintext[..out_len]) + } + + /// Returns the RSA key size in bytes. + #[must_use] + pub fn key_size_bytes(&self) -> usize { + self.private_key.key_size_bytes() + } + + /// Returns the RSA key size in bits. + #[must_use] + pub fn key_size_bits(&self) -> usize { + self.private_key.key_size_bits() + } + + /// Returns the minimum plaintext buffer size required for `Self::decrypt`. + #[must_use] + pub fn min_output_size(&self) -> usize { + self.key_size_bytes() + } +} + +impl Debug for Pkcs1PrivateDecryptingKey { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Pkcs1PrivateDecryptingKey") + .finish_non_exhaustive() + } +} + +fn configure_pkcs1_crypto_operation(evp_pkey_ctx: &LcPtr) -> Result<(), Unspecified> { + if 1 != unsafe { EVP_PKEY_CTX_set_rsa_padding(**evp_pkey_ctx, RSA_PKCS1_PADDING) } { + return Err(Unspecified); + }; + + Ok(()) +} diff --git a/aws-lc-rs/tests/data/rsa_test_private_key_2048.der b/aws-lc-rs/tests/data/rsa_test_private_key_2048.der new file mode 100644 index 00000000000..2bc6328dc8f Binary files /dev/null and b/aws-lc-rs/tests/data/rsa_test_private_key_2048.der differ diff --git a/aws-lc-rs/tests/rsa_test.rs b/aws-lc-rs/tests/rsa_test.rs index d2fe57e77b5..5550b826476 100644 --- a/aws-lc-rs/tests/rsa_test.rs +++ b/aws-lc-rs/tests/rsa_test.rs @@ -6,8 +6,8 @@ use aws_lc_rs::encoding::{AsDer, Pkcs8V1Der, PublicKeyX509Der}; use aws_lc_rs::rsa::{ EncryptionAlgorithmId, KeySize, OaepPrivateDecryptingKey, OaepPublicEncryptingKey, - PrivateDecryptingKey, PublicEncryptingKey, OAEP_SHA1_MGF1SHA1, OAEP_SHA256_MGF1SHA256, - OAEP_SHA384_MGF1SHA384, OAEP_SHA512_MGF1SHA512, + Pkcs1PrivateDecryptingKey, Pkcs1PublicEncryptingKey, PrivateDecryptingKey, PublicEncryptingKey, + OAEP_SHA1_MGF1SHA1, OAEP_SHA256_MGF1SHA256, OAEP_SHA384_MGF1SHA384, OAEP_SHA512_MGF1SHA512, }; use aws_lc_rs::signature::{ KeyPair, RsaKeyPair, RsaParameters, RsaPublicKeyComponents, RsaSubjectPublicKey, @@ -458,7 +458,7 @@ fn encryption_algorithm_debug() { assert_eq!("OaepSha1Mgf1sha1", format!("{OAEP_SHA1_MGF1SHA1:?}")); } -macro_rules! round_trip_algorithm { +macro_rules! round_trip_oaep_algorithm { ($name:ident, $alg:expr, $keysize:expr) => { #[test] fn $name() { @@ -592,93 +592,93 @@ macro_rules! round_trip_algorithm { }; } -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa2048_oaep_sha1_mgf1sha1, &OAEP_SHA1_MGF1SHA1, KeySize::Rsa2048 ); -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa3072_oaep_sha1_mgf1sha1, &OAEP_SHA1_MGF1SHA1, KeySize::Rsa3072 ); -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa4096_oaep_sha1_mgf1sha1, &OAEP_SHA1_MGF1SHA1, KeySize::Rsa4096 ); // RSA8192 tests are not run in dev (debug) builds because it is too slow. #[cfg(not(debug_assertions))] -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa8192_oaep_sha1_mgf1sha1, &OAEP_SHA1_MGF1SHA1, KeySize::Rsa8192 ); -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa2048_oaep_sha256_mgf1sha256, &OAEP_SHA256_MGF1SHA256, KeySize::Rsa2048 ); -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa3072_oaep_sha256_mgf1sha256, &OAEP_SHA256_MGF1SHA256, KeySize::Rsa3072 ); -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa4096_oaep_sha256_mgf1sha256, &OAEP_SHA256_MGF1SHA256, KeySize::Rsa4096 ); // RSA8192 tests are not run in dev (debug) builds because it is too slow. #[cfg(not(debug_assertions))] -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa8192_oaep_sha256_mgf1sha256, &OAEP_SHA256_MGF1SHA256, KeySize::Rsa8192 ); -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa2048_oaep_sha384_mgf1sha384, &OAEP_SHA384_MGF1SHA384, KeySize::Rsa2048 ); -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa3072_oaep_sha384_mgf1sha384, &OAEP_SHA384_MGF1SHA384, KeySize::Rsa3072 ); -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa4096_oaep_sha384_mgf1sha384, &OAEP_SHA384_MGF1SHA384, KeySize::Rsa4096 ); // RSA8192 tests are not run in dev (debug) builds because it is too slow. #[cfg(not(debug_assertions))] -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa8192_oaep_sha384_mgf1sha384, &OAEP_SHA384_MGF1SHA384, KeySize::Rsa8192 ); -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa2048_oaep_sha512_mgf1sha512, &OAEP_SHA512_MGF1SHA512, KeySize::Rsa2048 ); -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa3072_oaep_sha512_mgf1sha512, &OAEP_SHA512_MGF1SHA512, KeySize::Rsa3072 ); -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa4096_oaep_sha512_mgf1sha512, &OAEP_SHA512_MGF1SHA512, KeySize::Rsa4096 ); // RSA8192 tests are not run in dev (debug) builds because it is too slow. #[cfg(not(debug_assertions))] -round_trip_algorithm!( +round_trip_oaep_algorithm!( rsa8192_oaep_sha512_mgf1sha512, &OAEP_SHA512_MGF1SHA512, KeySize::Rsa8192 @@ -694,18 +694,36 @@ fn encrypting_keypair_debug() { assert_eq!("PublicEncryptingKey", format!("{:?}", &public_key)); - let private_key = OaepPrivateDecryptingKey::new(private_key).expect("oaep private key"); + let oaep_private_key = + OaepPrivateDecryptingKey::new(private_key.clone()).expect("oaep private key"); assert_eq!( "OaepPrivateDecryptingKey { .. }", - format!("{:?}", &private_key) + format!("{:?}", &oaep_private_key) ); - let public_key = OaepPublicEncryptingKey::new(public_key).expect("oaep public key"); + let oaep_public_key = + OaepPublicEncryptingKey::new(public_key.clone()).expect("oaep public key"); assert_eq!( "OaepPublicEncryptingKey { .. }", - format!("{:?}", &public_key) + format!("{:?}", &oaep_public_key) + ); + + let pkcs1_private_key = + Pkcs1PrivateDecryptingKey::new(private_key.clone()).expect("oaep private key"); + + assert_eq!( + "Pkcs1PrivateDecryptingKey { .. }", + format!("{:?}", &pkcs1_private_key) + ); + + let pkcs1_public_key = + Pkcs1PublicEncryptingKey::new(public_key.clone()).expect("oaep public key"); + + assert_eq!( + "Pkcs1PublicEncryptingKey { .. }", + format!("{:?}", &pkcs1_public_key) ); } @@ -786,19 +804,38 @@ fn min_encrypt_key() { let oaep_parsed_public = OaepPublicEncryptingKey::new(parsed_public_key.clone()).expect("supported key"); - let message: &[u8] = br"foo bar baz"; + let message = vec![42u8; oaep_parsed_public.max_plaintext_size(&OAEP_SHA256_MGF1SHA256)]; let mut ciphertext = vec![0u8; oaep_parsed_public.ciphertext_size()]; let ciphertext = oaep_parsed_public - .encrypt(&OAEP_SHA256_MGF1SHA256, message, &mut ciphertext, None) + .encrypt(&OAEP_SHA256_MGF1SHA256, &message, &mut ciphertext, None) .expect("encrypted"); - let mut plaintext = vec![0u8; oaep_parsed_private.key_size_bytes()]; + let mut plaintext = vec![0u8; oaep_parsed_private.min_output_size()]; let plaintext = oaep_parsed_private .decrypt(&OAEP_SHA256_MGF1SHA256, ciphertext, &mut plaintext, None) .expect("decrypted"); assert_eq!(message, plaintext); + + let pkcs1_parsed_private = + Pkcs1PrivateDecryptingKey::new(parsed_private_key.clone()).expect("supported key"); + let pkcs1_parsed_public = + Pkcs1PublicEncryptingKey::new(parsed_public_key.clone()).expect("supported key"); + + let message = vec![42u8; pkcs1_parsed_public.max_plaintext_size()]; + + let mut ciphertext = vec![0u8; pkcs1_parsed_public.ciphertext_size()]; + let ciphertext = pkcs1_parsed_public + .encrypt(&message, &mut ciphertext) + .expect("encrypted"); + + let mut plaintext = vec![0u8; pkcs1_parsed_private.min_output_size()]; + let plaintext = pkcs1_parsed_private + .decrypt(ciphertext, &mut plaintext) + .expect("decrypted"); + + assert_eq!(message, plaintext); } #[test] @@ -823,11 +860,11 @@ fn max_encrypt_key() { let oaep_parsed_public = OaepPublicEncryptingKey::new(parsed_public_key.clone()).expect("supported key"); - let message: &[u8] = br"foo bar baz"; + let message = vec![42u8; oaep_parsed_public.max_plaintext_size(&OAEP_SHA256_MGF1SHA256)]; let mut ciphertext = vec![0u8; oaep_parsed_public.ciphertext_size()]; let ciphertext = oaep_parsed_public - .encrypt(&OAEP_SHA256_MGF1SHA256, message, &mut ciphertext, None) + .encrypt(&OAEP_SHA256_MGF1SHA256, &message, &mut ciphertext, None) .expect("encrypted"); let mut plaintext = vec![0u8; oaep_parsed_private.key_size_bytes()]; @@ -836,6 +873,52 @@ fn max_encrypt_key() { .expect("decrypted"); assert_eq!(message, plaintext); + + let pkcs1_parsed_private = + Pkcs1PrivateDecryptingKey::new(parsed_private_key.clone()).expect("supported key"); + let pkcs1_parsed_public = + Pkcs1PublicEncryptingKey::new(parsed_public_key.clone()).expect("supported key"); + + let message = vec![42u8; pkcs1_parsed_public.max_plaintext_size()]; + + let mut ciphertext = vec![0u8; pkcs1_parsed_public.ciphertext_size()]; + let ciphertext = pkcs1_parsed_public + .encrypt(&message, &mut ciphertext) + .expect("encrypted"); + + let mut plaintext = vec![0u8; pkcs1_parsed_private.min_output_size()]; + let plaintext = pkcs1_parsed_private + .decrypt(ciphertext, &mut plaintext) + .expect("decrypted"); + + assert_eq!(message, plaintext); +} + +#[test] +fn errors_on_larger_than_max_plaintext() { + const PUBLIC_KEY: &[u8] = include_bytes!("data/rsa_test_public_key_2048.x509"); + + let parsed_public_key = PublicEncryptingKey::from_der(PUBLIC_KEY).expect("key supported"); + + let oaep_parsed_public = + OaepPublicEncryptingKey::new(parsed_public_key.clone()).expect("supported key"); + + let message = vec![42u8; oaep_parsed_public.max_plaintext_size(&OAEP_SHA256_MGF1SHA256) + 1]; + + let mut ciphertext = vec![0u8; oaep_parsed_public.ciphertext_size()]; + oaep_parsed_public + .encrypt(&OAEP_SHA256_MGF1SHA256, &message, &mut ciphertext, None) + .expect_err("plaintext too large"); + + let pkcs1_parsed_public = + Pkcs1PublicEncryptingKey::new(parsed_public_key.clone()).expect("supported key"); + + let message = vec![42u8; pkcs1_parsed_public.max_plaintext_size() + 1]; + + let mut ciphertext = vec![0u8; pkcs1_parsed_public.ciphertext_size()]; + pkcs1_parsed_public + .encrypt(&message, &mut ciphertext) + .expect_err("plaintext too large"); } #[test] @@ -843,3 +926,93 @@ fn too_big_encrypt_key() { const PRIVATE_KEY: &[u8] = include_bytes!("data/rsa_test_private_key_16384.p8"); PrivateDecryptingKey::from_pkcs8(PRIVATE_KEY).expect_err("key too big"); } + +macro_rules! round_trip_pkcs1_encryption { + ($name:ident, $keysize:expr) => { + #[test] + fn $name() { + const MESSAGE: &[u8] = b"Hello World"; + + let priv_key = PrivateDecryptingKey::generate($keysize).expect("key generated"); + let pub_key = priv_key.public_key(); + + let priv_key = + Pkcs1PrivateDecryptingKey::new(priv_key).expect("construct PKCS1 private key"); + + let pub_key = + Pkcs1PublicEncryptingKey::new(pub_key).expect("construct PKCS1 private key"); + + let (byte_len, bit_len) = match $keysize { + KeySize::Rsa2048 => (256, 2048), + KeySize::Rsa3072 => (384, 3072), + KeySize::Rsa4096 => (512, 4096), + KeySize::Rsa8192 => (1024, 8192), + _ => panic!("missing KeySize match arm"), + }; + + assert_eq!(priv_key.key_size_bytes(), byte_len); + assert_eq!(pub_key.key_size_bytes(), byte_len); + assert_eq!(priv_key.key_size_bits(), bit_len); + assert_eq!(pub_key.key_size_bits(), bit_len); + + let mut ciphertext = vec![0u8; pub_key.ciphertext_size()]; + + let ciphertext: &[u8] = pub_key + .encrypt(MESSAGE, &mut ciphertext) + .expect("encrypted"); + + let mut plaintext = vec![0u8; priv_key.min_output_size()]; + + let plaintext: &[u8] = priv_key + .decrypt(ciphertext, &mut plaintext) + .expect("decrypt"); + + assert_eq!(MESSAGE, plaintext); + } + }; +} + +round_trip_pkcs1_encryption!(rsa2048_pkcs1_encryption, KeySize::Rsa2048); +round_trip_pkcs1_encryption!(rsa3072_pkcs1_encryption, KeySize::Rsa3072); +round_trip_pkcs1_encryption!(rsa4096_pkcs1_encryption, KeySize::Rsa4096); +#[cfg(not(debug_assertions))] +round_trip_pkcs1_encryption!(rsa8192_pkcs1_encryption, KeySize::Rsa8192); + +// Generated by `echo -n "OpenSSL KAT" | openssl pkeyutl -inkey rsa_test_public_key_2048.x509 -pubin -encrypt -pkeyopt rsa_padding_mode:pkcs1 | xxd -i` +#[test] +fn rsa2048_pkcs1_openssl_kat() { + const PRIVATE_KEY: &[u8] = include_bytes!("data/rsa_test_private_key_2048.p8"); + + const EXPECTED_MESSAGE: &[u8] = b"OpenSSL KAT"; + const CIPHERTEXT: &[u8] = &[ + 0x79, 0xc3, 0xf1, 0x0a, 0x69, 0xc7, 0x0b, 0x19, 0xc1, 0xfd, 0x62, 0xbe, 0x24, 0x85, 0x31, + 0xc1, 0x1d, 0x6c, 0x85, 0x34, 0x03, 0x78, 0x4a, 0x7e, 0xbe, 0xb8, 0xa2, 0xe5, 0xac, 0x79, + 0xaf, 0x1c, 0x6a, 0xff, 0x2f, 0xa5, 0xff, 0xaa, 0x5b, 0xb9, 0x6f, 0xa1, 0xaa, 0x42, 0x72, + 0xa5, 0x87, 0x92, 0x05, 0x97, 0xb4, 0xef, 0x42, 0x02, 0xd3, 0xc4, 0x9f, 0x6e, 0xe3, 0xed, + 0x51, 0xba, 0x52, 0xcf, 0x44, 0x14, 0xf8, 0x47, 0x53, 0x8c, 0xfc, 0x12, 0x0d, 0x53, 0x13, + 0x11, 0x00, 0x7f, 0x87, 0xf7, 0xb5, 0x56, 0xdc, 0xd7, 0xe9, 0xf4, 0xc5, 0xb0, 0x34, 0x85, + 0x10, 0x8a, 0x04, 0xe4, 0x62, 0x38, 0x91, 0xa4, 0xb3, 0x5e, 0x98, 0x15, 0x89, 0x98, 0xf2, + 0xf7, 0x4f, 0xb1, 0x30, 0xa2, 0x09, 0x23, 0x38, 0x43, 0x22, 0x58, 0xec, 0x3c, 0xeb, 0x8d, + 0x62, 0x75, 0x9f, 0xa9, 0x83, 0x0d, 0xe0, 0x43, 0x5a, 0x1c, 0xd5, 0xdb, 0xc6, 0x2c, 0x97, + 0x19, 0xfd, 0xa7, 0xb5, 0x71, 0x1b, 0x87, 0xab, 0x3d, 0xf2, 0x3c, 0x42, 0xc2, 0xea, 0xd8, + 0x57, 0x2a, 0x80, 0xdc, 0xc1, 0x00, 0x66, 0xa5, 0xf0, 0x95, 0x51, 0x56, 0xe8, 0x66, 0x8e, + 0xe9, 0x8e, 0x2a, 0xa6, 0x37, 0x16, 0xeb, 0xbf, 0xe5, 0x12, 0x25, 0x67, 0x0e, 0xc0, 0x3d, + 0x3c, 0x58, 0x15, 0x16, 0x54, 0x15, 0x04, 0xa2, 0xa2, 0x26, 0x46, 0x81, 0x36, 0x64, 0xc0, + 0x7f, 0x6a, 0x04, 0x10, 0x2a, 0x7f, 0x08, 0x6d, 0x4b, 0x23, 0x12, 0x30, 0x9b, 0x0c, 0xb4, + 0xa5, 0x10, 0x80, 0xaa, 0xf0, 0xe3, 0xf3, 0x1e, 0x3b, 0x59, 0x1d, 0x52, 0x68, 0x8e, 0xb9, + 0x9c, 0x89, 0x97, 0x46, 0xfb, 0x06, 0x32, 0xd6, 0xc2, 0x1c, 0x81, 0x8c, 0xa6, 0xf4, 0xa7, + 0xf8, 0xda, 0xb4, 0x4b, 0xd2, 0x49, 0x17, 0xd6, 0x6c, 0x19, 0xe3, 0xa1, 0xbd, 0xe3, 0x5a, + 0x99, + ]; + + let private_key = PrivateDecryptingKey::from_pkcs8(PRIVATE_KEY).expect("private key"); + let private_key = Pkcs1PrivateDecryptingKey::new(private_key).expect("private key"); + + let mut plaintext = vec![0u8; private_key.min_output_size()]; + + let plaintext = private_key + .decrypt(CIPHERTEXT, &mut plaintext) + .expect("decrypt"); + + assert_eq!(EXPECTED_MESSAGE, plaintext); +}