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

Add ability to prepare the next operation and it's associated nonce for OpeningKey and SealingKey #619

Merged
merged 5 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Address feedback
  • Loading branch information
skmcgrail committed Dec 4, 2024
commit 5c5652415cf451c793f76cd1c8a618cf1383cb71
157 changes: 109 additions & 48 deletions aws-lc-rs/src/aead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,72 @@
//!
//! assert_eq!(plaintext, decrypted_plaintext);
//! ```
//!
//! ## Prepared Nonce API's with Nonce Sequence
//!
//! If you prefer to use the [NonceSequence] based API's, and need to know the [Nonce] explicit nonce used for a
//! cryptographic key operation operation, then [SealingKeyPreparedNonce] and
//! [OpeningKeyPreparedNonce] are available to you.
//!
//! ```rust
//! # use std::error::Error;
//! #
//! # fn main() -> Result<(), Box<dyn Error>> {
//! use aws_lc_rs::aead::{
//! nonce_sequence::Counter32Builder, Aad, BoundKey, OpeningKey, SealingKey, UnboundKey,
//! AES_128_GCM,
//! };
//! use std::vec::Vec;
//!
//! let key_bytes = &[
//! 0xa5, 0xf3, 0x8d, 0x0d, 0x2d, 0x7c, 0x48, 0x56, 0xe7, 0xf3, 0xc3, 0x63, 0x0d, 0x40, 0x5b,
//! 0x9e,
//! ];
//!
//! // Create AES-128-GCM SealingKey
//! let mut sealing_key = SealingKey::new(
//! UnboundKey::new(&AES_128_GCM, key_bytes)?,
//! Counter32Builder::new()
//! .identifier([0, 1, 2, 3, 4, 5, 6, 7])
//! .build(),
//! );
//!
//! // Create AES-128-GCM OpeningKey
//! let mut opening_key = OpeningKey::new(
//! UnboundKey::new(&AES_128_GCM, key_bytes)?,
//! Counter32Builder::new()
//! .identifier([0, 1, 2, 3, 4, 5, 6, 7])
//! .build(),
//! );
//!
//! let message = "test message";
//! let mut in_out = Vec::from(message);
//!
//! // Create a SealingKeyPreparedNonce which consumes a nonce from the underlying sequence
//! let seal_prepared_nonce = sealing_key.prepare_nonce()?;
//!
//! // Query the nonce that will be used for our seal operation with our prepared nonce
//! let seal_nonce_bytes = Vec::from(seal_prepared_nonce.nonce().as_ref());
//!
//! // Use the prepared nonce and seal the plaintext
//! seal_prepared_nonce.seal_in_place_append_tag(Aad::empty(), &mut in_out)?;
//!
//! // Create a OpeningKeyPreparedNonce which consumes a nonce from the underlying sequence
//! let open_prepared_nonce = opening_key.prepare_nonce()?;
//!
//! // Query the nonce that will be used for our seal operation with our prepared nonce
//! let open_nonce_bytes = Vec::from(open_prepared_nonce.nonce().as_ref());
//!
//! // Since we initialized the Counter32Builder the same between both builders the nonce here
//! // will match the one from the opening key.
//! assert_eq!(seal_nonce_bytes.as_slice(), open_nonce_bytes.as_slice());
//!
//! let plaintext = open_prepared_nonce.open_in_place(Aad::empty(), &mut in_out)?;
//!
//! assert_eq!(message.as_bytes(), plaintext);
//! # Ok(())
//! # }
//! ```

