diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f080367179..a40e0a02299 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -622,7 +622,7 @@ jobs: - name: Verify msrv working-directory: ./aws-lc-rs - run: cargo msrv verify + run: cargo msrv --log-level debug --log-target stdout verify minimal-versions: name: Resolve the dependencies to the minimum SemVer version diff --git a/aws-lc-rs/src/ec.rs b/aws-lc-rs/src/ec.rs index 0d08a509d99..e2d463f15b3 100644 --- a/aws-lc-rs/src/ec.rs +++ b/aws-lc-rs/src/ec.rs @@ -369,9 +369,9 @@ pub(crate) unsafe fn evp_pkey_from_public_point( return Err(Unspecified); } - let pkey = LcPtr::new(unsafe { EVP_PKEY_new() })?; + let pkey = LcPtr::new(EVP_PKEY_new())?; - if 1 != unsafe { EVP_PKEY_assign_EC_KEY(*pkey, *ec_key) } { + if 1 != EVP_PKEY_assign_EC_KEY(*pkey, *ec_key) { return Err(Unspecified); } diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index 7804a6cfa83..d21c7b23947 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -158,7 +158,7 @@ impl KeyPair for Ed25519KeyPair { } } -pub(crate) unsafe fn generate_key() -> Result, ()> { +pub(crate) fn generate_key() -> Result, ()> { let pkey_ctx = LcPtr::new(unsafe { EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, null_mut()) })?; if 1 != unsafe { EVP_PKEY_keygen_init(*pkey_ctx) } { @@ -200,7 +200,7 @@ impl Ed25519KeyPair { /// # Errors /// `error::Unspecified` if `rng` cannot provide enough bits or if there's an internal error. pub fn generate_pkcs8(_rng: &dyn SecureRandom) -> Result { - let evp_pkey = unsafe { generate_key()? }; + let evp_pkey = generate_key()?; evp_pkey.marshall_private_key(Version::V2) } @@ -237,7 +237,7 @@ impl Ed25519KeyPair { /// # Errors /// `error::Unspecified` if `rng` cannot provide enough bits or if there's an internal error. pub fn generate_pkcs8v1(_rng: &dyn SecureRandom) -> Result { - let evp_pkey = unsafe { generate_key()? }; + let evp_pkey = generate_key()?; evp_pkey.marshall_private_key(Version::V1) } diff --git a/aws-lc-rs/src/rsa.rs b/aws-lc-rs/src/rsa.rs index 0d4ddf38867..3c290c85918 100644 --- a/aws-lc-rs/src/rsa.rs +++ b/aws-lc-rs/src/rsa.rs @@ -23,7 +23,7 @@ pub use self::{ PublicEncryptingKey, OAEP_SHA1_MGF1SHA1, OAEP_SHA256_MGF1SHA256, OAEP_SHA384_MGF1SHA384, OAEP_SHA512_MGF1SHA512, }, - key::{KeyPair, PublicKey, PublicKeyComponents}, + key::{KeyPair, PublicKey, PublicKeyComponents, SignatureKeySize}, }; pub(crate) use self::signature::RsaVerificationAlgorithmId; diff --git a/aws-lc-rs/src/rsa/encoding.rs b/aws-lc-rs/src/rsa/encoding.rs index 69c0869fadb..fe4d10f09ae 100644 --- a/aws-lc-rs/src/rsa/encoding.rs +++ b/aws-lc-rs/src/rsa/encoding.rs @@ -1,7 +1,7 @@ /// [RFC 8017](https://www.rfc-editor.org/rfc/rfc8017.html) /// /// PKCS #1: RSA Cryptography Specifications Version 2.2 -pub mod rfc8017 { +pub(in super::super) mod rfc8017 { use crate::{ cbs, error::Unspecified, @@ -11,10 +11,12 @@ pub mod rfc8017 { EVP_PKEY_assign_RSA, EVP_PKEY_new, RSA_parse_private_key, RSA_parse_public_key, RSA_public_key_to_bytes, EVP_PKEY, }; - use core::ptr::null_mut; + use std::ptr::null_mut; /// DER encode a RSA public key to `RSAPublicKey` structure. - pub unsafe fn encode_public_key_der(pubkey: &LcPtr) -> Result, ()> { + pub(in super::super) unsafe fn encode_public_key_der( + pubkey: &LcPtr, + ) -> Result, ()> { let mut pubkey_bytes = null_mut::(); let mut outlen: usize = 0; if 1 != RSA_public_key_to_bytes( @@ -32,7 +34,9 @@ pub mod rfc8017 { /// Decode a DER encoded `RSAPublicKey` structure. #[inline] - pub fn decode_public_key_der(public_key: &[u8]) -> Result, Unspecified> { + pub(in super::super) fn decode_public_key_der( + public_key: &[u8], + ) -> Result, Unspecified> { let mut cbs = unsafe { cbs::build_CBS(public_key) }; let rsa = DetachableLcPtr::new(unsafe { RSA_parse_public_key(&mut cbs) })?; @@ -50,7 +54,9 @@ pub mod rfc8017 { /// Decodes a DER encoded `RSAPrivateKey` structure. #[inline] - pub fn decode_private_key_der(private_key: &[u8]) -> Result, Unspecified> { + pub(in super::super) fn decode_private_key_der( + private_key: &[u8], + ) -> Result, Unspecified> { let mut cbs = unsafe { cbs::build_CBS(private_key) }; let rsa = DetachableLcPtr::new(unsafe { RSA_parse_private_key(&mut cbs) })?; @@ -70,11 +76,11 @@ pub mod rfc8017 { /// [RFC 5280](https://www.rfc-editor.org/rfc/rfc5280.html) /// /// Encodings that use the `SubjectPublicKeyInfo` structure. -pub mod rfc5280 { +pub(in super::super) mod rfc5280 { use crate::{cbb::LcCBB, cbs, encoding::RsaPublicKeyX509Der, error::Unspecified, ptr::LcPtr}; use aws_lc::{EVP_marshal_public_key, EVP_parse_public_key, EVP_PKEY}; - pub fn encode_public_key_der( + pub(in super::super) fn encode_public_key_der( key: &LcPtr, ) -> Result, Unspecified> { let mut der = LcCBB::new(1024); @@ -86,22 +92,28 @@ pub mod rfc5280 { der.into_buffer() } - pub fn decode_public_key_der(value: &[u8]) -> Result, Unspecified> { + pub(in super::super) fn decode_public_key_der( + value: &[u8], + ) -> Result, Unspecified> { let mut der = unsafe { cbs::build_CBS(value) }; Ok(LcPtr::new(unsafe { EVP_parse_public_key(&mut der) })?) } } /// PKCS#8 Encoding Functions -pub mod pkcs8 { - use crate::{cbb::LcCBB, error::{Unspecified, KeyRejected}, ptr::LcPtr}; +pub(in super::super) mod pkcs8 { + use crate::{ + cbb::LcCBB, + error::{KeyRejected, Unspecified}, + ptr::LcPtr, + }; use aws_lc::{EVP_marshal_private_key, EVP_PKEY}; // Based on a measurement of a PKCS#8 v1 document containing an RSA-8192 key with an additional 1% capacity buffer // rounded to an even 64-bit words (4678 + 1% + padding ≈ 4728). const PKCS8_FIXED_CAPACITY_BUFFER: usize = 4728; - pub fn encode_v1_der(key: &LcPtr) -> Result, Unspecified> { + pub(in super::super) fn encode_v1_der(key: &LcPtr) -> Result, Unspecified> { let mut buffer = vec![0u8; PKCS8_FIXED_CAPACITY_BUFFER]; let out_len = { let mut cbb = LcCBB::new_fixed(<&mut [u8; PKCS8_FIXED_CAPACITY_BUFFER]>::try_from( @@ -120,7 +132,7 @@ pub mod pkcs8 { } // Supports v1 and v2 encodings through a single API entry-point. - pub fn decode_der(pkcs8: &[u8]) -> Result, KeyRejected> { + pub(in super::super) fn decode_der(pkcs8: &[u8]) -> Result, KeyRejected> { LcPtr::try_from(pkcs8) } } diff --git a/aws-lc-rs/src/rsa/encryption.rs b/aws-lc-rs/src/rsa/encryption.rs index f0e470304ef..72ece1aff12 100644 --- a/aws-lc-rs/src/rsa/encryption.rs +++ b/aws-lc-rs/src/rsa/encryption.rs @@ -7,13 +7,15 @@ use crate::{ fips::indicator_check, ptr::LcPtr, }; +#[cfg(feature = "fips")] +use aws_lc::RSA_check_fips; use aws_lc::{ EVP_PKEY_CTX_new, EVP_PKEY_CTX_set_rsa_mgf1_md, EVP_PKEY_CTX_set_rsa_oaep_md, EVP_PKEY_CTX_set_rsa_padding, EVP_PKEY_decrypt, EVP_PKEY_decrypt_init, EVP_PKEY_encrypt, EVP_PKEY_encrypt_init, EVP_PKEY_up_ref, EVP_sha1, EVP_sha256, EVP_sha384, EVP_sha512, EVP_MD, EVP_PKEY, EVP_PKEY_CTX, RSA_PKCS1_OAEP_PADDING, }; -use core::{fmt::Debug, ptr::null_mut}; +use std::{fmt::Debug, ptr::null_mut}; use super::key::{generate_rsa_key, rsa_key_size_enum, RsaEvpPkey, UsageContext}; @@ -116,7 +118,7 @@ impl EncryptionAlgorithm { rsa_key_size_enum!(EncryptionKeySize); -/// An RSA Private Key used for decrypting ciphertext encrypted by [`PublicEncryptingKey`]. +/// An RSA private key used for decrypting ciphertext encrypted by a [`PublicEncryptingKey`]. pub struct PrivateDecryptingKey(RsaEvpPkey); impl PrivateDecryptingKey { @@ -129,7 +131,22 @@ impl PrivateDecryptingKey { /// # Errors /// * `Unspecified` for any error that occurs during the generation of the RSA keypair. pub fn generate(size: EncryptionKeySize) -> Result { - let key = generate_rsa_key(size.bit_len())?; + let key = generate_rsa_key(size.bit_len(), false)?; + Self::new(key) + } + + /// Generate a RSA `KeyPair` of the specified key-strength. + /// + /// Supports the following key sizes: + /// * `EncryptionKeySize::Rsa2048` + /// * `EncryptionKeySize::Rsa3072` + /// * `EncryptionKeySize::Rsa4096` + /// + /// # Errors + /// * `Unspecified`: Any key generation failure. + #[cfg(feature = "fips")] + pub fn generate_fips(size: EncryptionKeySize) -> Result { + let key = generate_rsa_key(size.bit_len(), true)?; Self::new(key) } @@ -144,6 +161,19 @@ impl PrivateDecryptingKey { Self::new(evp_pkey).map_err(|_| KeyRejected::unexpected_error()) } + /// Returns a boolean indicator if this RSA key is an approved FIPS 140-3 key. + #[cfg(feature = "fips")] + #[must_use] + pub fn is_valid_fips_key(&self) -> bool { + let rsa_key = if let Ok(key) = self.0.key.get_rsa() { + key + } else { + return false; + }; + + 1 == unsafe { RSA_check_fips(*rsa_key) } + } + /// Returns the RSA key size in bytes. #[must_use] pub fn key_size(&self) -> usize { @@ -208,13 +238,19 @@ impl PrivateDecryptingKey { } } +impl Debug for PrivateDecryptingKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("PrivateDecryptingKey").finish() + } +} + impl AsDer> for PrivateDecryptingKey { fn as_der(&self) -> Result, Unspecified> { AsDer::>::as_der(&self.0) } } -/// An RSA Public Key used for decrypting ciphertext encrypted by [`PublicEncryptingKey`]. +/// An RSA public key used for encrypting plaintext that is decrypted by a [`PrivateDecryptingKey`]. pub struct PublicEncryptingKey(RsaEvpPkey); impl PublicEncryptingKey { @@ -279,6 +315,12 @@ impl PublicEncryptingKey { } } +impl Debug for PublicEncryptingKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("PublicEncryptingKey").finish() + } +} + fn configure_oaep_crypto_operation( evp_pkey_ctx: &LcPtr, oaep_hash_fn: OaepHashFn, @@ -353,8 +395,7 @@ mod tests { ($name:ident, $size:expr) => { #[test] fn $name() { - let private_key = - PrivateDecryptingKey::generate($size).expect("generation"); + let private_key = PrivateDecryptingKey::generate($size).expect("generation"); let pkcs8v1 = private_key.as_der().expect("encoded"); @@ -378,6 +419,58 @@ mod tests { generate_encode_decode!(rsa4096_generate_encode_decode, EncryptionKeySize::Rsa4096); generate_encode_decode!(rsa8192_generate_encode_decode, EncryptionKeySize::Rsa8192); + macro_rules! generate_fips_encode_decode { + ($name:ident, $size:expr) => { + #[cfg(feature = "fips")] + #[test] + fn $name() { + let private_key = PrivateDecryptingKey::generate_fips($size).expect("generation"); + + assert_eq!(true, private_key.is_valid_fips_key()); + + let pkcs8v1 = private_key.as_der().expect("encoded"); + + let private_key = + PrivateDecryptingKey::from_pkcs8(pkcs8v1.as_ref()).expect("decoded"); + + let public_key = private_key.public_key().expect("public key"); + + drop(private_key); + + let public_key_der = public_key.as_der().expect("encoded"); + + let _public_key = + PublicEncryptingKey::from_der(public_key_der.as_ref()).expect("decoded"); + } + }; + ($name:ident, $size:expr, false) => { + #[cfg(feature = "fips")] + #[test] + fn $name() { + let _ = PrivateDecryptingKey::generate_fips($size) + .expect_err("should fail for key size"); + } + }; + } + + generate_fips_encode_decode!( + rsa2048_generate_fips_encode_decode, + EncryptionKeySize::Rsa2048 + ); + generate_fips_encode_decode!( + rsa3072_generate_fips_encode_decode, + EncryptionKeySize::Rsa3072 + ); + generate_fips_encode_decode!( + rsa4096_generate_fips_encode_decode, + EncryptionKeySize::Rsa4096 + ); + generate_fips_encode_decode!( + rsa8192_generate_fips_encode_decode, + EncryptionKeySize::Rsa8192, + false + ); + macro_rules! round_trip_algorithm { ($name:ident, $alg:expr, $keysize:expr) => { #[test] diff --git a/aws-lc-rs/src/rsa/key.rs b/aws-lc-rs/src/rsa/key.rs index a474f9ac62b..b0e69494b4d 100644 --- a/aws-lc-rs/src/rsa/key.rs +++ b/aws-lc-rs/src/rsa/key.rs @@ -26,17 +26,17 @@ use crate::{ #[cfg(feature = "fips")] use aws_lc::RSA_check_fips; use aws_lc::{ - EVP_DigestSignInit, EVP_PKEY_CTX_new_id, EVP_PKEY_CTX_set_rsa_keygen_bits, EVP_PKEY_assign_RSA, - EVP_PKEY_bits, EVP_PKEY_id, EVP_PKEY_keygen, EVP_PKEY_keygen_init, EVP_PKEY_new, EVP_PKEY_size, - RSA_get0_d, RSA_new, RSA_set0_key, RSA_size, EVP_PKEY, EVP_PKEY_CTX, EVP_PKEY_RSA, + EVP_DigestSignInit, EVP_PKEY_assign_RSA, EVP_PKEY_bits, EVP_PKEY_id, EVP_PKEY_new, + EVP_PKEY_size, RSA_generate_key_ex, RSA_generate_key_fips, RSA_get0_d, RSA_new, RSA_set0_key, + RSA_size, BIGNUM, EVP_PKEY, EVP_PKEY_CTX, EVP_PKEY_RSA, RSA_F4, }; #[cfg(feature = "ring-io")] use aws_lc::{RSA_get0_e, RSA_get0_n}; -use core::{ +use mirai_annotations::verify_unreachable; +use std::{ fmt::{self, Debug, Formatter}, ptr::null_mut, }; -use mirai_annotations::verify_unreachable; #[cfg(feature = "ring-io")] use untrusted::Input; use zeroize::Zeroize; @@ -62,7 +62,7 @@ macro_rules! rsa_key_size_enum { } impl $name { - /// Returns the size of the key in bytes [`KeySize`]. + /// Returns the size of the key in bytes. #[inline] #[must_use] pub fn len(self) -> usize { @@ -74,7 +74,7 @@ macro_rules! rsa_key_size_enum { } } - /// Returns the bits of this [`KeySize`]. + /// Returns the key size in bits. #[inline] fn bit_len(self) -> i32 { match self { @@ -130,7 +130,22 @@ impl KeyPair { /// # Errors /// * `Unspecified`: Any key generation failure. pub fn generate(size: SignatureKeySize) -> Result { - let private_key = generate_rsa_key(size.bit_len())?; + let private_key = generate_rsa_key(size.bit_len(), false)?; + unsafe { Self::new(private_key).map_err(|_| Unspecified) } + } + + /// Generate a RSA `KeyPair` of the specified key-strength. + /// + /// Supports the following key sizes: + /// * `SignatureKeySize::Rsa2048` + /// * `SignatureKeySize::Rsa3072` + /// * `SignatureKeySize::Rsa4096` + /// + /// # Errors + /// * `Unspecified`: Any key generation failure. + #[cfg(feature = "fips")] + pub fn generate_fips(size: SignatureKeySize) -> Result { + let private_key = generate_rsa_key(size.bit_len(), true)?; unsafe { Self::new(private_key).map_err(|_| Unspecified) } } @@ -144,7 +159,7 @@ impl KeyPair { /// RSA keys. Thus signatures may be generated by keys that are not accepted /// by *ring*. In particular: /// * RSA keys ranging between 2048-bit keys and 8192-bit keys are supported. - /// * The public modulous does not have a required minimum size. + /// * The public exponenet does not have a required minimum size. /// /// # Errors /// `error::KeyRejected` if bytes do not encode an RSA private key or if the key is otherwise @@ -439,24 +454,36 @@ where } } -pub(super) fn generate_rsa_key(size: core::ffi::c_int) -> Result, Unspecified> { - let evp_pkey_ctx = LcPtr::new(unsafe { EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, null_mut()) })?; +pub(super) fn generate_rsa_key( + size: std::os::raw::c_int, + fips: bool, +) -> Result, Unspecified> { + // We explicitly don't use `EVP_PKEY_keygen`, as it will force usage of either the FIPS or non-FIPS + // keygen function based on the whether the build of AWS-LC had FIPS enbaled. Rather we delegate to the desired + // generation function. - if 1 != unsafe { EVP_PKEY_keygen_init(*evp_pkey_ctx) } { - return Err(Unspecified); - }; + let rsa = DetachableLcPtr::new(unsafe { RSA_new() })?; - if 1 != unsafe { EVP_PKEY_CTX_set_rsa_keygen_bits(*evp_pkey_ctx, size) } { + if 1 != if fips { + indicator_check!(unsafe { RSA_generate_key_fips(*rsa, size, null_mut()) }) + } else { + // Safety: RSA_F4 == 65537, RSA_F4 an i32 is safe to cast to u64 + debug_assert_eq!(RSA_F4 as u64, 65537u64); + let e: DetachableLcPtr = (RSA_F4 as u64).try_into()?; + unsafe { RSA_generate_key_ex(*rsa, size, *e, null_mut()) } + } { return Err(Unspecified); - }; + } - let mut pkey: *mut EVP_PKEY = null_mut(); + let evp_pkey = LcPtr::new(unsafe { EVP_PKEY_new() })?; - if 1 != indicator_check!(unsafe { EVP_PKEY_keygen(*evp_pkey_ctx, &mut pkey) }) { + if 1 != unsafe { EVP_PKEY_assign_RSA(*evp_pkey, *rsa) } { return Err(Unspecified); }; - Ok(LcPtr::new(pkey)?) + rsa.detach(); + + Ok(evp_pkey) } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -665,4 +692,49 @@ mod tests { generate_encode_decode!(rsa3072_generate_encode_decode, SignatureKeySize::Rsa3072); generate_encode_decode!(rsa4096_generate_encode_decode, SignatureKeySize::Rsa4096); generate_encode_decode!(rsa8192_generate_encode_decode, SignatureKeySize::Rsa8192); + + macro_rules! generate_fips_encode_decode { + ($name:ident, $size:expr) => { + #[cfg(feature = "fips")] + #[test] + fn $name() { + let private_key = KeyPair::generate_fips($size).expect("generation"); + + assert_eq!(true, private_key.is_valid_fips_key()); + + let pkcs8v1 = private_key.as_der().expect("encoded"); + + let private_key = KeyPair::from_pkcs8(pkcs8v1.as_ref()).expect("decoded"); + + let public_key = crate::signature::KeyPair::public_key(&private_key); + + let _ = public_key.as_ref(); + } + }; + ($name:ident, $size:expr, false) => { + #[cfg(feature = "fips")] + #[test] + fn $name() { + let _ = KeyPair::generate_fips($size).expect_err("should fail for key size"); + } + }; + } + + generate_fips_encode_decode!( + rsa2048_generate_fips_encode_decode, + SignatureKeySize::Rsa2048 + ); + generate_fips_encode_decode!( + rsa3072_generate_fips_encode_decode, + SignatureKeySize::Rsa3072 + ); + generate_fips_encode_decode!( + rsa4096_generate_fips_encode_decode, + SignatureKeySize::Rsa4096 + ); + generate_fips_encode_decode!( + rsa8192_generate_fips_encode_decode, + SignatureKeySize::Rsa8192, + false + ); } diff --git a/aws-lc-rs/src/rsa/signature.rs b/aws-lc-rs/src/rsa/signature.rs index 9e80bb54d61..1ed88ae34f8 100644 --- a/aws-lc-rs/src/rsa/signature.rs +++ b/aws-lc-rs/src/rsa/signature.rs @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC -use core::{ +use std::{ fmt::{self, Debug, Formatter}, mem::MaybeUninit, ops::RangeInclusive,