Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSA OAEP Support #303

Merged
merged 14 commits into from
Apr 10, 2024
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 {
skmcgrail marked this conversation as resolved.
Show resolved Hide resolved
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)?;
andrewhop marked this conversation as resolved.
Show resolved Hide resolved
//!
//! // 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)?;
andrewhop marked this conversation as resolved.
Show resolved Hide resolved
//!
//! // The maximum size plaintext can be determined by calling `OaepPublicEncryptingKey::max_plaintext_size`
//! let message = b"hello world";
skmcgrail marked this conversation as resolved.
Show resolved Hide resolved
//! 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;
skmcgrail marked this conversation as resolved.
Show resolved Hide resolved
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
Loading