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

fix: deserialization issue #12

Merged
merged 2 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions benches/src/chacha20.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
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;

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));

Expand Down
5 changes: 3 additions & 2 deletions benches/src/enclave.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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)
});
});
}
Expand Down
8 changes: 8 additions & 0 deletions cipher/src/chacha20/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
17 changes: 14 additions & 3 deletions cipher/src/chacha20/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
pub mod core;
pub mod stream;

pub use self::core::{KEY_SIZE, NONCE_SIZE};
use crate::{Bytes, Cipher, Slice};
pub use stream::ChaChaStream;

Expand All @@ -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),
}
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
7 changes: 4 additions & 3 deletions cipher/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
//! ```
Expand All @@ -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);
//! ```
Expand All @@ -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.
Expand Down
52 changes: 34 additions & 18 deletions enclave/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
pub mod errors;

pub use errors::EnclaveError;
use secured_cipher::{chacha20::ChaCha20, random_bytes, Cipher};
use secured_cipher::{
chacha20::{core::KEY_SIZE, ChaCha20},
random_bytes, Cipher,
};

/// `Enclave` acts as a container for encrypted data, including metadata and the encrypted content itself.
///
Expand All @@ -11,18 +14,18 @@ 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<T> {
pub struct Enclave<T, const NONCE_SIZE: usize> {
/// Metadata associated with the encrypted data.
pub metadata: T,

/// The encrypted data.
encrypted_bytes: Box<[u8]>,

/// The nonce used in the encryption process, 8 bytes long (ChaCha20).
nonce: [u8; 8],
nonce: [u8; NONCE_SIZE],
}

impl<T> Enclave<T> {
impl<T, const NONCE_SIZE: usize> Enclave<T, NONCE_SIZE> {
/// Creates a new `Enclave` instance from unencrypted data.
///
/// # Arguments
Expand All @@ -34,11 +37,11 @@ impl<T> Enclave<T> {
/// 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<u8>,
) -> Result<Self, String> {
let nonce = random_bytes::<8>();
let mut cipher = ChaCha20::new(key, nonce);
let nonce = random_bytes::<NONCE_SIZE>();
let mut cipher = ChaCha20::new(&key, &nonce);

let encrypted_bytes = cipher.encrypt(&plain_bytes);

Expand All @@ -56,14 +59,14 @@ impl<T> Enclave<T> {
///
/// # 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<Vec<u8>, String> {
let mut cipher = ChaCha20::new(key, self.nonce);
pub fn decrypt(&self, key: [u8; KEY_SIZE]) -> Result<Vec<u8>, String> {
let mut cipher = ChaCha20::new(&key, &self.nonce);

Ok(cipher.decrypt(&self.encrypted_bytes))
}
}

impl<T> From<Enclave<T>> for Vec<u8>
impl<T, const NONCE_SIZE: usize> From<Enclave<T, NONCE_SIZE>> for Vec<u8>
where
T: TryFrom<Vec<u8>> + Into<Vec<u8>>,
{
Expand All @@ -74,7 +77,7 @@ where
///
/// # Returns
/// A `Vec<u8>` representing the serialized enclave.
fn from(enclave: Enclave<T>) -> Vec<u8> {
fn from(enclave: Enclave<T, NONCE_SIZE>) -> Vec<u8> {
let mut bytes: Vec<u8> = vec![];
let metadata_bytes = enclave.metadata.into();

Expand All @@ -87,7 +90,7 @@ where
}
}

impl<T> TryFrom<Vec<u8>> for Enclave<T>
impl<T, const NONCE_SIZE: usize> TryFrom<Vec<u8>> for Enclave<T, NONCE_SIZE>
where
T: TryFrom<Vec<u8>> + Into<Vec<u8>>,
{
Expand All @@ -105,8 +108,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,
Expand All @@ -118,7 +121,7 @@ where
}
}

impl<T> PartialEq for Enclave<T>
impl<T, const NONCE_SIZE: usize> PartialEq for Enclave<T, NONCE_SIZE>
where
T: PartialEq + TryFrom<Vec<u8>> + Into<Vec<u8>>,
{
Expand Down Expand Up @@ -149,7 +152,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");
Expand All @@ -163,7 +166,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);

Expand All @@ -175,12 +178,25 @@ 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<u8> = enclave.clone().into();
let deserialized = Enclave::try_from(serialized).unwrap();

assert_eq!(enclave, deserialized);
}
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 4 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ use rpassword::prompt_password;
use std::fs::{metadata, File};
use std::io::{Read, Write};

use cipher::{chacha20::NONCE_SIZE, Key};
use enclave::Enclave;

use cipher::Key;

/// Defines command line subcommands for the application.
#[derive(Debug, Subcommand)]
enum Command {
Expand Down Expand Up @@ -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),
Expand All @@ -81,7 +80,8 @@ 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)
Expand Down