From a979692662e47e5f050f4006e54fb0dba4f61011 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Wed, 28 Aug 2019 13:09:39 -0700 Subject: [PATCH] aead: Add `Payload` type This implements a `Payload` type suggested by @newpavlov here: https://github.com/RustCrypto/traits/pull/40#issuecomment-522697851 It uses an `impl From>` for all plaintexts and ciphertexts, with the idea that users of the API who are unconcerned with AAD can just pass the plaintext/ciphertext message they intend to encrypt or decrypt. With a `plaintext: &[u8]`: ```rust let ciphertext = cipher.encrypt(nonce, plaintext); let plaintext = cipher.encrypt(nonce, ciphertext).unwrap(); ``` Or if you do want to pass AAD: ```rust let ciphertext = cipher.encrypt(nonce, Payload { msg: plaintext, aad }); let plaintext = cipher.decrypt(nonce, Payload { msg: ciphertext, aad }).unwrap(); ``` This makes `msg` and `aad` explicit when used so users don't accidentally pass their intended plaintexts as `aad` and accidentally expose them because AAD is unencrypted. The `Into>` impl on `&[u8]` automatically uses `b""` (i.e. empty byte slice) as the AAD when it's coerced, which is what I think 99% of users will want. As someone who's concerned about AAD for protocols like Noise (and also other use cases like binding digital signatur keys to ciphertexts), I think for the overwheliming majority of users it's a confusing, superfluous detail. I think this approach neatly leverages Rust's polymorphism to hide this detail in cases where it doesn't matter. --- aead/src/lib.rs | 95 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 23 deletions(-) diff --git a/aead/src/lib.rs b/aead/src/lib.rs index 856819b6b..5713cc041 100644 --- a/aead/src/lib.rs +++ b/aead/src/lib.rs @@ -7,7 +7,7 @@ extern crate alloc; pub use generic_array; use alloc::vec::Vec; -use generic_array::{GenericArray, ArrayLength, typenum::Unsigned}; +use generic_array::{typenum::Unsigned, ArrayLength, GenericArray}; #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Error; @@ -33,22 +33,46 @@ pub trait Aead { /// ciphertext vs. a plaintext. type CiphertextOverhead: ArrayLength + Unsigned; - /// Encrypt the given plaintext slice, and return the resulting ciphertext - /// as a vector of bytes. - fn encrypt( + /// Encrypt the given plaintext payload, and return the resulting + /// ciphertext as a vector of bytes. + /// + /// The `Payload` type can be used to provide Additional Associated Data + /// (AAD) along with the message: this is an optional bytestring which is + /// not encrypted, but *is* authenticated along with the message. Failure + /// to pass the same AAD that was used during encryption will cause + /// decryption to fail, which is useful if you would like to "bind" the + /// ciphertext to some other identifier, like a digital signature key + /// or other identifier. + /// + /// If you don't care about AAD and just want to encrypt a plaintext + /// message, `&[u8]` will automatically be coerced into a `Payload`: + /// + /// ```nobuild + /// let plaintext = b"Top secret message, handle with care"; + /// let ciphertext = cipher.encrypt(nonce, plaintext); + /// ``` + fn encrypt<'msg, 'aad>( &mut self, - additional_data: &[u8], nonce: &GenericArray, - plaintext: &[u8] + plaintext: impl Into>, ) -> Result, Error>; /// Decrypt the given ciphertext slice, and return the resulting plaintext /// as a vector of bytes. - fn decrypt( + /// + /// See notes on `Aead::encrypt()` about allowable message payloads and + /// Associated Additional Data (AAD). + /// + /// If you have no AAD, you can call this as follows: + /// + /// ```nobuild + /// let ciphertext = b"..."; + /// let plaintext = cipher.decrypt(nonce, ciphertext)?; + /// ``` + fn decrypt<'msg, 'aad>( &mut self, - additional_data: &[u8], nonce: &GenericArray, - ciphertext: &[u8] + ciphertext: impl Into>, ) -> Result, Error>; } @@ -65,20 +89,24 @@ pub trait StatelessAead { /// Encrypt the given plaintext slice, and return the resulting ciphertext /// as a vector of bytes. - fn encrypt( + /// + /// See notes on `Aead::encrypt()` about allowable message payloads and + /// Associated Additional Data (AAD). + fn encrypt<'msg, 'aad>( &self, - additional_data: &[u8], nonce: &GenericArray, - plaintext: &[u8] + plaintext: impl Into>, ) -> Result, Error>; /// Decrypt the given ciphertext slice, and return the resulting plaintext /// as a vector of bytes. - fn decrypt( + /// + /// See notes on `Aead::encrypt()` and `Aead::decrypt()` about allowable + /// message payloads and Associated Additional Data (AAD). + fn decrypt<'msg, 'aad>( &self, - additional_data: &[u8], nonce: &GenericArray, - ciphertext: &[u8] + ciphertext: impl Into>, ) -> Result, Error>; } @@ -91,23 +119,44 @@ impl Aead for Algo { /// Encrypt the given plaintext slice, and return the resulting ciphertext /// as a vector of bytes. - fn encrypt( + fn encrypt<'msg, 'aad>( &mut self, - additional_data: &[u8], nonce: &GenericArray, - plaintext: &[u8] + plaintext: impl Into>, ) -> Result, Error> { - ::encrypt(self, additional_data, nonce, plaintext) + ::encrypt(self, nonce, plaintext) } /// Decrypt the given ciphertext slice, and return the resulting plaintext /// as a vector of bytes. - fn decrypt( + fn decrypt<'msg, 'aad>( &mut self, - additional_data: &[u8], nonce: &GenericArray, - ciphertext: &[u8] + ciphertext: impl Into>, ) -> Result, Error> { - ::decrypt(self, additional_data, nonce, ciphertext) + ::decrypt(self, nonce, ciphertext) + } +} + +/// AEAD payloads are a combination of a message (plaintext or ciphertext) +/// and "additional associated data" (AAD) to be authenticated (in cleartext) +/// along with the message. +/// +/// If you don't care about AAD, you can pass a `&[u8]` as the payload to +/// `encrypt`/`decrypt` and it will automatically be coerced to this type. +pub struct Payload<'msg, 'aad> { + /// Message to be encrypted/decrypted + pub msg: &'msg [u8], + + /// Optional "additional associated data" to authenticate along with + /// this message. If AAD is provided at the time the message is encrypted, + /// the same AAD *MUST* be provided at the time the message is decrypted, + /// or decryption will fail. + pub aad: &'aad [u8], +} + +impl<'msg, 'aad> From<&'msg [u8]> for Payload<'msg, 'aad> { + fn from(msg: &'msg [u8]) -> Self { + Self { msg, aad: b"" } } }