Skip to content

Commit

Permalink
RSA OAEP Support (#303)
Browse files Browse the repository at this point in the history
  • Loading branch information
skmcgrail authored Apr 10, 2024
1 parent 564b3c7 commit 34283fa
Show file tree
Hide file tree
Showing 21 changed files with 1,508 additions and 334 deletions.
9 changes: 7 additions & 2 deletions aws-lc-rs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ include ../Makefile

UNAME_S := $(shell uname -s)

AWS_LC_RS_COV_EXTRA_FEATURES := unstable

asan:
# TODO: This build target produces linker error on Mac.
# Run specific tests:
Expand All @@ -21,7 +23,10 @@ asan-fips:
RUST_BACKTRACE=1 ASAN_OPTIONS=detect_leaks=1 RUSTFLAGS=-Zsanitizer=address RUSTDOCFLAGS=-Zsanitizer=address cargo +nightly test --lib --bins --tests --examples --target `rustc -vV | sed -n 's|host: ||p'` --no-default-features --features fips,asan

coverage:
cargo llvm-cov --no-fail-fast --fail-under-lines 95 --ignore-filename-regex "aws-lc-sys/*" --lcov --output-path lcov.info
cargo llvm-cov --features "${AWS_LC_RS_COV_EXTRA_FEATURES}" --no-fail-fast --fail-under-lines 95 --ignore-filename-regex "aws-lc(-fips|)-sys/*" --lcov --output-path lcov.info

coverage-fips:
cargo llvm-cov --features "${AWS_LC_RS_COV_EXTRA_FEATURES},fips" --no-fail-fast --fail-under-lines 95 --ignore-filename-regex "aws-lc(-fips|)-sys/*" --lcov --output-path lcov.info

test:
cargo test --all-targets --features unstable
Expand All @@ -44,4 +49,4 @@ clippy:

ci: format clippy msrv test coverage api-diff-pub

.PHONY: asan asan-fips asan-release ci clippy coverage test msrv clippy
.PHONY: asan asan-fips asan-release ci clippy coverage coverage-fips test msrv clippy
10 changes: 1 addition & 9 deletions aws-lc-rs/src/bn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 OR ISC

use crate::ptr::{ConstPointer, DetachableLcPtr, LcPtr};
use aws_lc::{BN_bin2bn, BN_bn2bin, BN_cmp, BN_new, BN_num_bits, BN_num_bytes, BN_set_u64, BIGNUM};
use core::cmp::Ordering;
use aws_lc::{BN_bin2bn, BN_bn2bin, BN_new, BN_num_bits, BN_num_bytes, BN_set_u64, BIGNUM};
use core::ptr::null_mut;
use mirai_annotations::unrecoverable;

Expand Down Expand Up @@ -65,13 +64,6 @@ impl ConstPointer<BIGNUM> {
}
}

pub(crate) fn compare(&self, other: &ConstPointer<BIGNUM>) -> Ordering {
unsafe {
let result = BN_cmp(**self, **other);
result.cmp(&0)
}
}

pub(crate) fn num_bits(&self) -> u32 {
unsafe { BN_num_bits(**self) }
}
Expand Down
6 changes: 6 additions & 0 deletions aws-lc-rs/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ macro_rules! generated_encodings {
f.debug_struct(stringify!($name)).finish()
}
}

impl<'a> From<Buffer<'a, buffer_type::[<$name Type>]>> for $name<'a> {
fn from(value: Buffer<'a, buffer_type::[<$name Type>]>) -> Self {
Self(value)
}
}
)*
}}
}
Expand Down
31 changes: 10 additions & 21 deletions aws-lc-rs/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,24 +128,11 @@ impl KeyRejected {
KeyRejected("InconsistentComponents")
}

//pub(crate) fn invalid_component() -> Self {
// KeyRejected("InvalidComponent")
//}

#[inline]
pub(crate) fn invalid_encoding() -> Self {
KeyRejected("InvalidEncoding")
}

// XXX: See the comment at the call site.
//pub(crate) fn rng_failed() -> Self {
// KeyRejected("RNG failed")
//}

//pub(crate) fn public_key_is_missing() -> Self {
// KeyRejected("PublicKeyIsMissing")
//}

pub(crate) fn too_small() -> Self {
KeyRejected("TooSmall")
}
Expand All @@ -154,21 +141,17 @@ impl KeyRejected {
KeyRejected("TooLarge")
}

//pub(crate) fn version_not_supported() -> Self {
// KeyRejected("VersionNotSupported")
//}

pub(crate) fn wrong_algorithm() -> Self {
KeyRejected("WrongAlgorithm")
}

pub(crate) fn private_modulus_len_not_multiple_of_512_bits() -> Self {
KeyRejected("PrivateModulusLenNotMultipleOf512Bits")
}

