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