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"),
    }
  }
}