use crate::{derive_debug_via_id, error::Unspecified, hkdf};
use aead_ctx::AeadCtx;
Expand Down Expand Up @@ -215,7 +281,6 @@ impl<N: NonceSequence> OpeningKey<N> {
/// plaintext without the tag.
///
/// Prefer [`RandomizedNonceKey::open_in_place`].
///
// # FIPS
// Use this method with one of the following algorithms:
// * `AES_128_GCM`
Expand Down Expand Up @@ -281,7 +346,6 @@ impl<N: NonceSequence> OpeningKey<N> {
/// This reassembly be accomplished with three calls to `open_within()`.
///
/// Prefer [`RandomizedNonceKey::open_in_place`].
///
// # FIPS
// Use this method with one of the following algorithms:
// * `AES_128_GCM`
Expand Down Expand Up @@ -309,11 +373,19 @@ impl<N: NonceSequence> OpeningKey<N> {
)
}

/// Returns a `OpeningKeyOpMut` with the next computed `Nonce` from the `NonceSequence` for the next operation.
/// Returns a `OpeningKeyPreparedNonce` containing the next computed `Nonce` consumed from `NonceSequence`.
///
/// The encapsulated Nonce will be used **if and only if** either
/// [OpeningKeyPreparedNonce::open_in_place] or [OpeningKeyPreparedNonce::open_within]
/// are invoked. Dropping `OpeningKeyPreparedNonce` without invoking either method results in the nonce remaining
/// consumed and unused within the associated `NonceSequence`. Subsequent calls to [OpeningKey] methods will
/// always use a proceeding nonce from the `NonceSequence` regardless of whether
/// a `OpeningKeyPreparedNonce` is consumed or not.
///
/// # Errors
/// `Unspecified` if there is a failure computing the nonce for the next operation.
pub fn prepare_operation(&mut self) -> Result<OpeningKeyOpMut<'_, N>, Unspecified> {
OpeningKeyOpMut::new(self)
/// `Unspecified` if there is a failure computing the nonce for the next operation, i.e. `NonceSequence` exhausted.
pub fn prepare_nonce(&mut self) -> Result<OpeningKeyPreparedNonce<'_, N>, Unspecified> {
OpeningKeyPreparedNonce::new(self)
}
}

