aws_lc_rs/cipher/
chacha.rs1use crate::aws_lc::CRYPTO_chacha_20;
8use crate::cipher::block::{Block, BLOCK_LEN};
9use zeroize::Zeroize;
10
11use crate::error;
12
13pub(crate) const KEY_LEN: usize = 32usize;
14pub(crate) const NONCE_LEN: usize = 96 / 8;
15
16pub(crate) struct ChaCha20Key(pub(super) [u8; KEY_LEN]);
17
18impl From<[u8; KEY_LEN]> for ChaCha20Key {
19 fn from(bytes: [u8; KEY_LEN]) -> Self {
20 ChaCha20Key(bytes)
21 }
22}
23
24impl Drop for ChaCha20Key {
25 fn drop(&mut self) {
26 self.0.zeroize();
27 }
28}
29
30#[allow(clippy::needless_pass_by_value)]
31impl ChaCha20Key {
32 #[inline]
33 pub(crate) fn encrypt_in_place(&self, nonce: &[u8; NONCE_LEN], in_out: &mut [u8], ctr: u32) {
34 encrypt_in_place_chacha20(self, nonce, in_out, ctr);
35 }
36}
37
38#[inline]
39#[allow(clippy::needless_pass_by_value)]
40pub(crate) fn encrypt_block_chacha20(
41 key: &ChaCha20Key,
42 block: Block,
43 nonce: &[u8; NONCE_LEN],
44 counter: u32,
45) -> Result<Block, error::Unspecified> {
46 let mut cipher_text = [0u8; BLOCK_LEN];
47 encrypt_chacha20(
48 key,
49 block.as_ref().as_slice(),
50 &mut cipher_text,
51 nonce,
52 counter,
53 )?;
54
55 crate::fips::set_fips_service_status_unapproved();
56
57 Ok(Block::from(cipher_text))
58}
59
60#[inline]
61pub(crate) fn encrypt_chacha20(
62 key: &ChaCha20Key,
63 plaintext: &[u8],
64 ciphertext: &mut [u8],
65 nonce: &[u8; NONCE_LEN],
66 counter: u32,
67) -> Result<(), error::Unspecified> {
68 if ciphertext.len() < plaintext.len() {
69 return Err(error::Unspecified);
70 }
71 let key_bytes = &key.0;
72 unsafe {
73 CRYPTO_chacha_20(
74 ciphertext.as_mut_ptr(),
75 plaintext.as_ptr(),
76 plaintext.len(),
77 key_bytes.as_ptr(),
78 nonce.as_ptr(),
79 counter,
80 );
81 };
82 Ok(())
83}
84
85#[inline]
86pub(crate) fn encrypt_in_place_chacha20(
87 key: &ChaCha20Key,
88 nonce: &[u8; NONCE_LEN],
89 in_out: &mut [u8],
90 counter: u32,
91) {
92 let key_bytes = &key.0;
93 unsafe {
94 CRYPTO_chacha_20(
95 in_out.as_mut_ptr(),
96 in_out.as_ptr(),
97 in_out.len(),
98 key_bytes.as_ptr(),
99 nonce.as_ptr(),
100 counter,
101 );
102 }
103 crate::fips::set_fips_service_status_unapproved();
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::{test, test_file};
110
111 const MAX_ALIGNMENT: usize = 15;
112
113 #[test]
121 fn chacha20_test() {
122 let mut buf = vec![0u8; 1300];
124
125 test::run(
126 test_file!("data/chacha_tests.txt"),
127 move |section, test_case| {
128 assert_eq!(section, "");
129
130 let key = test_case.consume_bytes("Key");
131 let key: &[u8; KEY_LEN] = key.as_slice().try_into()?;
132 let key = ChaCha20Key::from(*key);
133
134 #[allow(clippy::cast_possible_truncation)]
135 let ctr = test_case.consume_usize("Ctr") as u32;
136 let nonce: [u8; NONCE_LEN] = test_case.consume_bytes("Nonce").try_into().unwrap();
137 let input = test_case.consume_bytes("Input");
138 let output = test_case.consume_bytes("Output");
139
140 for len in 0..=input.len() {
144 chacha20_test_case_inner(
145 &key,
146 nonce,
147 ctr,
148 &input[..len],
149 &output[..len],
150 &mut buf,
151 );
152 }
153
154 Ok(())
155 },
156 );
157 }
158
159 fn chacha20_test_case_inner(
160 key: &ChaCha20Key,
161 nonce: [u8; NONCE_LEN],
162 ctr: u32,
163 input: &[u8],
164 expected: &[u8],
165 buf: &mut [u8],
166 ) {
167 const ARBITRARY: u8 = 123;
170
171 for alignment in 0..=MAX_ALIGNMENT {
172 buf[..alignment].fill(ARBITRARY);
173 let buf = &mut buf[..input.len()];
174 buf.copy_from_slice(input);
175 let nonce = &nonce;
176
177 key.encrypt_in_place(nonce, buf, ctr);
178 assert_eq!(
179 &buf[..input.len()],
180 expected,
181 "Failed on alignment: {alignment}",
182 );
183 }
184 }
185}