Skip to content

Commit

Permalink
Key generation changes for FIPS
Browse files Browse the repository at this point in the history
  • Loading branch information
skmcgrail committed Jan 12, 2024
1 parent 889b012 commit b014eeb
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 23 deletions.
12 changes: 8 additions & 4 deletions aws-lc-rs/src/rsa/encoding.rs
Original file line number Diff line number Diff line change
@@ -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(super) mod rfc8017 {
use crate::{
cbs,
error::Unspecified,
Expand Down Expand Up @@ -70,7 +70,7 @@ pub mod rfc8017 {
/// [RFC 5280](https://www.rfc-editor.org/rfc/rfc5280.html)
///
/// Encodings that use the `SubjectPublicKeyInfo` structure.
pub mod rfc5280 {
pub(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};

Expand All @@ -93,8 +93,12 @@ pub mod rfc5280 {
}

/// PKCS#8 Encoding Functions
pub mod pkcs8 {
use crate::{cbb::LcCBB, error::{Unspecified, KeyRejected}, ptr::LcPtr};
pub(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
Expand Down
101 changes: 97 additions & 4 deletions aws-lc-rs/src/rsa/encryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ 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,
Expand Down Expand Up @@ -90,7 +92,7 @@ impl OaepAlgorithm {
}

impl Debug for OaepAlgorithm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Debug::fmt(&self.id, f)
}
}
Expand Down Expand Up @@ -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<Self, Unspecified> {
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<Self, Unspecified> {
let key = generate_rsa_key(size.bit_len(), true)?;
Self::new(key)
}

Expand All @@ -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 {
Expand Down Expand Up @@ -208,6 +238,12 @@ impl PrivateDecryptingKey {
}
}

impl Debug for PrivateDecryptingKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("PrivateDecryptingKey").finish()
}
}

impl AsDer<Pkcs8V1Der<'static>> for PrivateDecryptingKey {
fn as_der(&self) -> Result<Pkcs8V1Der<'static>, Unspecified> {
AsDer::<Pkcs8V1Der<'_>>::as_der(&self.0)
Expand Down Expand Up @@ -279,6 +315,12 @@ impl PublicEncryptingKey {
}
}

impl Debug for PublicEncryptingKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("PublicEncryptingKey").finish()
}
}

fn configure_oaep_crypto_operation(
evp_pkey_ctx: &LcPtr<EVP_PKEY_CTX>,
oaep_hash_fn: OaepHashFn,
Expand Down Expand Up @@ -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");

Expand All @@ -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]
Expand Down
102 changes: 87 additions & 15 deletions aws-lc-rs/src/rsa/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ 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};
Expand Down Expand Up @@ -130,7 +130,22 @@ impl KeyPair {
/// # Errors
/// * `Unspecified`: Any key generation failure.
pub fn generate(size: SignatureKeySize) -> Result<Self, Unspecified> {
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<Self, Unspecified> {
let private_key = generate_rsa_key(size.bit_len(), true)?;
unsafe { Self::new(private_key).map_err(|_| Unspecified) }
}

Expand All @@ -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
Expand Down Expand Up @@ -439,24 +454,36 @@ where
}
}

pub(super) fn generate_rsa_key(size: core::ffi::c_int) -> Result<LcPtr<EVP_PKEY>, 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: core::ffi::c_int,
fips: bool,
) -> Result<LcPtr<EVP_PKEY>, 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<BIGNUM> = (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)]
Expand Down Expand Up @@ -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
);
}

0 comments on commit b014eeb

Please sign in to comment.