Skip to content

Commit

Permalink
docs: enhance docs
Browse files Browse the repository at this point in the history
  • Loading branch information
mikesposito committed Dec 2, 2023
1 parent 55a5967 commit 4958017
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 15 deletions.
4 changes: 2 additions & 2 deletions cipher/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
//!
//! The library includes the following key components:
//!
//! - `core`: A module containing essential ChaCha20 cryptographic functionalities.
//! - `ChaCha20`: A struct for the ChaCha20 stream cipher algorithm.
//! - `XChaCha20`: A struct for the XChaCha20 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 (ChaCha20 or XChaCha20).
//! - `CipherMode`: An enum to specify the mode of the cipher (only ChaCha20 for now).
//!
//! ## Features
//!
Expand Down
82 changes: 69 additions & 13 deletions cipher/src/permutation/chacha20.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::permutation::core::CHACHA20_NONCE_SIZE;

use super::core::{permute, xor_bytes, CONSTANTS, STATE_WORDS, Block};
use super::core::{permute, xor_bytes, Block, CONSTANTS, STATE_WORDS};

use super::Permutation;

Expand All @@ -22,18 +22,49 @@ impl ChaCha20 {
}
}

/// Generates the next 64-byte keystream block from the ChaCha20 state.
///
/// This function advances the ChaCha20 state and produces a keystream block based on the current state.
/// It performs a permutation of the state, increments the block counter to ensure uniqueness for subsequent calls,
/// and then serializes the permuted state into a 64-byte array.
///
/// # Returns
/// A 64-byte array representing the generated keystream block.
///
/// # Panics
/// Panics if the 32-bit block counter overflows, which would only happen after a very large
/// number of blocks (2^32-1) have been processed with the same key-nonce combination.
///
/// # Example
/// ```
/// // Assuming `self` is a valid mutable reference to a ChaCha20 cipher instance
/// let keystream_block = self.next_keystream();
/// // `keystream_block` now contains the next 64 bytes of the keystream
/// ```
///
/// # Notes
/// The keystream generated by this function is used to encrypt or decrypt data by XORing
/// it with the plaintext or ciphertext. Each call to this function must produce a unique keystream block.
/// This uniqueness is guaranteed by incrementing the internal block counter.
pub fn next_keystream(&mut self) -> [u8; 64] {
// Ensures the block counter has not overflowed
assert!(self.state[12] != 0, "ChaCha20 counter overflow");

// Initialize an array to hold the keystream
let mut keystream = [0u8; 64];

// Perform the ChaCha20 permutation on the current state
let block = permute(&self.state);

// Increment the block counter, wrapping around if it reaches its maximum value
self.state[12] = self.state[12].wrapping_add(1);

// Convert the 32-bit words from the permuted block into bytes and copy them into the keystream
for (bytes, word) in keystream.chunks_exact_mut(4).zip(block) {
bytes.copy_from_slice(&word.to_le_bytes());
}

// Return the generated 64-byte keystream block
keystream
}
}
Expand Down Expand Up @@ -75,30 +106,55 @@ impl Permutation for ChaCha20 {
}
}

/// Processes the input data with the ChaCha20 cipher.
/// Processes input data using the ChaCha20 cipher algorithm.
///
/// This method encrypts or decrypts the input data by XORing it with the keystream generated from the cipher's internal state.
/// It is suitable for both encryption and decryption due to the XOR operation's reversible nature.
/// This function applies the ChaCha20 encryption or decryption process to the given input bytes.
/// It works by generating a unique keystream for each 64-byte block of the input data and then
/// applying an XOR operation between the data block and the keystream. This process is suitable
/// for both encryption and decryption due to the reversible nature of the XOR operation.
///
/// # Arguments
/// * `bytes_in` - A slice of bytes to be processed (encrypted or decrypted).
/// * `bytes_in` - A slice of bytes representing the input data to be processed (either plaintext for encryption
/// or ciphertext for decryption).
///
/// # Returns
/// A vector of bytes containing the processed (encrypted or decrypted) data.
/// A `Vec<u8>` containing the processed data (encrypted or decrypted).
///
/// # Behavior
/// The function divides the input data into 64-byte blocks. For each block, it generates a unique
/// keystream using the `next_keystream` method. Each block of the input data is then XORed with its
/// corresponding keystream block. This method ensures that each block is encrypted or decrypted
/// with a different keystream, which is essential for the security of the cipher.
///
/// After processing all blocks, the function clears the internal state to prevent any residual
/// sensitive data from remaining in memory.
///
/// # Example
/// ```
/// // Assuming `self` is a valid mutable reference to a ChaCha20 cipher instance
/// let data = b"some plaintext data"; // Data to be encrypted or decrypted
/// let processed_data = self.process(data);
/// // `processed_data` now contains the encrypted or decrypted output
/// ```
///
/// # Notes
/// It's important to use the same nonce and key for decrypting the data that were used for encryption.
/// The output size will be equal to the input size, as ChaCha20 is a stream cipher.
fn process(&mut self, bytes_in: &[u8]) -> Vec<u8> {
// Prepare the output vector of 32-bit words from the input
// Clone the input bytes to prepare the output vector
let mut out = bytes_in.to_owned();
// Each block of 64 bytes should be processed by a separate keystream.
// Each keystream is initialized from the cipher's state with a
// block counter that is incremented after each block is processed

// Process each 64-byte block of the input data
out.chunks_mut(64).for_each(|plain_chunk| {
// Generate the keystream
// Generate the keystream for the current block
let keystream = self.next_keystream();
// XOR the plaintext with the keystream
// XOR the block with the keystream to perform encryption/decryption
xor_bytes(plain_chunk, &keystream);
});
// Clear the keystream

// Clear the internal state after processing to maintain security
self.clear();

// Return the processed data
out.to_vec()
}
Expand Down

0 comments on commit 4958017

Please sign in to comment.