diff --git a/QUALITY_AND_STYLE.md b/QUALITY_AND_STYLE.md index 2937ec7..9b9f131 100644 --- a/QUALITY_AND_STYLE.md +++ b/QUALITY_AND_STYLE.md @@ -28,9 +28,7 @@ Where the behaviour of a function is critical to test but cannot be tested from private function, in-line tests in the source file should be used. All traits in `bouncycastle-core` must have corresponding tests in `bouncycastle-core-test-framework` that exercise all -behaviours and error conditions that are comment to all implementations of that trait. - -All crypto algorithms must have tests against the bc-test-data repo and against wycheproof. +behaviours and error conditions that are comment to all implementations of that trait.All crypto algorithms must have tests against the bc-test-data repo and against wycheproof. ## Performance Benchmarks @@ -57,6 +55,17 @@ yourself whether this function would take 6-months-from-now-you more than 10 min there comments you could add that would help future you get back up to speed faster about what this code is doing and which parts were done for a very specific reason and should not be changed on a whim. +## Naming Conventions + +All normal rust naming convensions from clippy apply. In addition, some library-specific naming conventions: + +* In constants, "LEN" is the length of a value in bytes (typically used for sizing arrays), whereas "SIZE" is a value in + bits (typically used as a security parameter). For example SHA256 could have constants `HASH_SIZE = 256` and + `HASH_LEN = 32`. +* Functions that are part of a stateful streaming api should be named `do_*()`. +* We use "pk" for public key and "sk" for secret key / private key. (some other libraries use "pub" and "priv", but " + pub" is a keyword in rust, and "pubkey / privkey" is verbose :P ) + ## APIs Where possible, primitives should expose "one-shot APIs" that simply take data and return a result as a static member diff --git a/crypto/core-test-framework/src/lib.rs b/crypto/core-test-framework/src/lib.rs index 92f7215..97532cd 100644 --- a/crypto/core-test-framework/src/lib.rs +++ b/crypto/core-test-framework/src/lib.rs @@ -14,6 +14,7 @@ pub mod kdf; pub mod kem; pub mod mac; pub mod signature; +pub mod symmetric_ciphers; pub const DUMMY_SEED_512: &[u8; 512] = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; diff --git a/crypto/core-test-framework/src/symmetric_ciphers.rs b/crypto/core-test-framework/src/symmetric_ciphers.rs new file mode 100644 index 0000000..0023f8e --- /dev/null +++ b/crypto/core-test-framework/src/symmetric_ciphers.rs @@ -0,0 +1,361 @@ +use crate::{DUMMY_SEED_512, DUMMY_SEED_1024}; +use bouncycastle_core::errors::SymmetricCipherError; +use bouncycastle_core::key_material::{KeyMaterial, KeyMaterialTrait, KeyType}; +use bouncycastle_core::traits::{ + AEADCipher, BlockCipher, SecurityStrength, StreamCipher, SymmetricCipher, +}; + +pub struct TestFrameworkSymmetricCipher { + // Put any config options here +} + +impl TestFrameworkSymmetricCipher { + pub fn new() -> Self { + Self {} + } + + /// Test all the members of trait Hash against the given input-output pair. + /// This gives good baseline test coverage, but is not exhaustive. + pub fn test< + const KEY_LEN: usize, + const INIT_DATA_LEN: usize, + C: SymmetricCipher, + >( + &self, + ) { + let msg = b"The quick brown fox jumps over the lazy dog"; + + let key = KeyMaterial::::from_bytes_as_type( + &DUMMY_SEED_512[..KEY_LEN], + KeyType::SymmetricCipherKey, + ) + .unwrap(); + + // one-shot API + let mut ct = [0u8; 1024]; + let (iv, ct_bytes_written) = C::encrypt_out(&key, msg, &mut ct).unwrap(); + assert_ne!(ct_bytes_written, 0); + + let mut pt = [0u8; 1024]; + let pt_bytes_written = C::decrypt_out(&key, iv, &ct[..ct_bytes_written], &mut pt).unwrap(); + assert_ne!(pt_bytes_written, 0); + assert_eq!(msg, &pt[..pt_bytes_written]); + + // todo -- add tests for encrypt() / decrypt() wrapped in a #[cfg(std)] + + // messing with the ciphertext does not give back the same plaintext (or failing to decrypt is also ok) + ct[17] ^= 0xFF; + match C::decrypt_out(&key, iv, &ct[..ct_bytes_written], &mut pt) { + Ok(bytes_written) => { + // so it decrypted something, but it had better not match the original plaintext + assert_eq!(bytes_written, pt_bytes_written); + assert_ne!(&pt[..bytes_written], msg); + } + Err(SymmetricCipherError::DecryptionFailed) => { /* also ok */ } + _ => panic!("Unexpected error"), + }; + + // error case: KeyMaterial of wrong type + let mac_key = + KeyMaterial::::from_bytes_as_type(&DUMMY_SEED_512[..KEY_LEN], KeyType::MACKey) + .unwrap(); + match C::encrypt_out(&mac_key, msg, &mut ct) { + Err(SymmetricCipherError::KeyMaterialError(_)) => { /* good */ } + _ => panic!("Unexpected error"), + }; + + // error case: security strengths too weak and too strong + let mut key = KeyMaterial::::from_bytes_as_type( + &DUMMY_SEED_512[..KEY_LEN], + KeyType::SymmetricCipherKey, + ) + .unwrap(); + key.allow_hazardous_operations(); + + let security_strengths = [ + SecurityStrength::None, + SecurityStrength::_112bit, + SecurityStrength::_128bit, + SecurityStrength::_192bit, + SecurityStrength::_256bit, + ]; + for ss in security_strengths.iter() { + key.set_security_strength(ss.clone()).unwrap(); + + match C::encrypt_out(&key, msg, &mut ct) { + Ok(_) => { + if ss >= &C::MAX_SECURITY_STRENGTH { /* good */ + } else { + panic!("Should have been a strong enough key"); + } + } + Err(SymmetricCipherError::KeyMaterialError(_)) => { + if ss < &C::MAX_SECURITY_STRENGTH { /* good */ + } else { + panic!("Should not have accepted a key weaker than algorithm"); + } + } + _ => panic!("Unexpected error"), + }; + } + } +} + +pub struct TestFrameworkBlockCipher { + // Put any config options here +} + +impl TestFrameworkBlockCipher { + pub fn new() -> Self { + Self {} + } + + pub fn test< + const KEY_LEN: usize, + const INIT_DATA_LEN: usize, + const BLOCK_LEN: usize, + C: BlockCipher, + >( + &self, + ) { + let key = KeyMaterial::::from_bytes_as_type( + &DUMMY_SEED_512[..KEY_LEN], + KeyType::SymmetricCipherKey, + ) + .unwrap(); + + // to test blocks, we'll chunk our dummy seed + let (mut encryptor, iv) = C::do_encrypt_init(&key).unwrap(); + let mut decryptor = C::do_decrypt_init(&key, &iv).unwrap(); + + for msg_chunk in DUMMY_SEED_512.as_chunks::().0.iter() { + let ct = encryptor.do_encrypt_block(msg_chunk).unwrap(); + let pt = decryptor.do_decrypt_block(&ct).unwrap(); + assert_eq!(msg_chunk, &pt); + } + + // do it again using the _out versions + + let (mut encryptor, iv) = C::do_encrypt_init(&key).unwrap(); + let mut decryptor = C::do_decrypt_init(&key, &iv).unwrap(); + + let mut ct = [0u8; BLOCK_LEN]; + let mut pt = [0u8; BLOCK_LEN]; + for msg_chunk in DUMMY_SEED_1024.as_chunks::().0.iter() { + let ct_bytes_written = encryptor.do_encrypt_block_out(msg_chunk, &mut ct).unwrap(); + assert_eq!(ct_bytes_written, BLOCK_LEN); + + let pt_bytes_written = decryptor.do_decrypt_block_out(&ct, &mut pt).unwrap(); + assert_eq!(pt_bytes_written, BLOCK_LEN); + + assert_eq!(msg_chunk, &pt); + } + + // test that the iv is random (ie not the same on two runs) + let (_encryptor, iv1) = C::do_encrypt_init(&key).unwrap(); + let (_encryptor, iv2) = C::do_encrypt_init(&key).unwrap(); + assert_ne!(iv1, iv2); + + // error case: KeyMaterial of wrong type + let mac_key = + KeyMaterial::::from_bytes_as_type(&DUMMY_SEED_512[..KEY_LEN], KeyType::MACKey) + .unwrap(); + match C::do_encrypt_init(&mac_key) { + Err(SymmetricCipherError::KeyMaterialError(_)) => { /* good */ } + _ => panic!("Unexpected error"), + }; + + // error case: security strengths too weak and too strong + let mut key = KeyMaterial::::from_bytes_as_type( + &DUMMY_SEED_512[..KEY_LEN], + KeyType::SymmetricCipherKey, + ) + .unwrap(); + key.allow_hazardous_operations(); + + let security_strengths = [ + SecurityStrength::None, + SecurityStrength::_112bit, + SecurityStrength::_128bit, + SecurityStrength::_192bit, + SecurityStrength::_256bit, + ]; + for ss in security_strengths.iter() { + key.set_security_strength(ss.clone()).unwrap(); + + match C::do_encrypt_init(&key) { + Ok(_) => { + if ss >= &C::MAX_SECURITY_STRENGTH { /* good */ + } else { + panic!("Should have been a strong enough key"); + } + } + Err(SymmetricCipherError::KeyMaterialError(_)) => { + if ss < &C::MAX_SECURITY_STRENGTH { /* good */ + } else { + panic!("Should not have accepted a key weaker than algorithm"); + } + } + _ => panic!("Unexpected error"), + }; + } + } +} + +pub struct TestFrameworkAEADCipher { + // Put any config options here +} + +impl TestFrameworkAEADCipher { + pub fn new() -> Self { + Self {} + } + + /// Test all the members of trait Hash against the given input-output pair. + /// This gives good baseline test coverage, but is not exhaustive. + pub fn test< + const KEY_LEN: usize, + const NONCE_LEN: usize, + const TAG_LEN: usize, + C: AEADCipher, + >( + &self, + ) { + let msg = b"The quick brown fox jumps over the lazy dog"; + let aad = b"some associated data"; + + let key = KeyMaterial::::from_bytes_as_type( + &DUMMY_SEED_512[..KEY_LEN], + KeyType::SymmetricCipherKey, + ) + .unwrap(); + + // one-shot API + let mut ct = [0u8; 1024]; + let (nonce, ct_bytes_written, tag) = C::aead_encrypt_out(&key, aad, msg, &mut ct).unwrap(); + if nonce.len() != 0 { + assert_ne!(nonce, [0u8; NONCE_LEN]); + } + assert_ne!(ct_bytes_written, 0); + assert_ne!(tag, [0u8; TAG_LEN]); + + let mut pt = [0u8; 1024]; + let pt_bytes_written = + C::aead_decrypt_out(&key, &nonce, aad, &ct[..ct_bytes_written], &tag, &mut pt).unwrap(); + assert_ne!(pt_bytes_written, 0); + assert_eq!(msg, &pt[..pt_bytes_written]); + + // todo -- add tests for aead_encrypt() / aead_decrypt() wrapped in a #[cfg(std)] + + // messing with the ciphertext does not give back the same plaintext (or failing to decrypt is also ok) + ct[17] ^= 0xFF; + match C::aead_decrypt_out(&key, &nonce, aad, &ct[..ct_bytes_written], &tag, &mut pt) { + Ok(bytes_written) => { + // so it decrypted something, but it had better not match the original plaintext + assert_eq!(bytes_written, pt_bytes_written); + assert_ne!(&pt[..bytes_written], msg); + } + Err(SymmetricCipherError::DecryptionFailed) => { /* also ok */ } + _ => panic!("Unexpected error"), + }; + + // messing with the aad causes the aead_decrypt to fail + match C::aead_decrypt_out( + &key, + &nonce, + b"not the right associated data", + &ct[..ct_bytes_written], + &tag, + &mut pt, + ) { + Err(SymmetricCipherError::AEADTagCheckFailed) => { /* good */ } + _ => panic!("Expected TagCheckFailed error"), + }; + + // messing with the tag causes the aead_decrypt to fail + match C::aead_decrypt_out( + &key, + &nonce, + aad, + &ct[..ct_bytes_written], + &[3u8; TAG_LEN], + &mut pt, + ) { + Err(SymmetricCipherError::AEADTagCheckFailed) => { /* good */ } + _ => panic!("Expected TagCheckFailed error"), + }; + + // multiple invocations give different nonces + let (nonce1, _ct_bytes_written, _tag) = + C::aead_encrypt_out(&key, aad, msg, &mut ct).unwrap(); + let (nonce2, _ct_bytes_written, _tag) = + C::aead_encrypt_out(&key, aad, msg, &mut ct).unwrap(); + assert_ne!(nonce1, nonce2); + + // error case: KeyMaterial of wrong type + let mac_key = + KeyMaterial::::from_bytes_as_type(&DUMMY_SEED_512[..KEY_LEN], KeyType::MACKey) + .unwrap(); + match C::encrypt_out(&mac_key, msg, &mut ct) { + Err(SymmetricCipherError::KeyMaterialError(_)) => { /* good */ } + _ => panic!("Unexpected error"), + }; + + // error case: security strengths too weak and too strong + let mut key = KeyMaterial::::from_bytes_as_type( + &DUMMY_SEED_512[..KEY_LEN], + KeyType::SymmetricCipherKey, + ) + .unwrap(); + key.allow_hazardous_operations(); + + let security_strengths = [ + SecurityStrength::None, + SecurityStrength::_112bit, + SecurityStrength::_128bit, + SecurityStrength::_192bit, + SecurityStrength::_256bit, + ]; + for ss in security_strengths.iter() { + key.set_security_strength(ss.clone()).unwrap(); + + match C::encrypt_out(&mac_key, msg, &mut ct) { + Ok(_) => { + if ss >= &C::MAX_SECURITY_STRENGTH { /* good */ + } else { + panic!("Should have been a strong enough key"); + } + } + Err(SymmetricCipherError::KeyMaterialError(_)) => { + if ss < &C::MAX_SECURITY_STRENGTH { /* good */ + } else { + panic!("Should not have accepted a key weaker than algorithm"); + } + } + _ => panic!("Unexpected error"), + }; + } + } +} + +pub struct TestFrameworkStreamCipher { + // Put any config options here +} + +impl TestFrameworkStreamCipher { + pub fn new() -> Self { + Self {} + } + + /// Test all the members of trait Hash against the given input-output pair. + /// This gives good baseline test coverage, but is not exhaustive. + pub fn test< + const KEY_LEN: usize, + const INIT_DATA_LEN: usize, + C: StreamCipher, + >( + &self, + ) { + todo!() + } +} diff --git a/crypto/core/src/errors.rs b/crypto/core/src/errors.rs index b2eaf0c..f57b5e5 100644 --- a/crypto/core/src/errors.rs +++ b/crypto/core/src/errors.rs @@ -80,7 +80,32 @@ pub enum SignatureError { RNGError(RNGError), } +#[derive(Debug)] +pub enum SymmetricCipherError { + GenericError(&'static str), + AEADTagCheckFailed, + DecryptionFailed, + /// Indicates that the output buffer is not large enough to hold the requested output. + /// The usize represents the required buffer length. + IncorrectOutputBufferLength(&'static str, usize), + KeyMaterialError(KeyMaterialError), + RNGError(RNGError), + StateError(&'static str), +} + /*** Promotion functions ***/ +impl From for SymmetricCipherError { + fn from(e: KeyMaterialError) -> SymmetricCipherError { + Self::KeyMaterialError(e) + } +} + +impl From for SymmetricCipherError { + fn from(e: RNGError) -> SymmetricCipherError { + Self::RNGError(e) + } +} + impl From for HashError { fn from(e: KeyMaterialError) -> HashError { Self::KeyMaterialError(e) diff --git a/crypto/core/src/traits.rs b/crypto/core/src/traits.rs index 089e282..ae660ff 100644 --- a/crypto/core/src/traits.rs +++ b/crypto/core/src/traits.rs @@ -1,6 +1,8 @@ //! Provides simplified abstracted APIs over classes of cryptigraphic primitives, such as Hash, KDF, etc. -use crate::errors::{HashError, KDFError, KEMError, MACError, RNGError, SignatureError}; +use crate::errors::{ + HashError, KDFError, KEMError, MACError, RNGError, SignatureError, SymmetricCipherError, +}; use crate::key_material::KeyMaterialTrait; use core::fmt::{Debug, Display}; use core::marker::Sized; @@ -17,6 +19,230 @@ pub trait Algorithm { const MAX_SECURITY_STRENGTH: SecurityStrength; } +// todo -- split all the SymmetricCipher traits into Encryptor and Decryptor +/// The basic one-shot encrypt and decrypt that all types of symmetric ciphers must implement. +/// These are meant to be simple, easy to use, secure, and fool-proof APIs, but they may result in +/// ciphertexts that are incompatible with other implementations as ciphers in more complex modes, such +/// as AEADs or stream ciphers may need to stick extra data either at the beginning or end of the ciphertext. +/// See the documentation of the underlying implementation for more details. +pub trait SymmetricCipher: + Algorithm + Secret +{ + #[cfg(std)] + /// A one-shot API to encrypt some plaintext with the given key. + /// This function returns the ciphertext as a Vec, and therefore is only available when compiling with std. + /// Returns a tuple containing the initialization data and the ciphertext. + /// This is not available if building for no_std. + fn encrypt( + key: &KeyMaterial, + plaintext: &[u8], + ) -> Result<([u8; INIT_DATA_LEN], Vec), SymmetricCipherError>; + /// A one-shot API to encrypt some plaintext with the given key. + /// This function takes a reference to the output buffer for the ciphertext, and is therefore available in no_std. + /// See the documentation for the underlying implementation for details on providing a ciphertext buffer of sufficient size; + /// typically the ciphertext is the same length as the plaintext, but some ciphers may have an expansion factor or require + /// extra space for a nonce or tag. + /// Returns a tuple containing the initialization data and the number of bytes written to the ciphertext buffer. + fn encrypt_out( + key: &KeyMaterial, + plaintext: &[u8], + ciphertext: &mut [u8], + ) -> Result<([u8; INIT_DATA_LEN], usize), SymmetricCipherError>; + #[cfg(std)] + /// A one-shot API to decrypt some ciphertext with the given key. + /// This function returns the ciphertext as a Vec, and therefore is only available when compiling with std. + /// This is not available if building for no_std. + fn decrypt( + key: &KeyMaterial, + init_data: [u8; INIT_DATA_LEN], + ciphertext: &[u8], + ) -> Result, SymmetricCipherError>; + /// A one-shot API to decrypt some ciphertext with the given key. + /// This function takes a reference to the output buffer for the plaintext, and is therefore available in no_std. + /// See the documentation for the underlying implementation for details on providing a plaintext buffer of sufficient size; + /// typically the ciphertext is the same length as the plaintext, but some ciphers may have an expansion factor or require + /// extra space for a nonce or tag. + /// Returns a tuple containing the initialization data and the number of bytes written to the plaintext buffer. + fn decrypt_out( + key: &KeyMaterial, + init_data: [u8; INIT_DATA_LEN], + ciphertext: &[u8], + plaintext: &mut [u8], + ) -> Result; +} + +/// The basic functions of a block cipher. +/// This trait allows for a block cipher to generate initialization data, such as an Initialization Vector (IV) or Counter (CTR) +/// which is not technically part of the ciphertext, but must be transmitted along with the ciphertext in order for the +/// recipient to perform successful decryption. The length of the initialization data is specified by the implementing struct +/// via the `INIT_DATA_SIZE` constant. +/// In order for these one-shot APIs to be usable securely in all contexts, the init data will be generated +/// securely by the block cipher implementation and returned along with the ciphertext, and there is no API for the +/// user to provide the init data. If you require this functionality, see the documentation for the underlying implementation. +pub trait BlockCipher: + SymmetricCipher + Sized +{ + /// Constructor that begins a flow of the streaming API for encrypting one block at a time. + /// Allows for the implementation to return init data such as an IV which is generated prior to encrypting the first block. + fn do_encrypt_init( + key: &KeyMaterial, + ) -> Result<(Self, [u8; INIT_DATA_LEN]), SymmetricCipherError>; + /// Encrypts a single block of plaintext. + fn do_encrypt_block( + &mut self, + plaintext: &[u8; BLOCK_LEN], + ) -> Result<[u8; BLOCK_LEN], SymmetricCipherError>; + /// Encrypts a single block of plaintext and writes the ciphertext to the provided buffer. + fn do_encrypt_block_out( + &mut self, + plaintext: &[u8; BLOCK_LEN], + ciphertext: &mut [u8; BLOCK_LEN], + ) -> Result; + /// Encrypts the final block of plaintext. + fn do_encrypt_final( + &mut self, + plaintext: &[u8; BLOCK_LEN], + ) -> Result<[u8; BLOCK_LEN], SymmetricCipherError>; + /// Decrypts the final block of plaintext and writes the ciphertext to the provided buffer. + fn do_encrypt_final_out( + &mut self, + plaintext: &[u8; BLOCK_LEN], + ciphertext: &mut [u8; BLOCK_LEN], + ) -> Result; + /// Constructor that begins a flow of the streaming API for decryption one block at a time. + fn do_decrypt_init( + key: &KeyMaterial, + init_data: &[u8; INIT_DATA_LEN], + ) -> Result; + /// Decrypts a single block of ciphertext. + fn do_decrypt_block( + &mut self, + ciphertext: &[u8; BLOCK_LEN], + ) -> Result<[u8; BLOCK_LEN], SymmetricCipherError>; + /// Decrypts a single block of ciphertext and writes the plaintext to the provided buffer. + fn do_decrypt_block_out( + &mut self, + ciphertext: &[u8; BLOCK_LEN], + plaintext: &mut [u8; BLOCK_LEN], + ) -> Result; +} + +/// The basic functions of an Authenticated Encryption with Addititional Data cipher. +pub trait AEADCipher: + SymmetricCipher + Sized +{ + #[cfg(std)] + /// A one-shot API to encrypt some plaintext with the given key. + /// A distinguishing feature of AEAD ciphers is the ability to provide additional authenticated data (AAD) + /// that is not encrypted but is protected by the authentication tag; ie it can be sent along with the ciphertext + /// and any tampering with it will result in the decryption operation failing the tag check. + /// This function returns the ciphertext as a Vec, and therefore is only available when compiling with std. + /// Returns a tuple containing a generated nonce, the ciphertext and the tag. + fn aead_encrypt( + key: &KeyMaterial, + aad: &[u8], + plaintext: &[u8], + ) -> Result<([u8; NONCE_LEN], Vec, [u8; TAG_LEN]), SymmetricCipherError>; + /// A one-shot API to encrypt some plaintext with the given key. + /// A distinguishing feature of AEAD ciphers is the ability to provide additional authenticated data (AAD) + /// that is not encrypted but is protected by the authentication tag; ie it can be sent along with the ciphertext + /// and any tampering with it will result in the decryption operation failing the tag check. + /// Returns a tuple containing the randomly-generated nonce, number of bytes written to the ciphertext buffer, and the tag. + /// If you need a deterministic mode where you feed in the nonce, use the streaming API of [BlockCipher] + /// or [StreamCipher] as appropriate and feed the nonce into the IV field. + fn aead_encrypt_out( + key: &KeyMaterial, + aad: &[u8], + plaintext: &[u8], + ciphertext: &mut [u8], + ) -> Result<([u8; NONCE_LEN], usize, [u8; TAG_LEN]), SymmetricCipherError>; + /// All AEAD ciphers will also be either a [BlockCipher] or a [StreamCipher], and so will already + /// have a streaming API. + /// This allows you to finish either style of streaming API flow with AEAD specific do_final() + /// that computes and returns the authentication tag. + fn do_aead_encrypt_final(self) -> Result<[u8; TAG_LEN], SymmetricCipherError>; + #[cfg(std)] + /// A one-shot API to decrypt some ciphertext with the given key. + /// This function returns the ciphertext as a Vec, and therefore is only available when compiling with std. + fn aead_decrypt( + key: &KeyMaterial, + nonce: &[u8; NONCE_LEN], + aad: &[u8], + ciphertext: &[u8], + tag: &[u8; TAG_LEN], + ) -> Result, SymmetricCipherError>; + /// A one-shot API to decrypt some ciphertext with the given key. + /// This function takes a reference to the output buffer for the plaintext, and is therefore available in no_std. + /// See the documentation for the underlying implementation for details on providing a plaintext buffer of sufficient size; + /// typically the ciphertext is the same length as the plaintext, but some ciphers may have an expansion factor or require + /// extra space for a nonce or tag. + /// Returns a tuple containing the initialization data and the number of bytes written to the plaintext buffer. + fn aead_decrypt_out( + key: &KeyMaterial, + nonce: &[u8; NONCE_LEN], + aad: &[u8], + ciphertext: &[u8], + tag: &[u8], + plaintext: &mut [u8], + ) -> Result; + /// All AEAD ciphers will also be either a [BlockCipher] or a [StreamCipher], and so will already + /// have a streaming API. + /// This allows you to finish either style of streaming API flow with AEAD specific do_final() + /// that computes and returns the authentication tag. + fn do_aead_decrypt_final(self, tag: &[u8]) -> Result<(), SymmetricCipherError>; +} + +/// The basic functions of a stream cipher, which differ from those of a block cipher only in that +/// a stream cipher is assumed to have no underlying block size tied to the implementation, and so the caller gets to specify +/// the block size for the streaming APIs. +pub trait StreamCipher: + SymmetricCipher + Sized +{ + /// Constructor that begins a flow of the streaming API for encrypting one block at a time. + /// Allows for the implementation to return init data such as an IV which is generated prior to encrypting the first block. + fn do_stream_encrypt_init( + key: &KeyMaterial, + ) -> Result<(Self, [u8; INIT_DATA_LEN]), SymmetricCipherError>; + /// Encrypts a single block of plaintext. + fn do_stream_encrypt_block( + &mut self, + plaintext: &[u8; BLOCK_LEN], + ) -> Result<[u8; BLOCK_LEN], SymmetricCipherError>; + /// Encrypts a single block of plaintext and writes the ciphertext to the provided buffer. + fn do_stream_encrypt_block_out( + &mut self, + plaintext: &[u8; BLOCK_LEN], + ciphertext: &mut [u8; BLOCK_LEN], + ) -> Result<(), SymmetricCipherError>; + /// Encrypts the final block of plaintext. + fn do_stream_encrypt_final( + &mut self, + plaintext: &[u8; BLOCK_LEN], + ) -> Result<[u8; BLOCK_LEN], SymmetricCipherError>; + /// Encrypts the final block of plaintext and writes the ciphertext to the provided buffer. + fn do_stream_encrypt_final_out( + &mut self, + plaintext: &[u8; BLOCK_LEN], + ciphertext: &mut [u8; BLOCK_LEN], + ) -> Result<(), SymmetricCipherError>; + /// Constructor that begins a flow of the streaming API for decryption one block at a time. + fn do_stream_decrypt_init( + key: &KeyMaterial, + init_data: &[u8; INIT_DATA_LEN], + ) -> Result; + /// Decrypts a single block of ciphertext. + fn do_stream_decrypt_block( + &mut self, + ciphertext: &[u8; BLOCK_LEN], + ) -> Result<[u8; BLOCK_LEN], SymmetricCipherError>; + /// Decrypts a single block of ciphertext and writes the plaintext to the provided buffer. + fn do_stream_decrypt_block_out( + &mut self, + ciphertext: &[u8; BLOCK_LEN], + plaintext: &mut [u8; BLOCK_LEN], + ) -> Result<(), SymmetricCipherError>; +} + pub trait Hash: Default { /// The size of the internal block in bits -- needed by functions such as HMAC to compute security parameters. fn block_bitlen(&self) -> usize; @@ -37,19 +263,17 @@ pub trait Hash: Default { /// Provide a chunk of data to be absorbed into the hashes. /// `data` can be of any length, including zero bytes. /// do_update() is intended to be used as part of a streaming interface, and so may by called multiple times. - // fn do_update(&mut self, data: &[u8]) -> Result<(), HashError>; fn do_update(&mut self, data: &[u8]); /// Finish absorbing input and produce the hashes output. /// Consumes self, so this must be the final call to this object. - // fn do_final(self) -> Result, HashError>; fn do_final(self) -> Vec; /// Finish absorbing input and produce the hashes output. /// Consumes self, so this must be the final call to this object. /// /// If the provided buffer is smaller than the hash's output length, the output will be truncated. - /// If the provided buffor is larger than the hash's output length, the output will be placed in + /// If the provided buffer is larger than the hash's output length, the output will be placed in /// the first [Hash::output_len] bytes. /// The entire output buffer is zeroized before the hash output is written, so any bytes past /// [Hash::output_len] will be 0.