pub(crate) fn unexpected_error() -> Self {
KeyRejected("UnexpectedError")
}

pub(crate) fn unspecified() -> Self {
KeyRejected("Unspecified")
}
}

impl Error for KeyRejected {
Expand Down Expand Up @@ -239,6 +222,12 @@ impl From<TryFromIntError> for KeyRejected {
}
}

impl From<Unspecified> for KeyRejected {
fn from(_: Unspecified) -> Self {
Self::unspecified()
}
}

#[allow(deprecated, unused_imports)]
#[cfg(test)]
mod tests {
Expand Down
16 changes: 14 additions & 2 deletions aws-lc-rs/src/evp_pkey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ use crate::error::{KeyRejected, Unspecified};
use crate::pkcs8::{Document, Version};
use crate::ptr::LcPtr;
use aws_lc::{
EVP_PKEY_bits, EVP_PKEY_get1_EC_KEY, EVP_PKEY_get1_RSA, EVP_PKEY_id, EVP_marshal_private_key,
EVP_marshal_private_key_v2, EVP_parse_private_key, EC_KEY, EVP_PKEY, RSA,
EVP_PKEY_bits, EVP_PKEY_get1_EC_KEY, EVP_PKEY_get1_RSA, EVP_PKEY_id, EVP_PKEY_up_ref,
EVP_marshal_private_key, EVP_marshal_private_key_v2, EVP_parse_private_key, EC_KEY, EVP_PKEY,
RSA,
};
// TODO: Uncomment when MSRV >= 1.64
// use core::ffi::c_int;
Expand Down Expand Up @@ -112,3 +113,14 @@ impl LcPtr<EVP_PKEY> {
Ok(Document::new(buffer.into_boxed_slice()))
}
}

impl Clone for LcPtr<EVP_PKEY> {
fn clone(&self) -> Self {
assert_eq!(
1,
unsafe { EVP_PKEY_up_ref(**self) },
"infallible AWS-LC function"
);
Self::new(**self).expect("non-null AWS-LC EVP_PKEY pointer")
}
}
17 changes: 16 additions & 1 deletion aws-lc-rs/src/fips.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ pub(crate) fn set_fips_service_status_unapproved() {
indicator::set_unapproved();
}

#[allow(dead_code)]
#[cfg(all(feature = "fips", debug_assertions))]
#[inline]
pub(crate) fn clear_fips_service_status() {
indicator::clear();
}

#[cfg(all(feature = "fips", debug_assertions))]
pub(crate) mod indicator {
use core::cell::Cell;
Expand All @@ -47,6 +54,10 @@ pub(crate) mod indicator {
pub fn set_unapproved() {
STATUS_INDICATOR.with(|v| v.set(Some(false)));
}

pub fn clear() {
STATUS_INDICATOR.with(|v| v.set(None));
}
}

#[cfg(all(feature = "fips", debug_assertions))]
Expand Down Expand Up @@ -125,8 +136,12 @@ pub(crate) use indicator_check;
#[cfg(all(feature = "fips", debug_assertions))]
macro_rules! check_fips_service_status {
($function:expr) => {{
use $crate::fips::get_fips_service_status;
// Clear the current indicator status first by retrieving it
use $crate::fips::{clear_fips_service_status, get_fips_service_status};
clear_fips_service_status();
// do the expression
let result = $function;
// Check indicator after expression
get_fips_service_status().map(|()| result)
}};
}
Expand Down
21 changes: 1 addition & 20 deletions aws-lc-rs/src/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,21 +101,6 @@ impl<P: Pointer> DetachablePointer<P> {
}
}

impl<P: Pointer> DetachablePointer<P> {
#[inline]
pub fn as_const(&self) -> ConstPointer<P::T> {
match &self.pointer {
Some(pointer) => ConstPointer {
ptr: pointer.as_const_ptr(),
},
None => {
// Safety: pointer is only None when DetachableLcPtr is detached or dropped
verify_unreachable!()
}
}
}
}

