secured_cipher/lib.rs
1//! # Secured-Cipher Library
2//!
3//! `secured-cipher` is a Rust library offering an implementation of the ChaCha20 and XChaCha20 encryption algorithms.
4//! It provides both high-level and low-level cryptographic functionalities through a common interface.
5//!
6//! ## Overview
7//!
8//! The library includes the following key components:
9//!
10//! - `core`: A module containing essential ChaCha20 cryptographic functionalities.
11//! - `ChaCha20`: A struct for the ChaCha20 stream cipher algorithm.
12//! - `Cipher`: A struct that provides a common interface for cryptographic operations, focusing on encryption and decryption.
13//! - `CipherMode`: An enum to specify the mode of the cipher (only ChaCha20 for now).
14//!
15//! ## Features
16//!
17//! - High-level interfaces for ChaCha20 and XChaCha20 ciphers.
18//! - Common `Cipher` interface for encryption and decryption operations.
19//! - Flexible usage with support for both raw and high-level cryptographic operations.
20//!
21//! ## Usage
22//!
23//! ### Basic Encryption and Decryption
24//!
25//! This example demonstrates encrypting and decrypting data using the ChaCha20 cipher.
26//!
27//! ```rust
28//! use secured_cipher::Cipher;
29//!
30//! let key: [u8; 32] = [0; 32]; // Your key
31//! let nonce: [u8; 12] = [0; 12]; // Your nonce
32//! let data: &[u8] = b"Your data here"; // Data to be encrypted
33//!
34//! let mut cipher = Cipher::default();
35//! cipher.init(&key, &nonce);
36//!
37//! // Encrypt and decrypt
38//! let encrypted_data = cipher.encrypt(data);
39//! let decrypted_data = cipher.decrypt(&encrypted_data);
40//!
41//! // Sign - the secret evelope contains the encrypted data and its MAC (message authentication code)
42//! let signed_secret_envelope = cipher.sign(b"your readable header", &encrypted_data);
43//!
44//! // Decrypt and verify - the verified decrypted data is returned if the MAC is valid
45//! let verified_decrypted_data = cipher.decrypt_and_verify(&signed_secret_envelope);
46//!
47//! // if the MAC is invalid, the decryption will fail
48//! let is_decryption_ok = verified_decrypted_data.is_ok();
49//!
50//! println!("Decrypted and verified data: {:?}", verified_decrypted_data.unwrap());
51//!
52//! ```
53//!
54//! ## Modules
55//!
56//! - `core`: Core functionalities and algorithmic implementations.
57//! - `stream`: Internal stream cipher operations, including `ChaChaStream`.
58
59pub mod algorithm;
60
61pub use secured_cipher_key::{random_bytes, Key, KeyDerivationStrategy};
62
63pub use algorithm::{
64  AEADAlgorithm, AlgorithmKeyIVInit, AlgorithmKeyInit, AlgorithmProcess, AlgorithmProcessInPlace,
65  ChaCha20, EncryptionAlgorithm, Poly1305, SignedEnvelope,
66};
67
68use std::error::Error;
69
70/// The `Cipher` struct provides a common interface for cryptographic operations,
71/// specifically focusing on encryption and decryption.
72pub struct Cipher {
73  /// The cipher's internal encryption logic.
74  encryption: Box<dyn EncryptionAlgorithm>,
75
76  // The cipher's AEAD (authenticated encryption with associated data) logic.
77  aead: Box<dyn AEADAlgorithm>,
78}
79
80pub enum CipherMode {
81  ChaCha20Poly1305,
82  Custom(Box<dyn EncryptionAlgorithm>, Box<dyn AEADAlgorithm>),
83}
84
85impl Cipher {
86  /// Constructs a new `Cipher` instance using the specified cipher mode.
87  ///
88  /// # Arguments
89  /// * `mode` - The mode of cipher (ChaCha20 or Custom) to use.
90  ///
91  /// # Returns
92  /// A new instance of `Cipher`.
93  pub fn new(mode: CipherMode) -> Self {
94    let (encryption, aead): (Box<dyn EncryptionAlgorithm>, Box<dyn AEADAlgorithm>) = match mode {
95      CipherMode::ChaCha20Poly1305 => (Box::new(ChaCha20::new()), Box::new(Poly1305::new())),
96      CipherMode::Custom(encryption, aead) => (encryption, aead),
97    };
98
99    Self { encryption, aead }
100  }
101
102  /// Initializes the cipher with a key and IV (initialization vector).
103  /// Sets up the cipher's internal state for encryption or decryption.
104  ///
105  /// # Arguments
106  /// * `key` - A byte slice representing the key.
107  /// * `iv` - A byte slice representing the initialization vector.
108  ///
109  /// # Returns
110  /// A mutable reference to the cipher instance.
111  pub fn init(&mut self, key: &[u8], iv: &[u8]) -> &mut Self {
112    self.encryption.init(key, iv);
113
114    // The Poly1305 authenticator uses a subkey derived from the cipher's key.
115    // This subkey is generated by running the ChaCha20 permutation on a block of zeros.
116    self.aead.init(&self.encryption.process(&[0; 64]));
117
118    self
119  }
120
121  /// Encrypts the provided data.
122  ///
123  /// # Arguments
124  /// * `data` - A slice of data to be encrypted.
125  ///
126  /// # Returns
127  /// Encrypted data as a vector of bytes (`Bytes`).
128  pub fn encrypt(&mut self, data: &[u8]) -> Vec<u8> {
129    // Encrypt the data using the ChaCha20 permutation
130    self.encryption.process(data)
131  }
132
133  /// Signs the provided data.
134  ///
135  /// # Arguments
136  /// * `header` - A slice of unencrypted data to be signed.
137  /// * `data` - A slice of data to be signed.
138  ///
139  /// # Returns
140  /// A signed envelope containing the data and its MAC (message authentication code).
141  pub fn sign(&mut self, header: &[u8], data: &[u8]) -> SignedEnvelope {
142    let mac = self
143      .aead
144      .process(&[header.to_vec(), data.to_vec()].concat());
145
146    SignedEnvelope {
147      header: header.to_vec(),
148      data: data.into(),
149      mac,
150    }
151  }
152
153  /// Decrypts the provided data.
154  /// Note that this method does not provide any integrity checks. Most of the
155  /// use cases should be covered by `decrypt_and_verify()` instead.
156  ///
157  /// # Arguments
158  /// * `data` - A slice of data to be decrypted.
159  ///
160  /// # Returns
161  /// Decrypted data as a vector of bytes (`Bytes`).
162  pub fn decrypt(&mut self, data: &[u8]) -> Vec<u8> {
163    // Decrypt the data using the ChaCha20 permutation
164    self.encryption.process(data)
165  }
166
167  /// Decrypts the provided data and verifies the MAC.
168  ///
169  /// # Arguments
170  /// * `envelope` - A signed envelope containing encrypted data to be decrypted.
171  ///
172  /// # Returns
173  /// Decrypted data as a vector of bytes (`Bytes`), or an error in case of decryption failure.
174  pub fn decrypt_and_verify(&mut self, envelope: &SignedEnvelope) -> Result<Vec<u8>, CipherError> {
175    // Check the MAC (message authentication code) to ensure the integrity of the data
176    if envelope.mac
177      != self
178        .aead
179        .process(&[envelope.header.clone(), envelope.data.clone()].concat())
180    {
181      return Err(CipherError::AuthenticationFailed);
182    }
183
184    // Decrypt the data using the ChaCha20 permutation
185    Ok(self.encryption.process(&envelope.data))
186  }
187}
188
189impl Default for Cipher {
190  /// Provides a default instance of `Cipher` using the XChaCha20 mode.
191  ///
192  /// # Returns
193  /// A new instance of `Cipher` with XChaCha20 mode.
194  fn default() -> Self {
195    Self::new(CipherMode::ChaCha20Poly1305)
196  }
197}
198
199#[derive(Debug)]
200pub enum CipherError {
201  AuthenticationFailed,
202}
203
204impl Error for CipherError {}
205
206impl std::fmt::Display for CipherError {
207  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208    match self {
209      CipherError::AuthenticationFailed => write!(f, "Authentication failed"),
210    }
211  }
212}