nanorand/crypto/
chacha.rs
1const CHACHA_TAU: &[u8] = b"expand 32-byte k";
2
3fn chacha_quarter_round(state: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize) {
4 state[a] = state[a].wrapping_add(state[b]);
5 state[d] ^= state[a];
6 state[d] = state[d].rotate_left(16);
7
8 state[c] = state[c].wrapping_add(state[d]);
9 state[b] ^= state[c];
10 state[b] = state[b].rotate_left(12);
11
12 state[a] = state[a].wrapping_add(state[b]);
13 state[d] ^= state[a];
14 state[d] = state[d].rotate_left(8);
15
16 state[c] = state[c].wrapping_add(state[d]);
17 state[b] ^= state[c];
18 state[b] = state[b].rotate_left(7);
19}
20
21const fn chacha_pack(unpacked: &[u8], idx: usize) -> u32 {
22 (unpacked[idx] as u32)
23 | ((unpacked[idx + 1] as u32) << 8)
24 | ((unpacked[idx + 2] as u32) << 16)
25 | ((unpacked[idx + 3] as u32) << 24)
26}
27
28pub fn chacha_block<const ROUNDS: u8>(input: [u32; 16]) -> [u32; 16] {
30 let mut x = input;
31 assert_eq!(ROUNDS % 2, 0, "ChaCha rounds must be divisble by 2!");
32 for _ in (0..ROUNDS).step_by(2) {
33 chacha_quarter_round(&mut x, 0, 4, 8, 12);
35 chacha_quarter_round(&mut x, 1, 5, 9, 13);
36 chacha_quarter_round(&mut x, 2, 6, 10, 14);
37 chacha_quarter_round(&mut x, 3, 7, 11, 15);
38 chacha_quarter_round(&mut x, 0, 5, 10, 15);
40 chacha_quarter_round(&mut x, 1, 6, 11, 12);
41 chacha_quarter_round(&mut x, 2, 7, 8, 13);
42 chacha_quarter_round(&mut x, 3, 4, 9, 14);
43 }
44 x.iter_mut()
45 .zip(input.iter())
46 .for_each(|(l, r)| *l = l.wrapping_add(*r));
47 x
48}
49
50pub const fn chacha_init(key: [u8; 32], nonce: [u8; 8]) -> [u32; 16] {
52 let mut state = [0u32; 16];
53 state[0] = chacha_pack(CHACHA_TAU, 0);
54 state[1] = chacha_pack(CHACHA_TAU, 4);
55 state[2] = chacha_pack(CHACHA_TAU, 8);
56 state[3] = chacha_pack(CHACHA_TAU, 12);
57
58 state[4] = chacha_pack(&key, 0);
59 state[5] = chacha_pack(&key, 4);
60 state[6] = chacha_pack(&key, 8);
61 state[7] = chacha_pack(&key, 12);
62 state[8] = chacha_pack(&key, 16);
63 state[9] = chacha_pack(&key, 20);
64 state[10] = chacha_pack(&key, 24);
65 state[11] = chacha_pack(&key, 28);
66
67 state[12] = 0;
69 state[13] = 0;
70 state[14] = chacha_pack(&nonce, 0);
72 state[15] = chacha_pack(&nonce, 4);
73 state
74}
75
76pub fn chacha_increment_counter(state: &mut [u32; 16]) -> bool {
79 let counter = ((state[13] as u64) << 32) | (state[12] as u64);
80 match counter.checked_add(1) {
81 Some(new_counter) => {
82 state[12] = (new_counter & 0xFFFFFFFF) as u32;
83 state[13] = ((counter >> 32) & 0xFFFFFFFF) as u32;
84 true
85 }
86 None => false,
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use std::convert::TryInto;
94
95 macro_rules! ietf_test_vector {
96 ($key_hex: tt, $nonce_hex: tt, $keystream_hex: tt) => {
97 let key: [u8; 32] = hex::decode($key_hex).unwrap().try_into().unwrap();
98 let nonce: [u8; 8] = hex::decode($nonce_hex).unwrap().try_into().unwrap();
99 let expected_keystream: Vec<u8> = hex::decode($keystream_hex).unwrap();
100
101 let mut state = chacha_init(key, nonce);
102 let mut keystream: Vec<u8> = Vec::with_capacity(expected_keystream.len());
103
104 while expected_keystream.len() > keystream.len() {
105 chacha_block::<20>(state)
106 .iter()
107 .for_each(|packed| keystream.extend_from_slice(&packed.to_le_bytes()));
108 chacha_increment_counter(&mut state);
109 }
110 keystream.resize(expected_keystream.len(), 0);
111
112 assert_eq!(keystream, expected_keystream);
113 };
114 }
115
116 #[test]
117 fn test_ietf_chacha20_test_vectors() {
118 ietf_test_vector!(
119 "0000000000000000000000000000000000000000000000000000000000000000",
120 "0000000000000000",
121 "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"
122 );
123
124 ietf_test_vector!(
125 "0000000000000000000000000000000000000000000000000000000000000001",
126 "0000000000000000",
127 "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d792b1c43fea817e9ad275ae546963"
128 );
129
130 ietf_test_vector!(
131 "0000000000000000000000000000000000000000000000000000000000000000",
132 "0000000000000001",
133 "de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b5277062eb7a0433e445f41e3"
134 );
135
136 ietf_test_vector!(
137 "0000000000000000000000000000000000000000000000000000000000000000",
138 "0100000000000000",
139 "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b"
140 );
141
142 ietf_test_vector!(
143 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
144 "0001020304050607",
145 "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc118be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78fab78c9"
146 );
147 }
148}