secured_cipher/algorithm/chacha20/
core.rs

1//! ChaCha20 Cryptographic Algorithm Implementation
2//!
3//! This module provides an implementation of the ChaCha20 stream cipher,
4//! as specified in RFC 7539 by the Internet Engineering Task Force (IETF).
5//! The implementation includes constants, state management, encryption/decryption functions,
6//! and utilities for data transformation relevant to the ChaCha20 algorithm.
7//!
8//! The constants and logic, as well as the test vectors used in this module,
9//! are based on and verifiable against the specifications detailed in the IETF paper:
10//! "ChaCha20 and Poly1305 for IETF Protocols" (RFC 7539).
11//! This can be accessed at https://datatracker.ietf.org/doc/html/rfc7539.
12//!
13//! The module is designed to be compliant with the RFC 7539 standard, ensuring reliability
14//! and correctness of the cryptographic operations as per the established IETF guidelines.
15
16/// Constants for the ChaCha20 algorithm.
17/// These four 32-bit words represent the ASCII encoding of "expand 32-byte k",
18/// used in the state initialization of the ChaCha20 block.
19pub const CONSTANTS: [u32; 4] = [0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574];
20
21/// Number of 32-bit words in the ChaCha state.
22/// The ChaCha20 state consists of 16 words, each of which is 32 bits long.
23pub const STATE_WORDS: usize = 16;
24
25/// Number of ChaCha20 rounds.
26/// This constant defines how many rounds of the main ChaCha20 algorithm will be executed.
27/// The standard number of rounds is 20.
28pub const ROUNDS: usize = 10;
29
30/// Size of the ChaCha20 nonce in bytes.
31/// The nonce is a 64-bit (8 bytes) value used to make each block unique.
32pub const CHACHA20_NONCE_SIZE: usize = 12;
33
34/// The array of words representing a ChaCha20 block.
35pub type Block = [u32; STATE_WORDS];
36
37/// Performs the quarter round operation on the state.
38///
39/// This operation modifies four words in the state as per the ChaCha20 algorithm's quarter round rules.
40/// It involves a series of addition, XOR, and rotation operations to mix the input words.
41///
42/// # Arguments
43/// * `a`, `b`, `c`, `d` - Indices of the state words to be modified.
44/// * `state` - A mutable reference to the 512-bit state array.
45pub fn quarter_round(a: usize, b: usize, c: usize, d: usize, state: &mut Block) {
46  state[a] = state[a].wrapping_add(state[b]);
47  state[d] ^= state[a];
48  state[d] = state[d].rotate_left(16);
49
50  state[c] = state[c].wrapping_add(state[d]);
51  state[b] ^= state[c];
52  state[b] = state[b].rotate_left(12);
53
54  state[a] = state[a].wrapping_add(state[b]);
55  state[d] ^= state[a];
56  state[d] = state[d].rotate_left(8);
57
58  state[c] = state[c].wrapping_add(state[d]);
59  state[b] ^= state[c];
60  state[b] = state[b].rotate_left(7);
61}
62
63/// Runs the ChaCha20 permutation on the provided state.
64pub fn permute(state: &Block) -> Block {
65  let mut block = *state;
66
67  // The ChaCha20 permutation consists of 20 rounds of quarter round operations.
68  run_rounds(&mut block);
69
70  // The original ChaCha20 algorithm adds the original state to the output of the rounds.
71  for (s1, s0) in block.iter_mut().zip(state.iter()) {
72    *s1 = s1.wrapping_add(*s0);
73  }
74
75  block
76}
77
78/// Runs the ChaCha20 rounds on the provided state.
79/// This function modifies the state in place.
80pub fn run_rounds(state: &mut Block) {
81  for _ in 0..ROUNDS {
82    // Odd rounds
83    quarter_round(0, 4, 8, 12, state);
84    quarter_round(1, 5, 9, 13, state);
85    quarter_round(2, 6, 10, 14, state);
86    quarter_round(3, 7, 11, 15, state);
87    // Even rounds
88    quarter_round(0, 5, 10, 15, state);
89    quarter_round(1, 6, 11, 12, state);
90    quarter_round(2, 7, 8, 13, state);
91    quarter_round(3, 4, 9, 14, state);
92  }
93}
94
95/// XORs two 512-bit state arrays.
96/// This function modifies the first array in place.
97///
98/// # Arguments
99/// * `a` - A mutable reference to the first state array.
100/// * `b` - A reference to the second state array.
101///
102/// # Panics
103/// Panics if the two arrays are not of equal length.
104pub fn xor_bytes(left: &mut [u8], right: &[u8]) {
105  assert!(
106    right.len() >= left.len(),
107    "The left array can't be XORed completely with the right array"
108  );
109  left
110    .iter_mut()
111    .zip(right.iter())
112    .for_each(|(left, right)| *left ^= *right);
113}
114
115#[cfg(test)]
116mod test {
117  use super::*;
118
119  #[test]
120  fn it_should_do_the_quarter_round() {
121    let mut state: Block = [
122      0x879531e0, 0xc5ecf37d, 0x516461b1, 0xc9a62f8a, 0x44c20ef3, 0x3390af7f, 0xd9fc690b,
123      0x2a5f714c, 0x53372767, 0xb00a5631, 0x974c541a, 0x359e9963, 0x5c971061, 0x3d631689,
124      0x2098d9d6, 0x91dbd320,
125    ];
126
127    quarter_round(2, 7, 8, 13, &mut state);
128
129    assert_eq!(
130      state,
131      [
132        0x879531e0, 0xc5ecf37d, 0xbdb886dc, 0xc9a62f8a, 0x44c20ef3, 0x3390af7f, 0xd9fc690b,
133        0xcfacafd2, 0xe46bea80, 0xb00a5631, 0x974c541a, 0x359e9963, 0x5c971061, 0xccc07c79,
134        0x2098d9d6, 0x91dbd320,
135      ]
136    );
137  }
138
139  #[test]
140  fn it_runs_all_the_quarter_rounds() {
141    let mut state: Block = [
142      0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, 0x03020100, 0x07060504, 0x0b0a0908,
143      0x0f0e0d0c, 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c, 0x00000001, 0x09000000,
144      0x4a000000, 0x00000000,
145    ];
146
147    run_rounds(&mut state);
148
149    assert_eq!(
150      state,
151      [
152        0x837778ab, 0xe238d763, 0xa67ae21e, 0x5950bb2f, 0xc4f2d0c7, 0xfc62bb2f, 0x8fa018fc,
153        0x3f5ec7b7, 0x335271c2, 0xf29489f3, 0xeabda8fc, 0x82e46ebd, 0xd19c12b4, 0xb04e16de,
154        0x9e83d0cb, 0x4e3c50a2,
155      ]
156    );
157  }
158
159  #[test]
160  fn it_executes_the_chacha20_permutation() {
161    let state: Block = [
162      0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, 0x03020100, 0x07060504, 0x0b0a0908,
163      0x0f0e0d0c, 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c, 0x00000001, 0x09000000,
164      0x4a000000, 0x00000000,
165    ];
166
167    let result = permute(&state);
168
169    assert_eq!(
170      result,
171      [
172        0xe4e7f110, 0x15593bd1, 0x1fdd0f50, 0xc47120a3, 0xc7f4d1c7, 0x0368c033, 0x9aaa2204,
173        0x4e6cd4c3, 0x466482d2, 0x09aa9f07, 0x05d7c214, 0xa2028bd9, 0xd19c12b5, 0xb94e16de,
174        0xe883d0cb, 0x4e3c50a2,
175      ]
176    );
177  }
178}