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 bbb7d8f
Show file tree
Hide file tree
Showing 8 changed files with 220 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions aws-lc-rs/src/ec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
6 changes: 3 additions & 3 deletions aws-lc-rs/src/ed25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ impl KeyPair for Ed25519KeyPair {
}
}

pub(crate) unsafe fn generate_key() -> Result<LcPtr<EVP_PKEY>, ()> {
pub(crate) fn generate_key() -> Result<LcPtr<EVP_PKEY>, ()> {
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) } {
Expand Down Expand Up @@ -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<Document, Unspecified> {
let evp_pkey = unsafe { generate_key()? };
let evp_pkey = generate_key()?;
evp_pkey.marshall_private_key(Version::V2)
}

Expand Down Expand Up @@ -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<Document, Unspecified> {
let evp_pkey = unsafe { generate_key()? };
let evp_pkey = generate_key()?;
evp_pkey.marshall_private_key(Version::V1)
}

Expand Down
2 changes: 1 addition & 1 deletion aws-lc-rs/src/rsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
36 changes: 24 additions & 12 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(in super::super) mod rfc8017 {
use crate::{
cbs,
error::Unspecified,
Expand All @@ -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<EVP_PKEY>) -> Result<Box<[u8]>, ()> {
pub(in super::super) unsafe fn encode_public_key_der(
pubkey: &LcPtr<EVP_PKEY>,
) -> Result<Box<[u8]>, ()> {
let mut pubkey_bytes = null_mut::<u8>();
let mut outlen: usize = 0;
if 1 != RSA_public_key_to_bytes(
Expand All @@ -32,7 +34,9 @@ pub mod rfc8017 {

/// Decode a DER encoded `RSAPublicKey` structure.
#[inline]
pub fn decode_public_key_der(public_key: &[u8]) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
pub(in super::super) fn decode_public_key_der(
public_key: &[u8],
) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
let mut cbs = unsafe { cbs::build_CBS(public_key) };

let rsa = DetachableLcPtr::new(unsafe { RSA_parse_public_key(&mut cbs) })?;
Expand All @@ -50,7 +54,9 @@ pub mod rfc8017 {

/// Decodes a DER encoded `RSAPrivateKey` structure.
#[inline]
pub fn decode_private_key_der(private_key: &[u8]) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
pub(in super::super) fn decode_private_key_der(
private_key: &[u8],
) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
let mut cbs = unsafe { cbs::build_CBS(private_key) };

let rsa = DetachableLcPtr::new(unsafe { RSA_parse_private_key(&mut cbs) })?;
Expand All @@ -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<EVP_PKEY>,
) -> Result<RsaPublicKeyX509Der<'static>, Unspecified> {
let mut der = LcCBB::new(1024);
Expand All @@ -86,22 +92,28 @@ pub mod rfc5280 {
der.into_buffer()
}

pub fn decode_public_key_der(value: &[u8]) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
pub(in super::super) fn decode_public_key_der(
value: &[u8],
) -> Result<LcPtr<EVP_PKEY>, 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<EVP_PKEY>) -> Result<Vec<u8>, Unspecified> {
pub(in super::super) fn encode_v1_der(key: &LcPtr<EVP_PKEY>) -> Result<Vec<u8>, 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(
Expand All @@ -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<LcPtr<EVP_PKEY>, KeyRejected> {
pub(in super::super) fn decode_der(pkcs8: &[u8]) -> Result<LcPtr<EVP_PKEY>, KeyRejected> {
LcPtr::try_from(pkcs8)
}
}
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,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};

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 std::fmt::Formatter<'_>) -> std::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 std::fmt::Formatter<'_>) -> std::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
Loading

0 comments on commit bbb7d8f

Please sign in to comment.