secured-cipher 0.4.1

Pure Rust implementation of the ChaCha cipher family
Documentation
//! # 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 algorithm;

pub use secured_cipher_key::{random_bytes, Key, KeyDerivationStrategy};

pub use algorithm::{
  AEADAlgorithm, AlgorithmKeyIVInit, AlgorithmKeyInit, AlgorithmProcess, AlgorithmProcessInPlace,
  ChaCha20, EncryptionAlgorithm, 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 encryption logic.
  encryption: Box<dyn EncryptionAlgorithm>,

  // The cipher's AEAD (authenticated encryption with associated data) logic.
  aead: Box<dyn AEADAlgorithm>,
}

pub enum CipherMode {
  ChaCha20Poly1305,
  Custom(Box<dyn EncryptionAlgorithm>, Box<dyn AEADAlgorithm>),
}

impl Cipher {
  /// Constructs a new `Cipher` instance using the specified cipher mode.
  ///
  /// # Arguments
  /// * `mode` - The mode of cipher (ChaCha20 or Custom) to use.
  ///
  /// # Returns
  /// A new instance of `Cipher`.
  pub fn new(mode: CipherMode) -> Self {
    let (encryption, aead): (Box<dyn EncryptionAlgorithm>, Box<dyn AEADAlgorithm>) = match mode {
      CipherMode::ChaCha20Poly1305 => (Box::new(ChaCha20::new()), Box::new(Poly1305::new())),
      CipherMode::Custom(encryption, aead) => (encryption, aead),
    };

    Self { encryption, aead }
  }

  /// 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.encryption.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.encryption.process(&[0; 64]));

    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.encryption.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.encryption.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.encryption.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::ChaCha20Poly1305)
  }
}

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