1use super::{
16 chacha::{self, Counter},
17 iv::Iv,
18 poly1305, Aad, Block, Direction, Nonce, Tag, BLOCK_LEN,
19};
20use crate::{aead, cpu, endian::*, error, polyfill};
21use core::convert::TryInto;
22
23pub static CHACHA20_POLY1305: aead::Algorithm = aead::Algorithm {
29 key_len: chacha::KEY_LEN,
30 init: chacha20_poly1305_init,
31 seal: chacha20_poly1305_seal,
32 open: chacha20_poly1305_open,
33 id: aead::AlgorithmID::CHACHA20_POLY1305,
34 max_input_len: super::max_input_len(64, 1),
35};
36
37fn chacha20_poly1305_init(
39 key: &[u8],
40 _todo: cpu::Features,
41) -> Result<aead::KeyInner, error::Unspecified> {
42 let key: [u8; chacha::KEY_LEN] = key.try_into()?;
43 Ok(aead::KeyInner::ChaCha20Poly1305(chacha::Key::from(key)))
44}
45
46fn chacha20_poly1305_seal(
47 key: &aead::KeyInner,
48 nonce: Nonce,
49 aad: Aad<&[u8]>,
50 in_out: &mut [u8],
51 cpu_features: cpu::Features,
52) -> Tag {
53 let key = match key {
54 aead::KeyInner::ChaCha20Poly1305(key) => key,
55 _ => unreachable!(),
56 };
57
58 #[cfg(target_arch = "x86_64")]
59 {
60 if cpu::intel::SSE41.available(cpu_features) {
61 #[repr(align(16), C)]
68 #[derive(Clone, Copy)]
69 struct seal_data_in {
70 key: [u8; chacha::KEY_LEN],
71 counter: u32,
72 nonce: [u8; super::NONCE_LEN],
73 extra_ciphertext: *const u8,
74 extra_ciphertext_len: usize,
75 }
76
77 let mut data = InOut {
78 input: seal_data_in {
79 key: *key.words_less_safe().as_byte_array(),
80 counter: 0,
81 nonce: *nonce.as_ref(),
82 extra_ciphertext: core::ptr::null(),
83 extra_ciphertext_len: 0,
84 },
85 };
86
87 extern "C" {
89 fn GFp_chacha20_poly1305_seal(
90 out_ciphertext: *mut u8,
91 plaintext: *const u8,
92 plaintext_len: usize,
93 ad: *const u8,
94 ad_len: usize,
95 data: &mut InOut<seal_data_in>,
96 );
97 }
98
99 let out = unsafe {
100 GFp_chacha20_poly1305_seal(
101 in_out.as_mut_ptr(),
102 in_out.as_ptr(),
103 in_out.len(),
104 aad.as_ref().as_ptr(),
105 aad.as_ref().len(),
106 &mut data,
107 );
108 &data.out
109 };
110
111 return Tag(out.tag);
112 }
113 }
114
115 aead(key, nonce, aad, in_out, Direction::Sealing, cpu_features)
116}
117
118fn chacha20_poly1305_open(
119 key: &aead::KeyInner,
120 nonce: Nonce,
121 aad: Aad<&[u8]>,
122 in_prefix_len: usize,
123 in_out: &mut [u8],
124 cpu_features: cpu::Features,
125) -> Tag {
126 let key = match key {
127 aead::KeyInner::ChaCha20Poly1305(key) => key,
128 _ => unreachable!(),
129 };
130
131 #[cfg(target_arch = "x86_64")]
132 {
133 if cpu::intel::SSE41.available(cpu_features) {
134 #[derive(Copy, Clone)]
141 #[repr(align(16), C)]
142 struct open_data_in {
143 key: [u8; chacha::KEY_LEN],
144 counter: u32,
145 nonce: [u8; super::NONCE_LEN],
146 }
147
148 let mut data = InOut {
149 input: open_data_in {
150 key: *key.words_less_safe().as_byte_array(),
151 counter: 0,
152 nonce: *nonce.as_ref(),
153 },
154 };
155
156 extern "C" {
158 fn GFp_chacha20_poly1305_open(
159 out_plaintext: *mut u8,
160 ciphertext: *const u8,
161 plaintext_len: usize,
162 ad: *const u8,
163 ad_len: usize,
164 data: &mut InOut<open_data_in>,
165 );
166 }
167
168 let out = unsafe {
169 GFp_chacha20_poly1305_open(
170 in_out.as_mut_ptr(),
171 in_out.as_ptr().add(in_prefix_len),
172 in_out.len() - in_prefix_len,
173 aad.as_ref().as_ptr(),
174 aad.as_ref().len(),
175 &mut data,
176 );
177 &data.out
178 };
179
180 return Tag(out.tag);
181 }
182 }
183
184 aead(
185 key,
186 nonce,
187 aad,
188 in_out,
189 Direction::Opening { in_prefix_len },
190 cpu_features,
191 )
192}
193
194pub type Key = chacha::Key;
195
196#[repr(C)]
199#[cfg(target_arch = "x86_64")]
200union InOut<T>
201where
202 T: Copy,
203{
204 input: T,
205 out: Out,
206}
207
208#[cfg(target_arch = "x86_64")]
213#[derive(Clone, Copy)]
214#[repr(align(16), C)]
215struct Out {
216 tag: [u8; super::TAG_LEN],
217}
218
219#[inline(always)] fn aead(
221 chacha20_key: &Key,
222 nonce: Nonce,
223 Aad(aad): Aad<&[u8]>,
224 in_out: &mut [u8],
225 direction: Direction,
226 cpu_features: cpu::Features,
227) -> Tag {
228 let mut counter = Counter::zero(nonce);
229 let mut ctx = {
230 let key = derive_poly1305_key(chacha20_key, counter.increment(), cpu_features);
231 poly1305::Context::from_key(key)
232 };
233
234 poly1305_update_padded_16(&mut ctx, aad);
235
236 let in_out_len = match direction {
237 Direction::Opening { in_prefix_len } => {
238 poly1305_update_padded_16(&mut ctx, &in_out[in_prefix_len..]);
239 chacha20_key.encrypt_overlapping(counter, in_out, in_prefix_len);
240 in_out.len() - in_prefix_len
241 }
242 Direction::Sealing => {
243 chacha20_key.encrypt_in_place(counter, in_out);
244 poly1305_update_padded_16(&mut ctx, in_out);
245 in_out.len()
246 }
247 };
248
249 ctx.update(
250 Block::from_u64_le(
251 LittleEndian::from(polyfill::u64_from_usize(aad.len())),
252 LittleEndian::from(polyfill::u64_from_usize(in_out_len)),
253 )
254 .as_ref(),
255 );
256 ctx.finish()
257}
258
259#[inline]
260fn poly1305_update_padded_16(ctx: &mut poly1305::Context, input: &[u8]) {
261 let remainder_len = input.len() % BLOCK_LEN;
262 let whole_len = input.len() - remainder_len;
263 if whole_len > 0 {
264 ctx.update(&input[..whole_len]);
265 }
266 if remainder_len > 0 {
267 let mut block = Block::zero();
268 block.overwrite_part_at(0, &input[whole_len..]);
269 ctx.update(block.as_ref())
270 }
271}
272
273pub(super) fn derive_poly1305_key(
275 chacha_key: &chacha::Key,
276 iv: Iv,
277 cpu_features: cpu::Features,
278) -> poly1305::Key {
279 let mut key_bytes = [0u8; 2 * BLOCK_LEN];
280 chacha_key.encrypt_iv_xor_blocks_in_place(iv, &mut key_bytes);
281 poly1305::Key::new(key_bytes, cpu_features)
282}
283
284#[cfg(test)]
285mod tests {
286 #[test]
287 fn max_input_len_test() {
288 assert_eq!(super::CHACHA20_POLY1305.max_input_len, 274_877_906_880u64);
290 }
291}