diff --git a/CHANGELOG.md b/CHANGELOG.md index c0e3709..9310fb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. --- -## [3.0.0] - 2022-09-02 +## [2.0.1] - 2022-09-06 ### Added ### Changed - use constant generics diff --git a/Cargo.lock b/Cargo.lock index e5c68af..890f59f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,7 +82,7 @@ dependencies = [ [[package]] name = "cosmian_crypto_core" -version = "3.0.0" +version = "2.0.1" dependencies = [ "aes-gcm", "curve25519-dalek", diff --git a/Cargo.toml b/Cargo.toml index 6ed859f..8aa6972 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ description = "Crypto lib for pure crypto primitives" edition = "2021" license = "MIT/Apache-2.0" name = "cosmian_crypto_core" -version = "3.0.0" +version = "2.0.1" [lib] crate-type = ["cdylib", "rlib"] diff --git a/src/asymmetric_crypto/curve25519.rs b/src/asymmetric_crypto/curve25519.rs index 7111ba4..0e03332 100644 --- a/src/asymmetric_crypto/curve25519.rs +++ b/src/asymmetric_crypto/curve25519.rs @@ -104,7 +104,7 @@ impl TryFrom<&[u8]> for X25519PrivateKey { impl From for [u8; X25519_SK_LENGTH] { #[inline] fn from(key: X25519PrivateKey) -> Self { - key.0.to_bytes() + key.to_bytes() } } @@ -292,14 +292,7 @@ impl TryFrom<&[u8]> for X25519PublicKey { impl From for [u8; X25519_PK_LENGTH] { #[inline] fn from(key: X25519PublicKey) -> Self { - key.0.compress().to_bytes() - } -} - -impl From<&X25519PublicKey> for [u8; X25519_PK_LENGTH] { - #[inline] - fn from(key: &X25519PublicKey) -> Self { - key.0.compress().to_bytes() + key.to_bytes() } } diff --git a/src/asymmetric_crypto/mod.rs b/src/asymmetric_crypto/mod.rs index 9d4ba19..b6195f5 100644 --- a/src/asymmetric_crypto/mod.rs +++ b/src/asymmetric_crypto/mod.rs @@ -1,9 +1,9 @@ use crate::KeyTrait; -use rand_core::{CryptoRng, RngCore}; use core::{ fmt::Debug, ops::{Add, Mul}, }; +use rand_core::{CryptoRng, RngCore}; use zeroize::{Zeroize, ZeroizeOnDrop}; pub mod curve25519; diff --git a/src/kdf.rs b/src/kdf.rs index a9b02b1..f596ec4 100644 --- a/src/kdf.rs +++ b/src/kdf.rs @@ -1,23 +1,24 @@ use crate::CryptoCoreError; use hkdf::Hkdf; -use sha2::Sha256; +use sha2::{digest::OutputSizeUser, Sha256}; /// Derive a key of `KEY_LENGTH` bytes using a HMAC-based Extract-and-Expand /// Key Derivation Function (HKDF) supplying a `bytes` and some `info` context /// `String`. The hash function used is Sha256. /// -/// - `bytes` : input bytes to hash, should be at least 32-bytes long +/// - `ikm` : input key material, should be at least 32-bytes long /// - `info` : some optional additional information to use in the hash pub fn hkdf_256( - bytes: &[u8], + ikm: &[u8], info: &[u8], ) -> Result<[u8; LENGTH], CryptoCoreError> { - if bytes.len() < 32 { - return Err(CryptoCoreError::InvalidSize( - "Input `bytes` size should be at least 32 bytes".to_string(), - )); + if ikm.len() < ::output_size() { + return Err(CryptoCoreError::InvalidSize(format!( + "Input `bytes` size should be at least {} bytes", + ::output_size() + ))); } - let h = Hkdf::::new(None, bytes); + let h = Hkdf::::new(None, ikm); let mut out = [0; LENGTH]; h.expand(info, &mut out) .map_err(|_| CryptoCoreError::KdfError(LENGTH))?; diff --git a/src/symmetric_crypto/aes_256_gcm_pure/dem.rs b/src/symmetric_crypto/aes_256_gcm_pure/dem.rs deleted file mode 100644 index 647e466..0000000 --- a/src/symmetric_crypto/aes_256_gcm_pure/dem.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! Implement the `DEM` trait for `Aes256GcmCrypto`. - -use crate::{ - symmetric_crypto::{ - aes_256_gcm_pure::{Aes256GcmCrypto, KEY_LENGTH}, - nonce::NonceTrait, - Dem, SymmetricCrypto, - }, - CryptoCoreError, -}; -use rand_core::{CryptoRng, RngCore}; -use std::convert::TryFrom; - -impl Dem for Aes256GcmCrypto { - fn encaps( - rng: &mut R, - secret_key: &[u8], - additional_data: Option<&[u8]>, - message: &[u8], - ) -> Result, CryptoCoreError> { - if secret_key.len() < KEY_LENGTH { - return Err(CryptoCoreError::SizeError { - given: secret_key.len(), - expected: KEY_LENGTH, - }); - } - // AES GCM includes an authentication method - // there is no need for parsing a MAC key - let key = Self::Key::try_from(&secret_key[..KEY_LENGTH])?; - let nonce = Self::Nonce::new(rng); - let mut c = Self::encrypt(&key, message, &nonce, additional_data) - .map_err(|err| CryptoCoreError::EncryptionError(err.to_string()))?; - // allocate correct byte number - let mut res: Vec = Vec::with_capacity(message.len() + Self::ENCAPSULATION_OVERHEAD); - res.append(&mut nonce.into()); - res.append(&mut c); - Ok(res) - } - - fn decaps( - secret_key: &[u8], - additional_data: Option<&[u8]>, - encapsulation: &[u8], - ) -> Result, CryptoCoreError> { - if secret_key.len() < KEY_LENGTH { - return Err(CryptoCoreError::SizeError { - given: secret_key.len(), - expected: KEY_LENGTH, - }); - } - // AES GCM includes an authentication method - // there is no need for parsing a MAC key - let key = Self::Key::try_from(&secret_key[..KEY_LENGTH])?; - let nonce = Self::Nonce::try_from(&encapsulation[..Self::Nonce::LENGTH])?; - Self::decrypt( - &key, - &encapsulation[Self::Nonce::LENGTH..], - &nonce, - additional_data, - ) - .map_err(|err| CryptoCoreError::EncryptionError(err.to_string())) - } -} - -#[cfg(test)] -mod tests { - - use crate::{ - entropy::CsRng, - symmetric_crypto::{aes_256_gcm_pure::Aes256GcmCrypto, Dem}, - CryptoCoreError, - }; - - #[test] - fn test_dem() -> Result<(), CryptoCoreError> { - let m = b"my secret message"; - let additional_data = Some(b"public tag".as_slice()); - let mut rng = CsRng::new(); - let secret_key = rng.generate_random_bytes::<256>(); - let c = Aes256GcmCrypto::encaps(&mut rng, &secret_key, additional_data, m)?; - let res = Aes256GcmCrypto::decaps(&secret_key, additional_data, &c)?; - if res != m { - return Err(CryptoCoreError::DecryptionError( - "Decaps failed".to_string(), - )); - } - Ok(()) - } -} diff --git a/src/symmetric_crypto/aes_256_gcm_pure/mod.rs b/src/symmetric_crypto/aes_256_gcm_pure/mod.rs index ed96b6b..ca63938 100644 --- a/src/symmetric_crypto/aes_256_gcm_pure/mod.rs +++ b/src/symmetric_crypto/aes_256_gcm_pure/mod.rs @@ -3,7 +3,7 @@ //! //! It will use the AES native interface on the CPU if available. use crate::{ - symmetric_crypto::{nonce::NonceTrait, SymKey, SymmetricCrypto}, + symmetric_crypto::{nonce::NonceTrait, Dem, SymKey}, CryptoCoreError, }; use aes_gcm::{ @@ -11,64 +11,93 @@ use aes_gcm::{ aes::cipher::generic_array::GenericArray, AeadInPlace, Aes256Gcm, KeyInit, }; -use std::fmt::Display; +use rand_core::{CryptoRng, RngCore}; -pub mod dem; +use super::{key::Key, nonce::Nonce}; /// Use a 256-bit AES key -pub const KEY_LENGTH: usize = 32; +const KEY_LENGTH: usize = 32; /// Use a 96-bit nonce -pub const NONCE_LENGTH: usize = 12; +const NONCE_LENGTH: usize = 12; /// Use a 128-bit MAC tag -pub const MAC_LENGTH: usize = 16; +const MAC_LENGTH: usize = 16; -pub type Key = crate::symmetric_crypto::key::Key; - -pub type Nonce = crate::symmetric_crypto::nonce::Nonce; +/// A 96-bit nonce restricts the plaintext size to 4096-bytes +const MAX_PLAINTEXT_LENGTH: usize = 4096; /// Structure implementing `SymmetricCrypto` and the `DEM` interfaces based on /// AES 256 GCM. pub struct Aes256GcmCrypto; -impl Display for Aes256GcmCrypto { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", Self::description()) - } -} - -impl SymmetricCrypto for Aes256GcmCrypto { - type Key = Key; - type Nonce = Nonce; +impl Dem for Aes256GcmCrypto { + const ENCRYPTION_OVERHEAD: usize = Self::Nonce::LENGTH + Self::MAC_LENGTH; const MAC_LENGTH: usize = MAC_LENGTH; - fn description() -> String { - format!( - "AES 256 GCM pure Rust (key bits: {}, nonce bits: {}, tag bits: {})", - KEY_LENGTH * 8, - NONCE_LENGTH * 8, - MAC_LENGTH * 8 - ) - } + type Key = Key; - fn encrypt( - key: &Self::Key, - bytes: &[u8], - nonce: &Self::Nonce, + type Nonce = Nonce; + + fn encrypt( + rng: &mut R, + secret_key: &Self::Key, + plaintext: &[u8], additional_data: Option<&[u8]>, ) -> Result, CryptoCoreError> { - encrypt_combined(key, bytes, nonce, additional_data) + if plaintext.len() > MAX_PLAINTEXT_LENGTH { + return Err(CryptoCoreError::InvalidSize(format!( + "Plaintext is too large ({} bytes), max size: {} ", + plaintext.len(), + MAX_PLAINTEXT_LENGTH + ))); + } + let nonce = Self::Nonce::new(rng); + // allocate correct byte number + let mut res: Vec = Vec::with_capacity(plaintext.len() + Self::ENCRYPTION_OVERHEAD); + res.extend_from_slice(nonce.as_bytes()); + res.append( + &mut encrypt_combined( + secret_key.as_bytes(), + plaintext, + nonce.as_bytes(), + additional_data, + ) + .map_err(|err| CryptoCoreError::EncryptionError(err.to_string()))?, + ); + Ok(res) } fn decrypt( - key: &Self::Key, - bytes: &[u8], - nonce: &Self::Nonce, + secret_key: &Self::Key, + ciphertext: &[u8], additional_data: Option<&[u8]>, ) -> Result, CryptoCoreError> { - decrypt_combined(key, bytes, nonce, additional_data) + if ciphertext.len() < Self::ENCRYPTION_OVERHEAD { + return Err(CryptoCoreError::InvalidSize(format!( + "Ciphertext is too small ({} bytes), min size: {}", + ciphertext.len(), + Self::ENCRYPTION_OVERHEAD + ))); + } + if ciphertext.len() > MAX_PLAINTEXT_LENGTH + Self::ENCRYPTION_OVERHEAD { + return Err(CryptoCoreError::InvalidSize(format!( + "Ciphertext is too large ({} bytes), max size: {} ", + ciphertext.len(), + MAX_PLAINTEXT_LENGTH + Self::ENCRYPTION_OVERHEAD + ))); + } + // Read the nonce used for encryption. We know this will not fail since + // Self::Nonce::LENGTH < Self::ENCRYPTOION_OVERHEAD + let nonce = Self::Nonce::try_from(&ciphertext[..Self::Nonce::LENGTH])?; + decrypt_combined( + secret_key.as_bytes(), + &ciphertext[Self::Nonce::LENGTH..], + nonce.as_bytes(), + additional_data, + ) + .map_err(|err| CryptoCoreError::EncryptionError(err.to_string())) } } @@ -78,15 +107,15 @@ impl SymmetricCrypto for Aes256GcmCrypto { /// /// The total length of the encrypted data is the message length + `MAC_LENGTH` pub fn encrypt_combined( - key: &Key, + key: &[u8], bytes: &[u8], - nonce: &Nonce, + nonce: &[u8], additional_data: Option<&[u8]>, ) -> Result, CryptoCoreError> { let payload = additional_data.map_or_else(|| Payload::from(bytes), |aad| Payload { msg: bytes, aad }); - Aes256Gcm::new(GenericArray::from_slice(key.as_bytes())) - .encrypt(GenericArray::from_slice(nonce.as_slice()), payload) + Aes256Gcm::new(GenericArray::from_slice(key)) + .encrypt(GenericArray::from_slice(nonce), payload) .map_err(|e| CryptoCoreError::EncryptionError(e.to_string())) } @@ -96,14 +125,14 @@ pub fn encrypt_combined( /// /// The tag length is `MAC_LENGTH` pub fn encrypt_in_place_detached( - key: &Key, + key: &[u8], bytes: &mut [u8], - nonce: &Nonce, + nonce: &[u8], additional_data: Option<&[u8]>, ) -> Result, CryptoCoreError> { - Aes256Gcm::new(GenericArray::from_slice(key.as_bytes())) + Aes256Gcm::new(GenericArray::from_slice(key)) .encrypt_in_place_detached( - GenericArray::from_slice(nonce.as_slice()), + GenericArray::from_slice(nonce), additional_data.unwrap_or_default(), bytes, ) @@ -118,14 +147,14 @@ pub fn encrypt_in_place_detached( /// /// Decryption will never be performed, even partially, before verification. pub fn decrypt_combined( - key: &Key, + key: &[u8], msg: &[u8], - nonce: &Nonce, + nonce: &[u8], additional_data: Option<&[u8]>, ) -> Result, CryptoCoreError> { let payload = additional_data.map_or_else(|| Payload::from(msg), |aad| Payload { msg, aad }); - Aes256Gcm::new(GenericArray::from_slice(key.as_bytes())) - .decrypt(GenericArray::from_slice(nonce.as_slice()), payload) + Aes256Gcm::new(GenericArray::from_slice(key)) + .decrypt(GenericArray::from_slice(nonce), payload) .map_err(|e| CryptoCoreError::DecryptionError(e.to_string())) } @@ -137,15 +166,15 @@ pub fn decrypt_combined( /// /// Decryption will never be performed, even partially, before verification. pub fn decrypt_in_place_detached( - key: &Key, + key: &[u8], bytes: &mut [u8], tag: &[u8], - nonce: &Nonce, + nonce: &[u8], additional_data: Option<&[u8]>, ) -> Result<(), CryptoCoreError> { - Aes256Gcm::new(GenericArray::from_slice(key.as_bytes())) + Aes256Gcm::new(GenericArray::from_slice(key)) .decrypt_in_place_detached( - GenericArray::from_slice(nonce.as_slice()), + GenericArray::from_slice(nonce), additional_data.unwrap_or_default(), bytes, GenericArray::from_slice(tag), @@ -161,9 +190,12 @@ mod tests { symmetric_crypto::{ aes_256_gcm_pure::{ decrypt_combined, decrypt_in_place_detached, encrypt_combined, - encrypt_in_place_detached, Key, Nonce, MAC_LENGTH, + encrypt_in_place_detached, Aes256GcmCrypto, Nonce, KEY_LENGTH, MAC_LENGTH, + NONCE_LENGTH, }, + key::Key, nonce::NonceTrait, + Dem, SymKey, }, CryptoCoreError, }; @@ -171,29 +203,29 @@ mod tests { #[test] fn test_encryption_decryption_combined() -> Result<(), CryptoCoreError> { let mut cs_rng = CsRng::new(); - let key = Key::new(&mut cs_rng); + let key = Key::::new(&mut cs_rng); let bytes = cs_rng.generate_random_bytes::<8192>(); - let iv = Nonce::new(&mut cs_rng); + let iv = Nonce::::new(&mut cs_rng); // no additional data - let encrypted_result = encrypt_combined(&key, &bytes, &iv, None)?; + let encrypted_result = encrypt_combined(&key, &bytes, iv.as_bytes(), None)?; assert_ne!(encrypted_result, bytes.to_vec()); assert_eq!(bytes.len() + MAC_LENGTH, encrypted_result.len()); - let recovered = decrypt_combined(&key, &encrypted_result, &iv, None)?; + let recovered = decrypt_combined(&key, &encrypted_result, iv.as_bytes(), None)?; assert_eq!(bytes.to_vec(), recovered); // additional data let aad = cs_rng.generate_random_bytes::<42>(); - let encrypted_result = encrypt_combined(&key, &bytes, &iv, Some(&aad))?; + let encrypted_result = encrypt_combined(&key, &bytes, iv.as_bytes(), Some(&aad))?; assert_ne!(encrypted_result, bytes.to_vec()); assert_eq!(bytes.len() + MAC_LENGTH, encrypted_result.len()); - let recovered = decrypt_combined(&key, &encrypted_result, &iv, Some(&aad))?; + let recovered = decrypt_combined(&key, &encrypted_result, iv.as_bytes(), Some(&aad))?; assert_eq!(bytes.to_vec(), recovered); // data should not be recovered if the AAD is modified let aad = cs_rng.generate_random_bytes::<42>(); - let recovered = decrypt_combined(&key, &encrypted_result, &iv, Some(&aad)); + let recovered = decrypt_combined(&key, &encrypted_result, iv.as_bytes(), Some(&aad)); assert_ne!(Ok(bytes.to_vec()), recovered); // data should not be recovered if the key is modified - let new_key = Key::new(&mut cs_rng); - let recovered = decrypt_combined(&new_key, &encrypted_result, &iv, Some(&aad)); + let new_key = Key::::new(&mut cs_rng); + let recovered = decrypt_combined(&new_key, &encrypted_result, iv.as_bytes(), Some(&aad)); assert_ne!(Ok(bytes.to_vec()), recovered); Ok(()) } @@ -201,26 +233,42 @@ mod tests { #[test] fn test_encryption_decryption_detached() -> Result<(), CryptoCoreError> { let mut cs_rng = CsRng::new(); - let key = Key::new(&mut cs_rng); + let key = Key::::new(&mut cs_rng); let bytes = cs_rng.generate_random_bytes::<8192>(); - let iv = Nonce::new(&mut cs_rng); + let iv = Nonce::::new(&mut cs_rng); // no additional data let mut data = bytes; - let tag = encrypt_in_place_detached(&key, &mut data, &iv, None)?; + let tag = encrypt_in_place_detached(&key, &mut data, iv.as_bytes(), None)?; assert_ne!(bytes, data); assert_eq!(bytes.len(), data.len()); assert_eq!(MAC_LENGTH, tag.len()); - decrypt_in_place_detached(&key, &mut data, &tag, &iv, None)?; + decrypt_in_place_detached(&key, &mut data, &tag, iv.as_bytes(), None)?; assert_eq!(bytes, data); // // additional data let ad = cs_rng.generate_random_bytes::<42>(); let mut data = bytes; - let tag = encrypt_in_place_detached(&key, &mut data, &iv, Some(&ad))?; + let tag = encrypt_in_place_detached(&key, &mut data, iv.as_bytes(), Some(&ad))?; assert_ne!(bytes, data); assert_eq!(bytes.len(), data.len()); assert_eq!(MAC_LENGTH, tag.len()); - decrypt_in_place_detached(&key, &mut data, &tag, &iv, Some(&ad))?; + decrypt_in_place_detached(key.as_bytes(), &mut data, &tag, iv.as_bytes(), Some(&ad))?; assert_eq!(bytes, data); Ok(()) } + + #[test] + fn test_dem() -> Result<(), CryptoCoreError> { + let m = b"my secret message"; + let additional_data = Some(b"public tag".as_slice()); + let mut rng = CsRng::new(); + let secret_key = Key::new(&mut rng); + let c = Aes256GcmCrypto::encrypt(&mut rng, &secret_key, m, additional_data)?; + let res = Aes256GcmCrypto::decrypt(&secret_key, &c, additional_data)?; + if res != m { + return Err(CryptoCoreError::DecryptionError( + "Decaps failed".to_string(), + )); + } + Ok(()) + } } diff --git a/src/symmetric_crypto/block.rs b/src/symmetric_crypto/block.rs deleted file mode 100644 index 780b0e8..0000000 --- a/src/symmetric_crypto/block.rs +++ /dev/null @@ -1,306 +0,0 @@ -use crate::{ - symmetric_crypto::{nonce::NonceTrait, SymmetricCrypto}, - CryptoCoreError, -}; -use rand_core::{CryptoRng, RngCore}; - -/// Block holding clear text data that needs to be encrypted. -/// -/// The max fixed length of clear text is `MAX_CLEAR_TEXT_LENGTH`. -/// The max block encrypted length is `Block::MAX_ENCRYPTED_LENGTH`. -/// -/// When calling `to_encrypted_bytes(...)` an array of bytes is generated that -/// is made of a `BlockHeader` containing the nonce, the cipher text and - -/// depending on the symmetric scheme - an authentication MAC. The nonce is -/// refreshed on each call to `to_encrypted_bytes(...)` -pub struct Block -where - S: SymmetricCrypto, -{ - clear_text: Vec, //padded with zeroes if need be - phantom_data: std::marker::PhantomData, -} - -impl - Block -where - S: SymmetricCrypto, -{ - /// Number of bytes added to the size of the input data in the output ciphertext. - pub const ENCRYPTION_OVERHEAD: usize = - BlockHeader::::LENGTH + >::MAC_LENGTH; - - /// Maximum number of bytes for an output ciphertext. - pub const MAX_ENCRYPTED_LENGTH: usize = MAX_CLEAR_TEXT_LENGTH + Self::ENCRYPTION_OVERHEAD; - - /// Creates a new empty block - #[must_use] - pub fn new() -> Self { - Self { - clear_text: vec![], - phantom_data: std::marker::PhantomData::default(), - } - } - - /// Parses a block of encrypted data to a `Block`. - /// The resource `uid` and `block_number` are part of the - /// authentication scheme amd must be re-supplied with the - /// same values used to encrypt the block - pub fn from_encrypted_bytes( - encrypted_bytes: &[u8], - symmetric_key: &>::Key, - uid: &[u8], - block_number: usize, - ) -> Result { - // The block header is always present - if encrypted_bytes.len() < Self::ENCRYPTION_OVERHEAD { - return Err(CryptoCoreError::InvalidSize(format!( - "array of encrypted data bytes of length {} is too small", - encrypted_bytes.len(), - ))); - } - if encrypted_bytes.len() > Self::MAX_ENCRYPTED_LENGTH { - return Err(CryptoCoreError::InvalidSize(format!( - "array of encrypted data bytes of length {} is too large", - encrypted_bytes.len(), - ))); - } - let block_header_len = BlockHeader::::LENGTH; - // recover the block header and regenerate the IV - let block_header = - BlockHeader::::parse(&encrypted_bytes[0..block_header_len])?; - let mut ad = Vec::with_capacity(uid.len() + 8); - ad.extend_from_slice(uid); - // Warning: `usize` can be interpreted as `u32` on 32-bits CPU-architecture. - // The `u64`-cast prevents build on those 32-bits machine or on - // `wasm32-unknown-unknown` builds. - ad.extend_from_slice(&(block_number as u64).to_le_bytes()); - // decrypt - let clear_text = S::decrypt( - symmetric_key, - &encrypted_bytes[block_header_len..], - &block_header.nonce, - Some(&ad), - )?; - Ok(Self { - clear_text, - phantom_data: std::marker::PhantomData::default(), - }) - } - - /// Generates the encrypted data block. The nonce is refreshed on each - /// call. The resource `uid` and `block_number` are part of the AEAD and - /// must be re-supplied to decrypt the bytes. They are used to guarantee - /// that a block cannot be moved within and between resources - pub fn to_encrypted_bytes( - &self, - rng: &mut R, - symmetric_key: &>::Key, - uid: &[u8], - block_number: usize, - ) -> Result, CryptoCoreError> { - // refresh the nonce - let nonce = S::Nonce::new(rng); - - // get the associated data ready - let mut ad = Vec::with_capacity(uid.len() + 8); - ad.extend_from_slice(uid); - // Warning: `usize` can be interpreted as `u32` on 32-bits CPU-architecture. - // The `u64`-cast prevents build on those 32-bits machine or on - // `wasm32-unknown-unknown` builds. - ad.extend_from_slice(&(block_number as u64).to_le_bytes()); - - // allocate correct number of bytes - let mut bytes = Vec::with_capacity( - S::Nonce::LENGTH + S::MAC_LENGTH + self.clear_text().len() + ad.len(), - ); - // write the header - bytes.append( - &mut BlockHeader:: { - nonce: nonce.clone(), - } - .to_bytes(), - ); - // write encrypted data - bytes.append(&mut S::encrypt( - symmetric_key, - &self.clear_text, - &nonce, - Some(&ad), - )?); - Ok(bytes) - } - - /// Returns a reference to the clear text - #[must_use] - pub fn clear_text(&self) -> &[u8] { - &self.clear_text - } - - /// Moves clear text data out of the block - #[must_use] - pub fn clear_text_owned(self) -> Vec { - self.clear_text - } - - /// Writes the given clear text data in the block. Pad the block with - /// zeroes if the offset is beyond the current end of the block. - /// - /// Return the length of the data written - pub fn write(&mut self, start_offset: usize, data: &[u8]) -> Result { - if start_offset >= MAX_CLEAR_TEXT_LENGTH { - return Err(CryptoCoreError::InvalidSize(format!( - "write in block: start offset: {} is greater than max block clear text len {}", - start_offset, MAX_CLEAR_TEXT_LENGTH - ))); - } - // pad if need be - let num_to_pad = start_offset - self.clear_text.len(); - if num_to_pad > 0 { - self.clear_text.append(&mut vec![0; num_to_pad]); - } - // see what space is available - let space_left = MAX_CLEAR_TEXT_LENGTH - start_offset; - if space_left == 0 { - return Ok(0); - } - if data.len() <= space_left { - self.clear_text.extend_from_slice(data); - return Ok(data.len()); - } - self.clear_text.extend_from_slice(&data[0..space_left]); - Ok(space_left) - } -} - -impl Default for Block -where - S: SymmetricCrypto, -{ - fn default() -> Self { - Self::new() - } -} - -/// The `BlockHeader` contains the nonce/IV of an encrypted `Block` -pub struct BlockHeader -where - S: SymmetricCrypto, -{ - nonce: >::Nonce, -} - -impl BlockHeader -where - S: SymmetricCrypto, -{ - pub const LENGTH: usize = >::Nonce::LENGTH; - - pub fn parse(bytes: &[u8]) -> Result { - //TODO: use transmute to make this faster ? - Ok(Self { - nonce: <>::Nonce>::try_from_bytes(bytes)?, - }) - } - - /// Convert the block header into a slice of bytes. - pub fn as_bytes(&self) -> &[u8] { - self.nonce.as_slice() - } - - /// Convert the block header into a vector of bytes. - pub fn to_bytes(&self) -> Vec { - self.as_bytes().to_vec() - } -} - -#[cfg(test)] -mod tests { - - use super::Block; - use crate::{ - entropy::CsRng, - symmetric_crypto::aes_256_gcm_pure::{Aes256GcmCrypto, Key, KEY_LENGTH}, - }; - const MAX_CLEAR_TEXT_LENGTH: usize = 4096; - type Bl = Block; - - #[test] - fn test_empty_block() { - let b = Bl::new(); - assert!(b.clear_text().is_empty()); - - let mut cs_rng = CsRng::new(); - let symmetric_key = Key::new(&mut cs_rng); - let uid = [1_u8; 32]; - // let iv = cs_rng.generate_nonce(); - let encrypted_bytes = b - .to_encrypted_bytes(&mut cs_rng, &symmetric_key, &uid, 1) - .expect("failed to encrypted bytes"); - assert_eq!(Bl::ENCRYPTION_OVERHEAD, encrypted_bytes.len()); - let c = Bl::from_encrypted_bytes(&encrypted_bytes, &symmetric_key, &uid, 1) - .expect("failed from encrypted bytes"); - assert!(c.clear_text().is_empty()); - } - - #[test] - fn test_full_block() { - let mut cs_rng = CsRng::new(); - let symmetric_key = Key::new(&mut cs_rng); - let uid = [1_u8; 32]; - - let mut b = Bl::new(); - assert!(b.clear_text().is_empty()); - let data = cs_rng.generate_random_bytes::<16384>(); - let written = b.write(0, &data).expect("failed writing data"); - assert_eq!(MAX_CLEAR_TEXT_LENGTH, written); - - // let iv = cs_rng.generate_nonce(); - let encrypted_bytes = b - .to_encrypted_bytes(&mut cs_rng, &symmetric_key, &uid, 1) - .expect("failed to encrypted bytes"); - assert_eq!( - Bl::ENCRYPTION_OVERHEAD + MAX_CLEAR_TEXT_LENGTH, - encrypted_bytes.len() - ); - assert_eq!( - Bl::ENCRYPTION_OVERHEAD + MAX_CLEAR_TEXT_LENGTH, - Bl::MAX_ENCRYPTED_LENGTH - ); - let c = Bl::from_encrypted_bytes(&encrypted_bytes, &symmetric_key, &uid, 1) - .expect("failed from encrypted bytes"); - assert_eq!(&data[0..MAX_CLEAR_TEXT_LENGTH], c.clear_text()); - } - - #[test] - fn test_partial_block() { - let mut cs_rng = CsRng::new(); - let symmetric_key = Key::new(&mut cs_rng); - let uid = [1_u8; 32]; - - let mut b = Bl::new(); - assert!(b.clear_text().is_empty()); - - let data1 = cs_rng.generate_random_bytes::<100>(); - let written = b.write(0, &data1).expect("failed writing data"); - assert_eq!(100, written); - - let data2 = cs_rng.generate_random_bytes::<100>(); - let written = b.write(200, &data2).expect("failed writing data"); - assert_eq!(100, written); - - // let iv = cs_rng.generate_nonce(); - let encrypted_bytes = b - .to_encrypted_bytes(&mut cs_rng, &symmetric_key, &uid, 1) - .expect("failed to encrypted bytes"); - assert_eq!(300 + Bl::ENCRYPTION_OVERHEAD, encrypted_bytes.len()); - let c = Bl::from_encrypted_bytes(&encrypted_bytes, &symmetric_key, &uid, 1) - .expect("failed from encrypted bytes"); - let mut data: Vec = vec![]; - data.extend_from_slice(&data1); - data.extend_from_slice(&[0_u8; 100]); - data.extend_from_slice(&data2); - - assert_eq!(&data, c.clear_text()); - } -} diff --git a/src/symmetric_crypto/mod.rs b/src/symmetric_crypto/mod.rs index b874071..55ef7d4 100644 --- a/src/symmetric_crypto/mod.rs +++ b/src/symmetric_crypto/mod.rs @@ -6,10 +6,8 @@ pub mod aes_256_gcm_pure; pub mod key; pub mod nonce; -mod block; mod metadata; -pub use block::Block; pub use metadata::BytesScanner; pub use metadata::Metadata; @@ -30,90 +28,47 @@ pub trait SymKey: KeyTrait + Hash { fn from_bytes(bytes: [u8; LENGTH]) -> Self; } -/// Defines a symmetric encryption scheme. If this scheme is authenticated, -/// the `MAC_LENGTH` will be greater than `0`. -pub trait SymmetricCrypto: Send + Sync { - const MAC_LENGTH: usize; - type Key: SymKey; - type Nonce: NonceTrait; +/// Defines a DEM based on a symmetric scheme as defined in section 9.1 of the +/// [ISO 2004](https://www.shoup.net/iso/std6.pdf). +pub trait Dem { + /// Number of bytes added to the message length in the encapsulation. + const ENCRYPTION_OVERHEAD: usize = Self::Nonce::LENGTH + Self::MAC_LENGTH; - /// A short description of the scheme - fn description() -> String; + /// MAC length + const MAC_LENGTH: usize; - /// Encrypts a message using a secret key and a public nonce in combined - /// mode. - /// - /// Append the MAC tag authenticating both the confidential and non - /// confidential data (aad) to the ciphertext. Thus, the length of the - /// encrypted data is the message length + `MAC_LENGTH`. - /// - /// It can also be used as a pur MAC by providing an empty message. - /// - /// # Parameters - /// - /// - `key` : symmetric key to use for encryption - /// - `bytes` : message bytes to encrypt - /// - `nonce` : nonce to use - /// - `aad` : additional associated data, the same data must be used - /// for decryption - fn encrypt( - key: &Self::Key, - bytes: &[u8], - nonce: &Self::Nonce, - aad: Option<&[u8]>, - ) -> Result, CryptoCoreError>; + /// Symmetric key length + const KEY_LENGTH: usize = KEY_LENGTH; - /// Decrypts a message in combined mode. - /// - /// Attempts to read a MAC appended to the ciphertext. The provided - /// additional data must match those provided during encryption for the - /// MAC to verify. Decryption will never be performed, even partially, - /// before verification. - /// - /// # Parameters - /// - /// - `key` : symmetric key to use - /// - `bytes` : encrypted bytes to decrypt (the MAC must be appended) - /// - `nonce` : nonce to use for decryption - /// - `aad` : additional associated data, the same data must be used - /// for encryption - fn decrypt( - key: &Self::Key, - bytes: &[u8], - nonce: &Self::Nonce, - aad: Option<&[u8]>, - ) -> Result, CryptoCoreError>; -} + /// Associated nonce type + type Nonce: NonceTrait; -/// Defines a DEM based on a symmetric scheme as defined in section 9.1 of the -/// [ISO 2004](https://www.shoup.net/iso/std6.pdf). -pub trait Dem: SymmetricCrypto { - /// Number of bytes added to the message length in the encapsulation. - const ENCAPSULATION_OVERHEAD: usize = Self::Nonce::LENGTH + Self::MAC_LENGTH; + /// Associated key type + type Key: SymKey; - /// Encapsulate data using the given symmetric key. + /// Encrypt data using the given symmetric key. /// /// - `rng` : secure random number generator /// - `secret_key` : secret symmetric key - /// - `message` : message to encapsulate + /// - `plaintext` : plaintext message /// - `aad` : optional data to use in the authentication method, /// must use the same for decryption - fn encaps( + fn encrypt( rng: &mut R, - secret_key: &[u8], + secret_key: &Self::Key, + plaintext: &[u8], aad: Option<&[u8]>, - message: &[u8], ) -> Result, CryptoCoreError>; - /// Decapsulate data using the given symmetric key. + /// Decrypt data using the given symmetric key. /// - /// - `secret_key` : secret symmetric key - /// - `encapsulation` : message encapsulation - /// - `aad` : optional data to use in the authentication method, + /// - `secret_key` : symmetric key + /// - `ciphertext` : ciphertext message + /// - `aad` : optional data to use in the authentication method, /// must use the same for encyption - fn decaps( - secret_key: &[u8], + fn decrypt( + secret_key: &Self::Key, + ciphertext: &[u8], aad: Option<&[u8]>, - encapsulation: &[u8], ) -> Result, CryptoCoreError>; } diff --git a/src/symmetric_crypto/nonce.rs b/src/symmetric_crypto/nonce.rs index 29a9d6c..b785771 100644 --- a/src/symmetric_crypto/nonce.rs +++ b/src/symmetric_crypto/nonce.rs @@ -4,14 +4,12 @@ //! ensure a ciphertext cannot be reused, hence avoiding replay attacks. use crate::CryptoCoreError; -use num_bigint::BigUint; -use rand_core::{CryptoRng, RngCore}; -use std::{ - cmp::min, +use core::{ convert::{TryFrom, TryInto}, fmt::{Debug, Display}, - vec::Vec, }; +use num_bigint::BigUint; +use rand_core::{CryptoRng, RngCore}; /// Trait defining a nonce for use in a symmetric encryption scheme. pub trait NonceTrait: Send + Sync + Sized + Clone { @@ -35,7 +33,7 @@ pub trait NonceTrait: Send + Sync + Sized + Clone { fn xor(&self, b2: &[u8]) -> Self; /// Serialize the nonce. - fn as_slice(&self) -> &[u8]; + fn as_bytes(&self) -> &[u8]; } /// Nonce object of the given size. @@ -47,12 +45,14 @@ pub struct Nonce([u8; NONCE_LENGTH]); impl NonceTrait for Nonce { const LENGTH: usize = NONCE_LENGTH; + #[inline] fn new(rng: &mut R) -> Self { let mut bytes = [0_u8; NONCE_LENGTH]; rng.fill_bytes(&mut bytes); Self(bytes) } + #[inline] fn try_from_bytes(bytes: &[u8]) -> Result { let b: [u8; NONCE_LENGTH] = bytes.try_into().map_err(|_| CryptoCoreError::SizeError { given: bytes.len(), @@ -61,6 +61,7 @@ impl NonceTrait for Nonce { Ok(Self(b)) } + #[inline] fn increment(&self, increment: usize) -> Self { let mut bi = BigUint::from_bytes_le(&self.0); bi += BigUint::from(increment); @@ -69,27 +70,21 @@ impl NonceTrait for Nonce { Self(bi_bytes.try_into().expect("This should never happen")) } + #[inline] fn xor(&self, b2: &[u8]) -> Self { - let mut n = self.0; - for i in 0..min(b2.len(), NONCE_LENGTH) { - n[i] ^= b2[i]; + let mut n = [0; NONCE_LENGTH]; + for (i, n_i) in n.iter_mut().enumerate() { + *n_i = self.0[i] ^ b2[i]; } Self(n) } - fn as_slice(&self) -> &[u8] { + #[inline] + fn as_bytes(&self) -> &[u8] { &self.0 } } -impl TryFrom> for Nonce { - type Error = CryptoCoreError; - - fn try_from(bytes: Vec) -> Result { - Self::try_from_bytes(&bytes) - } -} - impl<'a, const NONCE_LENGTH: usize> TryFrom<&'a [u8]> for Nonce { type Error = CryptoCoreError; @@ -104,12 +99,6 @@ impl From<[u8; NONCE_LENGTH]> for Nonce } } -impl From> for Vec { - fn from(n: Nonce) -> Self { - n.0.to_vec() - } -} - impl Display for Nonce { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", hex::encode(self.0)) @@ -129,9 +118,9 @@ mod tests { fn test_nonce() { let mut cs_rng = CsRng::new(); let nonce_1 = Nonce::::new(&mut cs_rng); - assert_eq!(NONCE_LENGTH, nonce_1.as_slice().len()); + assert_eq!(NONCE_LENGTH, nonce_1.as_bytes().len()); let nonce_2 = Nonce::::new(&mut cs_rng); - assert_eq!(NONCE_LENGTH, nonce_2.as_slice().len()); + assert_eq!(NONCE_LENGTH, nonce_2.as_bytes().len()); assert_ne!(nonce_1, nonce_2); }