impl<P: Pointer> From<DetachablePointer<P>> for ManagedPointer<P> {
#[inline]
fn from(mut dptr: DetachablePointer<P>) -> Self {
Expand Down Expand Up @@ -225,7 +210,7 @@ create_pointer!(EVP_AEAD_CTX, EVP_AEAD_CTX_free);

#[cfg(test)]
mod tests {
use crate::ptr::{ConstPointer, DetachablePointer, ManagedPointer};
use crate::ptr::{DetachablePointer, ManagedPointer};
use aws_lc::BIGNUM;

#[test]
Expand All @@ -236,10 +221,6 @@ mod tests {
let debug = format!("{detachable_ptr:?}");
assert!(debug.contains("DetachablePointer { pointer: Some("));

let const_ptr: ConstPointer<BIGNUM> = detachable_ptr.as_const();
let debug = format!("{const_ptr:?}");
assert!(debug.contains("ConstPointer { ptr:"));

let lc_ptr = ManagedPointer::new(detachable_ptr.detach()).unwrap();
let debug = format!("{lc_ptr:?}");
assert!(debug.contains("ManagedPointer { pointer:"));
Expand Down
68 changes: 67 additions & 1 deletion aws-lc-rs/src/rsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,90 @@
// SPDX-License-Identifier: Apache-2.0 OR ISC

//! RSA Signature and Encryption Support.
//!
//! # OAEP Encryption / Decryption
//!
//! ```rust
//! # use std::error::Error;
//! # fn main() -> Result<(), Box<dyn Error>> {
//! use aws_lc_rs::{
//! encoding::{AsDer, Pkcs8V1Der, PublicKeyX509Der},
//! rsa::{KeySize, OAEP_SHA256_MGF1SHA256, OaepPublicEncryptingKey, OaepPrivateDecryptingKey, PublicEncryptingKey, PrivateDecryptingKey}
//! };
//!
//! // Generate a RSA 2048-bit key.
//! let private_key = PrivateDecryptingKey::generate(KeySize::Rsa2048)?;
//!
//! // Serialize the RSA private key to DER encoded PKCS#8 format for later usage.
//! let private_key_der = AsDer::<Pkcs8V1Der>::as_der(&private_key)?;
//! let private_key_der_bytes = private_key_der.as_ref();
//!
//! // Load a RSA private key from DER encoded PKCS#8 document.
//! let private_key = PrivateDecryptingKey::from_pkcs8(private_key_der_bytes)?;
//!
//! // Retrieve the RSA public key
//! let public_key = private_key.public_key();
//!
//! // Serialize the RSA public key to DER encoded X.509 SubjectPublicKeyInfo for later usage.
//! let public_key_der = AsDer::<PublicKeyX509Der>::as_der(&public_key)?;
//! let public_key_der_bytes = public_key_der.as_ref();
//!
//! // Load a RSA public key from DER encoded X.509 SubjectPublicKeyInfo.
//! let public_key = PublicEncryptingKey::from_der(public_key_der_bytes)?;
//!
//! // Construct a RSA-OAEP public encrypting key
//! let public_key = OaepPublicEncryptingKey::new(public_key)?;
//!
//! // The maximum size plaintext can be determined by calling `OaepPublicEncryptingKey::max_plaintext_size`
//! let message = b"hello world";
//! let mut ciphertext = vec![0u8; public_key.ciphertext_size()]; // Output will be the size of the RSA key length in bytes rounded up.
//!
//! // Encrypt a message with the public key without the optional label provided.
//! let ciphertext = public_key.encrypt(&OAEP_SHA256_MGF1SHA256, message, &mut ciphertext, None)?;
//!
//! assert_ne!(message, ciphertext);
//!
//! // Construct a RSA-OAEP private decrypting key
//! let private_key = OaepPrivateDecryptingKey::new(private_key)?;
//!
//! // Decrypt a message with the private key.
//! let mut plaintext = vec![0u8; private_key.min_output_size()];
//! let plaintext = private_key.decrypt(&OAEP_SHA256_MGF1SHA256, ciphertext, &mut plaintext, None)?;
//!
//! assert_eq!(message, plaintext);
//!
//! # Ok(())
//! # }
//! ```

// *R* and *r* in Montgomery math refer to different things, so we always use
// `R` to refer to *R* to avoid confusion, even when that's against the normal
// naming conventions. Also the standard camelCase names are used for `KeyPair`
// components.

mod encoding;
mod encryption;
pub(crate) mod key;
pub(crate) mod signature;

pub use self::key::{KeyPair, KeySize, PublicKey, PublicKeyComponents};
#[allow(clippy::module_name_repetitions)]
pub use self::signature::RsaParameters;
pub use self::{
encryption::{
EncryptionAlgorithmId, OaepAlgorithm, OaepPrivateDecryptingKey, OaepPublicEncryptingKey,
PrivateDecryptingKey, PublicEncryptingKey, OAEP_SHA1_MGF1SHA1, OAEP_SHA256_MGF1SHA256,
OAEP_SHA384_MGF1SHA384, OAEP_SHA512_MGF1SHA512,
},
key::{KeyPair, KeySize, PublicKey, PublicKeyComponents},
};

pub(crate) use self::signature::RsaVerificationAlgorithmId;

#[cfg(test)]
mod tests {
#[cfg(feature = "fips")]
mod fips;

#[cfg(feature = "ring-io")]
#[test]
fn test_rsa() {
Expand Down
Loading

0 comments on commit 34283fa

Please sign in to comment.