diff --git a/README.md b/README.md index 8637395..b518476 100644 --- a/README.md +++ b/README.md @@ -60,9 +60,9 @@ fn main() { // Key generation (32bytes for the key, 16 bytes for salt) let key = Key::<32, 16>::new(b"my password", 900_000); // 900K rounds - // Using Enclave for data encapsulation + // Using Enclave for data encapsulation (&str metadata, 8-byte nonce) let enclave = - Enclave::from_plain_bytes("Some metadata", key.pubk, b"Some bytes to encrypt".to_vec()) + Enclave::<&str, 8>::from_plain_bytes("Some metadata", key.pubk, b"Some bytes to encrypt".to_vec()) .unwrap(); // Get encrypted bytes (ciphertext) diff --git a/benches/src/chacha20.rs b/benches/src/chacha20.rs index 8bc6185..529cd7b 100644 --- a/benches/src/chacha20.rs +++ b/benches/src/chacha20.rs @@ -1,5 +1,5 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; -use secured_cipher::chacha20::ChaChaStream; +use secured_cipher::chacha20::{ChaChaStream, KEY_SIZE, NONCE_SIZE}; const KB: usize = 1024; @@ -7,8 +7,8 @@ fn bench(c: &mut Criterion) { let mut group = c.benchmark_group("ChaChaStream"); for size in &[KB, 2 * KB, 4 * KB, 8 * KB, 16 * KB] { - let key = [0u8; 32]; - let iv = [1u8; 8]; + let key = [0u8; KEY_SIZE]; + let iv = [1u8; NONCE_SIZE]; group.throughput(Throughput::Bytes(*size as u64)); diff --git a/benches/src/enclave.rs b/benches/src/enclave.rs index a19946d..7c8fa9d 100644 --- a/benches/src/enclave.rs +++ b/benches/src/enclave.rs @@ -1,4 +1,5 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; +use secured_cipher::chacha20::{KEY_SIZE, NONCE_SIZE}; use secured_enclave::Enclave; const KB: usize = 1024; @@ -7,13 +8,13 @@ fn bench(c: &mut Criterion) { let mut group = c.benchmark_group("enclave"); for size in &[KB, 2 * KB, 4 * KB, 8 * KB, 16 * KB] { - let key = [0u8; 32]; + let key = [0u8; KEY_SIZE]; group.throughput(Throughput::Bytes(*size as u64)); group.bench_with_input(BenchmarkId::new("encrypt", size), size, |b, &_size| { b.iter(|| { let buf = vec![0u8; *size]; - Enclave::from_plain_bytes("Metadata", key, buf) + Enclave::<&str, NONCE_SIZE>::from_plain_bytes("Metadata", key, buf) }); }); } diff --git a/cipher/src/chacha20/core.rs b/cipher/src/chacha20/core.rs index edd6fde..75a5611 100644 --- a/cipher/src/chacha20/core.rs +++ b/cipher/src/chacha20/core.rs @@ -12,6 +12,14 @@ pub const STATE_WORDS: usize = 16; /// The standard number of rounds is 20. pub const ROUNDS: usize = 20; +/// Size of the nonce in bytes. +/// The nonce is a 64-bit (8 bytes) value used to make each block unique. +pub const NONCE_SIZE: usize = 8; + +/// Size of the key in bytes. +/// The key is a 256-bit (32 bytes) value used for encryption and decryption. +pub const KEY_SIZE: usize = 32; + /// Performs the quarter round operation on the state. /// /// This operation modifies four words in the state as per the ChaCha20 algorithm's quarter round rules. diff --git a/cipher/src/chacha20/mod.rs b/cipher/src/chacha20/mod.rs index 2f03d68..0e30c7a 100644 --- a/cipher/src/chacha20/mod.rs +++ b/cipher/src/chacha20/mod.rs @@ -7,6 +7,7 @@ pub mod stream; use crate::{Bytes, Cipher, Slice}; pub use stream::ChaChaStream; +pub use self::core::{NONCE_SIZE, KEY_SIZE}; /// Represents the ChaCha20 encryption/decryption cipher. /// @@ -29,7 +30,17 @@ impl ChaCha20 { /// /// # Returns /// A new instance of `ChaCha20`. - pub fn new(key: [u8; 32], iv: [u8; 8]) -> Self { + pub fn new(key: &[u8], iv: &[u8]) -> Self { + let key: [u8; 32] = match key.len() { + KEY_SIZE => key.try_into().unwrap(), + _ => panic!("Invalid key length"), + }; + + let iv: [u8; 8] = match iv.len() { + NONCE_SIZE => iv.try_into().unwrap(), + _ => panic!("Invalid IV length"), + }; + Self { stream: ChaChaStream::new(key, iv), } @@ -82,7 +93,7 @@ mod tests { #[test] fn it_should_encrypt_data() { - let mut cipher = ChaCha20::new(KEY, IV); + let mut cipher = ChaCha20::new(&KEY, &IV); let encrypted_data = cipher.encrypt(&PLAINTEXT); assert_eq!(encrypted_data.len(), 64); @@ -91,7 +102,7 @@ mod tests { #[test] fn it_should_decrypt_data() { - let mut cipher = ChaCha20::new(KEY, IV); + let mut cipher = ChaCha20::new(&KEY, &IV); let decrypted_data = cipher.decrypt(&CIPHERTEXT); assert_eq!(decrypted_data.len(), 64); diff --git a/cipher/src/lib.rs b/cipher/src/lib.rs index 24d3cb0..8d5f3ec 100644 --- a/cipher/src/lib.rs +++ b/cipher/src/lib.rs @@ -26,7 +26,7 @@ //! let nonce: [u8; 8] = [0; 8]; // Replace with your nonce //! let data: &[u8] = b"Your data here"; // Data to be encrypted //! -//! let mut cipher = ChaCha20::new(key, nonce); +//! let mut cipher = ChaCha20::new(&key, &nonce); //! let encrypted_data = cipher.encrypt(data); //! println!("Encrypted data: {:?}", encrypted_data); //! ``` @@ -40,7 +40,7 @@ //! let nonce: [u8; 8] = [0; 8]; // Replace with your nonce //! let encrypted_data: &[u8] = &[0x1, 0x2, 0x3, 0x4]; // Replace with your encrypted data //! -//! let mut cipher = ChaCha20::new(key, nonce); +//! let mut cipher = ChaCha20::new(&key, &nonce); //! let decrypted_data = cipher.decrypt(encrypted_data); //! println!("Decrypted data: {:?}", decrypted_data); //! ``` @@ -54,8 +54,9 @@ //! suitable for various cryptographic needs. Whether you need high-level interfaces with `ChaCha20` //! or low-level control with `ChaChaStream`, `secured-cipher` is equipped to meet your cryptographic requirements. -pub use secured_cipher_key::{random_bytes, Key}; pub mod chacha20; +pub use secured_cipher_key::{random_bytes, Key}; +// pub use chacha20; /// A type alias for representing a slice of bytes. /// This is commonly used for raw data input/output in cryptographic operations. diff --git a/enclave/src/lib.rs b/enclave/src/lib.rs index cfcd082..4af5059 100644 --- a/enclave/src/lib.rs +++ b/enclave/src/lib.rs @@ -1,7 +1,7 @@ pub mod errors; pub use errors::EnclaveError; -use secured_cipher::{chacha20::ChaCha20, random_bytes, Cipher}; +use secured_cipher::{chacha20::{ChaCha20, core::KEY_SIZE}, random_bytes, Cipher}; /// `Enclave` acts as a container for encrypted data, including metadata and the encrypted content itself. /// @@ -11,7 +11,7 @@ use secured_cipher::{chacha20::ChaCha20, random_bytes, Cipher}; /// # Type Parameters /// * `T`: The type of metadata associated with the encrypted data. #[derive(Debug, Clone)] -pub struct Enclave { +pub struct Enclave { /// Metadata associated with the encrypted data. pub metadata: T, @@ -19,10 +19,10 @@ pub struct Enclave { encrypted_bytes: Box<[u8]>, /// The nonce used in the encryption process, 8 bytes long (ChaCha20). - nonce: [u8; 8], + nonce: [u8; NONCE_SIZE], } -impl Enclave { +impl Enclave { /// Creates a new `Enclave` instance from unencrypted data. /// /// # Arguments @@ -34,11 +34,11 @@ impl Enclave { /// A `Result` containing the newly created `Enclave` instance, or an error string if encryption fails. pub fn from_plain_bytes( metadata: T, - key: [u8; 32], + key: [u8; KEY_SIZE], plain_bytes: Vec, ) -> Result { - let nonce = random_bytes::<8>(); - let mut cipher = ChaCha20::new(key, nonce); + let nonce = random_bytes::(); + let mut cipher = ChaCha20::new(&key, &nonce); let encrypted_bytes = cipher.encrypt(&plain_bytes); @@ -56,14 +56,14 @@ impl Enclave { /// /// # Returns /// A `Result` containing the decrypted data as a vector of bytes, or an error string if decryption fails. - pub fn decrypt(&self, key: [u8; 32]) -> Result, String> { - let mut cipher = ChaCha20::new(key, self.nonce); + pub fn decrypt(&self, key: [u8; KEY_SIZE]) -> Result, String> { + let mut cipher = ChaCha20::new(&key, &self.nonce); Ok(cipher.decrypt(&self.encrypted_bytes)) } } -impl From> for Vec +impl From> for Vec where T: TryFrom> + Into>, { @@ -74,7 +74,7 @@ where /// /// # Returns /// A `Vec` representing the serialized enclave. - fn from(enclave: Enclave) -> Vec { + fn from(enclave: Enclave) -> Vec { let mut bytes: Vec = vec![]; let metadata_bytes = enclave.metadata.into(); @@ -87,7 +87,7 @@ where } } -impl TryFrom> for Enclave +impl TryFrom> for Enclave where T: TryFrom> + Into>, { @@ -105,8 +105,8 @@ where let metadata = T::try_from(bytes[1..metadata_len as usize + 1].to_vec()).or(Err( EnclaveError::Deserialization("error deserializing metadata".to_string()), ))?; - let encrypted_bytes = bytes[metadata_len as usize + 1..bytes.len() - 24].to_vec(); - let nonce = bytes[bytes.len() - 24..bytes.len()].to_vec(); + let encrypted_bytes = bytes[metadata_len as usize + 1..bytes.len() - NONCE_SIZE].to_vec(); + let nonce = bytes[bytes.len() - NONCE_SIZE..bytes.len()].to_vec(); Ok(Enclave { metadata, @@ -118,7 +118,7 @@ where } } -impl PartialEq for Enclave +impl PartialEq for Enclave where T: PartialEq + TryFrom> + Into>, { @@ -149,7 +149,7 @@ mod tests { let key: Key<32, 16> = Key::new(b"my password", 10_000); let bytes = [0u8, 1u8, 2u8, 3u8, 4u8].to_vec(); - let safe = Enclave::from_plain_bytes("metadata", key.pubk, bytes); + let safe = Enclave::<&str, 8>::from_plain_bytes("metadata", key.pubk, bytes); assert!(safe.is_ok()); assert_eq!(safe.unwrap().metadata, "metadata"); @@ -163,7 +163,7 @@ mod tests { fn it_should_decrypt_enclave() { let key: Key<32, 16> = Key::new(b"my password", 10_000); let bytes = [0u8, 1u8, 2u8, 3u8, 4u8].to_vec(); - let safe = Enclave::from_plain_bytes("metadata", key.pubk, bytes.clone()).unwrap(); + let safe = Enclave::<&str, 8>::from_plain_bytes("metadata", key.pubk, bytes.clone()).unwrap(); let decrypted_bytes = safe.decrypt(key.pubk); @@ -175,12 +175,24 @@ mod tests { fn it_should_fail_with_wrong_key() { let key: Key<32, 16> = Key::new(b"my password", 10_000); let bytes = [0u8, 1u8, 2u8, 3u8, 4u8].to_vec(); - let safe = Enclave::from_plain_bytes("metadata", key.pubk, bytes.clone()).unwrap(); + let safe = Enclave::<&str, 8>::from_plain_bytes("metadata", key.pubk, bytes.clone()).unwrap(); let wrong_key: Key<32, 16> = Key::new(b"my wrong password", 10_000); let decrypted_bytes = safe.decrypt(wrong_key.pubk).unwrap(); assert_ne!(decrypted_bytes, bytes); } + + #[test] + fn it_should_serialize_and_deserialize_to_bytes() { + let key: Key<32, 16> = Key::new(b"my password", 10_000); + let bytes = [0u8, 1u8, 2u8, 3u8, 4u8].to_vec(); + let enclave = Enclave::<[u8; 2], 8>::from_plain_bytes([0_u8, 1_u8], key.pubk, bytes.clone()).unwrap(); + + let serialized: Vec = enclave.clone().into(); + let deserialized = Enclave::try_from(serialized).unwrap(); + + assert_eq!(enclave, deserialized); + } } } diff --git a/src/lib.rs b/src/lib.rs index 68c32bf..022cda7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,7 @@ pub use cipher; /// let key: Key<32, 16> = Key::new(b"my password", 1_000); /// /// // Encrypt data: Utilize the Enclave to securely encrypt data along with metadata. -/// let enclave = Enclave::from_plain_bytes("some metadata", key.pubk, b"some bytes to encrypt".to_vec()).unwrap(); +/// let enclave = Enclave::<&str, 8>::from_plain_bytes("some metadata", key.pubk, b"some bytes to encrypt".to_vec()).unwrap(); /// /// // Decrypt data: Recover the original bytes from the encrypted enclave. /// let recovered_bytes = enclave.decrypt(key.pubk); diff --git a/src/main.rs b/src/main.rs index b965fab..abce2f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,8 +4,7 @@ use std::fs::{metadata, File}; use std::io::{Read, Write}; use enclave::Enclave; - -use cipher::Key; +use cipher::{Key, chacha20::NONCE_SIZE}; /// Defines command line subcommands for the application. #[derive(Debug, Subcommand)] @@ -58,7 +57,7 @@ fn main() { /// * `filename` - The name of the file to be encrypted. fn encrypt_file(password: &String, filename: &String) { let encryption_key: Key<32, 16> = Key::new(password.as_bytes(), 900_000); - let enclave = Enclave::from_plain_bytes( + let enclave = Enclave::<[u8; 16], NONCE_SIZE>::from_plain_bytes( encryption_key.salt, encryption_key.pubk, get_file_as_byte_vec(filename), @@ -81,7 +80,7 @@ fn encrypt_file(password: &String, filename: &String) { /// * `filename` - The name of the file to be decrypted. fn decrypt_file(password: &String, filename: &String) { let encrypted_bytes = get_file_as_byte_vec(filename); - let enclave = Enclave::try_from(encrypted_bytes).expect("Unable to deserialize enclave"); + let enclave = Enclave::<[u8; 16], NONCE_SIZE>::try_from(encrypted_bytes).expect("Unable to deserialize enclave"); let encryption_key: Key<32, 16> = Key::with_salt(password.as_bytes(), enclave.metadata, 900_000); let recovered_bytes = enclave .decrypt(encryption_key.pubk)