Expand Down Expand Up @@ -355,7 +427,6 @@ impl<N: NonceSequence> SealingKey<N> {
/// Deprecated. Renamed to `seal_in_place_append_tag`.
///
/// Prefer [`RandomizedNonceKey::seal_in_place_append_tag`].
///
// # FIPS
// This method must not be used.
//
Expand Down Expand Up @@ -386,7 +457,6 @@ impl<N: NonceSequence> SealingKey<N> {
/// ```
///
/// Prefer [`RandomizedNonceKey::seal_in_place_append_tag`].
///
// # FIPS
// This method must not be used.
//
Expand Down Expand Up @@ -421,7 +491,6 @@ impl<N: NonceSequence> SealingKey<N> {
/// The tag will be `self.algorithm.tag_len()` bytes long.
///
/// Prefer [`RandomizedNonceKey::seal_in_place_separate_tag`].
///
// # FIPS
// This method must not be used.
//
Expand All @@ -442,24 +511,32 @@ impl<N: NonceSequence> SealingKey<N> {
.map(|(_, tag)| tag)
}

/// Returns a `SealingKeyOpMut` with the next computed `Nonce` from the `NonceSequence` for the next operation.
/// Returns a `SealingKeyPreparedNonce` containing the next computed `Nonce` consumed from `NonceSequence`.
///
/// The encapsulated Nonce will be used **if and only if** either
/// [SealingKeyPreparedNonce::seal_in_place_append_tag] or [SealingKeyPreparedNonce::seal_in_place_separate_tag]
/// are invoked. Dropping `SealingKeyPreparedNonce` without invoking either method results in the nonce remaining
/// consumed and unused within the associated `NonceSequence`. Subsequent calls to [SealingKey] methods will
/// always use a proceeding nonce from the `NonceSequence` regardless of whether
/// a `SealingKeyPreparedNonce` is consumed or not.
///
/// # Errors
/// `Unspecified` if there is a failure computing the nonce for the next operation.
pub fn prepare_operation(&mut self) -> Result<SealingKeyOpMut<'_, N>, Unspecified> {
SealingKeyOpMut::new(self)
/// `Unspecified` if there is a failure computing the nonce for the next operation, i.e. `NonceSequence` exhausted.
pub fn prepare_nonce(&mut self) -> Result<SealingKeyPreparedNonce<'_, N>, Unspecified> {
SealingKeyPreparedNonce::new(self)
}
}

macro_rules! nonce_seq_key_op_mut {
($name:ident) => {
paste! {
/// A key operation with a precomputed nonce from a key's associated `NonceSequence`.
pub struct [<$name OpMut>]<'a, N: NonceSequence> {
pub struct [<$name PreparedNonce>]<'a, N: NonceSequence> {
key: &'a mut $name<N>,
nonce: Nonce,
}

impl<'a, N: NonceSequence> [<$name OpMut>]<'a, N> {
impl<'a, N: NonceSequence> [<$name PreparedNonce>]<'a, N> {
fn new(key: &'a mut $name<N>) -> Result<Self, Unspecified> {
let nonce = key.nonce_sequence.advance()?;
Ok(Self {
Expand All @@ -469,9 +546,17 @@ macro_rules! nonce_seq_key_op_mut {
}
}

impl<'a, N: NonceSequence> Debug for [<$name OpMut>]<'a, N> {
impl<N: NonceSequence> [<$name PreparedNonce>]<'_, N> {
/// Returns the prepared Nonce that is used for key methods invoked on [Self].
#[must_use]
pub fn nonce(&self) -> &Nonce {
&self.nonce
}
}

impl<N: NonceSequence> Debug for [<$name PreparedNonce>]<'_, N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
f.debug_struct(stringify!([<$name OpMut>])).finish_non_exhaustive()
f.debug_struct(stringify!([<$name PreparedNonce>])).finish_non_exhaustive()
}
}
}
Expand All @@ -481,15 +566,7 @@ macro_rules! nonce_seq_key_op_mut {
nonce_seq_key_op_mut!(OpeningKey);
nonce_seq_key_op_mut!(SealingKey);

impl<N: NonceSequence> OpeningKeyOpMut<'_, N> {
/// Returns the Nonce that will be used for this operation.
#[must_use]
pub fn nonce(&self) -> Nonce {
let nonce_bytes = self.nonce.0.as_ref();
let nonce: Nonce = Nonce(nonce_bytes.into());
nonce
}

impl<N: NonceSequence> OpeningKeyPreparedNonce<'_, N> {
/// Authenticates and decrypts (“opens”) data in place.
///
/// See [OpeningKey::open_in_place] for additional API information.
Expand Down Expand Up @@ -530,15 +607,7 @@ impl<N: NonceSequence> OpeningKeyOpMut<'_, N> {
}
}

impl<N: NonceSequence> SealingKeyOpMut<'_, N> {
/// Returns the Nonce that will be used for this operation.
#[must_use]
pub fn nonce(&self) -> Nonce {
let nonce_bytes = self.nonce.0.as_ref();
let nonce: Nonce = Nonce(nonce_bytes.into());
nonce
}

impl<N: NonceSequence> SealingKeyPreparedNonce<'_, N> {
/// Encrypts and signs (“seals”) data in place, appending the tag to the
/// resulting ciphertext.
///
Expand Down Expand Up @@ -629,7 +698,6 @@ impl hkdf::KeyType for &'static Algorithm {
/// `NonceSequence` cannot reasonably be used.
///
/// Prefer [`RandomizedNonceKey`] when practical.
///
// # FIPS
// The following conditions must be met:
// * `UnboundKey`'s algorithm is one of:
Expand All @@ -652,7 +720,6 @@ impl LessSafeKey {
/// `nonce` must be unique for every use of the key to open data.
///
/// Prefer [`RandomizedNonceKey::open_in_place`].
///
// # FIPS
// Use this method with one of the following algorithms:
// * `AES_128_GCM`
Expand All @@ -678,7 +745,6 @@ impl LessSafeKey {
/// `nonce` must be unique for every use of the key to open data.
///
/// Prefer [`RandomizedNonceKey::open_in_place`].
///
// # FIPS
// Use this method with one of the following algorithms:
// * `AES_128_GCM`
Expand Down Expand Up @@ -714,7 +780,6 @@ impl LessSafeKey {
/// # Errors
/// `error::Unspecified` when ciphertext is invalid. In this case, `out_plaintext` may
/// have been overwritten in an unspecified way.
///
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn open_separate_gather<A>(
Expand All @@ -735,7 +800,6 @@ impl LessSafeKey {
/// Deprecated. Renamed to `seal_in_place_append_tag()`.
///
/// Prefer [`RandomizedNonceKey::seal_in_place_append_tag`].
///
// # FIPS
// This method must not be used.
//
Expand All @@ -761,7 +825,6 @@ impl LessSafeKey {
/// `nonce` must be unique for every use of the key to seal data.
///
/// Prefer [`RandomizedNonceKey::seal_in_place_append_tag`].
///
// # FIPS
// This method must not be used.
//
Expand Down Expand Up @@ -790,7 +853,6 @@ impl LessSafeKey {
/// `nonce` must be unique for every use of the key to seal data.
///
/// Prefer [`RandomizedNonceKey::seal_in_place_separate_tag`].
///
// # FIPS
// This method must not be used.
//
Expand Down Expand Up @@ -825,7 +887,6 @@ impl LessSafeKey {
/// The `extra_out_and_tag` length must be equal to the `extra_len` and `self.algorithm.tag_len()`.
///
/// `nonce` must be unique for every use of the key to seal data.
///
// # FIPS
// This method must not be used.
//
Expand Down Expand Up @@ -1014,7 +1075,7 @@ mod tests {
}

#[test]
fn debug_key_op_mut() {
fn debug_prepared_nonce() {
let mut sk = SealingKey::new(
UnboundKey::new(&AES_128_GCM, &[0u8; 16]).unwrap(),
Counter32Builder::new().build(),
Expand All @@ -1023,10 +1084,10 @@ mod tests {
UnboundKey::new(&AES_128_GCM, &[0u8; 16]).unwrap(),
Counter32Builder::new().build(),
);
let so = sk.prepare_operation().unwrap();
let oo = ok.prepare_operation().unwrap();
assert_eq!("SealingKeyOpMut { .. }", format!("{so:?}"));
assert_eq!("OpeningKeyOpMut { .. }", format!("{oo:?}"));
let so = sk.prepare_nonce().unwrap();
let oo = ok.prepare_nonce().unwrap();
assert_eq!("SealingKeyPreparedNonce { .. }", format!("{so:?}"));
assert_eq!("OpeningKeyPreparedNonce { .. }", format!("{oo:?}"));
}

#[test]
Expand Down
22 changes: 11 additions & 11 deletions aws-lc-rs/tests/aead_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,7 @@ impl aead::NonceSequence for OneNonceSequence {
}

#[test]
fn prepare_operation() {
fn prepare_nonce() {
const KEY: &[u8] = &[
0x52, 0x05, 0x19, 0x7a, 0xcc, 0x88, 0xdb, 0x78, 0x39, 0x59, 0xbc, 0x03, 0xb8, 0x1d, 0x4a,
0x6c,
Expand All @@ -688,10 +688,10 @@ fn prepare_operation() {
let mut nonces: Vec<Vec<u8>> = vec![];

for _ in 0..(LIMIT / 2) {
let so = sk.prepare_operation().unwrap();
let oo = ok.prepare_operation().unwrap();
let so_nonce = Vec::from(so.nonce().as_ref());
let oo_nonce = Vec::from(oo.nonce().as_ref());
let skpn = sk.prepare_nonce().unwrap();
let okpn = ok.prepare_nonce().unwrap();
let so_nonce = Vec::from(skpn.nonce().as_ref());
let oo_nonce = Vec::from(okpn.nonce().as_ref());

assert_eq!(so_nonce.as_slice(), oo_nonce.as_slice());
assert!(!nonces.contains(&so_nonce));
Expand All @@ -701,15 +701,15 @@ fn prepare_operation() {
let mut message: Vec<u8> = vec![];
message.extend_from_slice(MESSAGE);

so.seal_in_place_append_tag(Aad::empty(), &mut message)
skpn.seal_in_place_append_tag(Aad::empty(), &mut message)
.unwrap();
assert_ne!(MESSAGE, message.as_slice());

let message = oo.open_in_place(Aad::empty(), &mut message).unwrap();
let message = okpn.open_in_place(Aad::empty(), &mut message).unwrap();
assert_eq!(MESSAGE, message);

let so = sk.prepare_operation().unwrap();
let oo = ok.prepare_operation().unwrap();
let so = sk.prepare_nonce().unwrap();
let oo = ok.prepare_nonce().unwrap();
let so_nonce = Vec::from(so.nonce().as_ref());
let oo_nonce = Vec::from(oo.nonce().as_ref());

Expand Down Expand Up @@ -741,8 +741,8 @@ fn prepare_operation() {
message.extend_from_slice(MESSAGE);

// Subsequent usage should fail now since the sequence is exhausted in each key.
sk.prepare_operation().expect_err("sequence limit reached");
ok.prepare_operation().expect_err("sequence limit reached");
sk.prepare_nonce().expect_err("sequence limit reached");
ok.prepare_nonce().expect_err("sequence limit reached");
sk.seal_in_place_append_tag(Aad::empty(), &mut message)
.expect_err("sequence limit reached");
sk.seal_in_place_separate_tag(Aad::empty(), &mut message)
Expand Down
Loading