1use crate::chacha20::ChaCha;
71use crate::constant_time::{Choice, CtEqual};
72use crate::cryptoutil::write_u64_le;
73use crate::mac::Mac;
74use crate::poly1305::Poly1305;
75use core::convert::TryFrom;
76
77#[derive(Clone)]
106pub struct Context<const ROUNDS: usize> {
107 cipher: ChaCha<ROUNDS>,
108 mac: Poly1305,
109 aad_len: u64,
110 data_len: u64,
111}
112
113#[derive(Clone)]
115pub struct ContextEncryption<const ROUNDS: usize>(Context<ROUNDS>);
116
117#[derive(Clone)]
119pub struct ContextDecryption<const ROUNDS: usize>(Context<ROUNDS>);
120
121#[derive(Debug, Clone)]
123pub struct Tag(pub [u8; 16]);
124
125impl CtEqual for &Tag {
126 fn ct_eq(self, other: Self) -> Choice {
127 self.0.ct_eq(&other.0)
128 }
129 fn ct_ne(self, b: Self) -> Choice {
130 self.ct_eq(b).negate()
131 }
132}
133
134impl PartialEq for Tag {
135 fn eq(&self, other: &Self) -> bool {
136 self.ct_eq(other).is_true()
137 }
138}
139
140impl Eq for Tag {}
141
142impl<const ROUNDS: usize> Context<ROUNDS> {
143 pub fn new(key: &[u8], nonce: &[u8; 12]) -> Self {
153 assert!(key.len() == 16 || key.len() == 32);
154 let mut cipher = ChaCha::new(key, nonce);
155 let mut mac_key = [0u8; 64];
156 let zero_key = [0u8; 64];
157 cipher.process(&zero_key, &mut mac_key);
158
159 let mac = Poly1305::new(<&[u8; 32]>::try_from(&mac_key[..32]).unwrap());
160 Context {
161 cipher: cipher,
162 mac: mac,
163 aad_len: 0,
164 data_len: 0,
165 }
166 }
167
168 fn add_encrypted(&mut self, encrypted: &[u8]) {
169 self.mac.input(encrypted);
170 self.data_len += encrypted.len() as u64;
171 }
172
173 pub fn add_data(&mut self, aad: &[u8]) {
177 self.aad_len += aad.len() as u64;
178 self.mac.input(aad);
179 }
180
181 pub fn to_encryption(mut self) -> ContextEncryption<ROUNDS> {
183 pad16(&mut self.mac, self.aad_len);
184 ContextEncryption(self)
185 }
186
187 pub fn to_decryption(mut self) -> ContextDecryption<ROUNDS> {
189 pad16(&mut self.mac, self.aad_len);
190 ContextDecryption(self)
191 }
192}
193
194fn finalize_raw<const ROUNDS: usize>(inner: &mut Context<ROUNDS>) -> [u8; 16] {
195 let mut len_buf = [0u8; 16];
196 pad16(&mut inner.mac, inner.data_len);
197 write_u64_le(&mut len_buf[0..8], inner.aad_len);
198 write_u64_le(&mut len_buf[8..16], inner.data_len);
199 inner.mac.input(&len_buf);
200 inner.mac.raw_result(&mut len_buf);
201 len_buf
202}
203
204impl<const ROUNDS: usize> ContextEncryption<ROUNDS> {
205 pub fn encrypt_mut(&mut self, buf: &mut [u8]) {
207 self.0.cipher.process_mut(buf);
208 self.0.add_encrypted(buf);
209 }
210
211 pub fn encrypt(&mut self, input: &[u8], output: &mut [u8]) {
219 assert_eq!(input.len(), output.len());
220 self.0.cipher.process(input, output);
221 self.0.add_encrypted(output);
222 }
223
224 #[must_use]
226 pub fn finalize(mut self) -> Tag {
227 let tag = finalize_raw(&mut self.0);
228 Tag(tag)
229 }
230}
231
232#[derive(Debug, Clone, PartialEq, Eq)]
234pub enum DecryptionResult {
235 Match,
237 MisMatch,
239}
240
241impl<const ROUNDS: usize> ContextDecryption<ROUNDS> {
242 pub fn decrypt_mut(&mut self, buf: &mut [u8]) {
244 self.0.add_encrypted(buf);
245 self.0.cipher.process_mut(buf);
246 }
247
248 pub fn decrypt(&mut self, input: &[u8], output: &mut [u8]) {
253 assert_eq!(input.len(), output.len());
254 self.0.add_encrypted(input);
255 self.0.cipher.process(input, output);
256 }
257
258 #[must_use = "if the result is not checked, then the data will not be verified against tempering"]
261 pub fn finalize(mut self, expected_tag: &Tag) -> DecryptionResult {
262 let got_tag = Tag(finalize_raw(&mut self.0));
263 if &got_tag == expected_tag {
264 DecryptionResult::Match
265 } else {
266 DecryptionResult::MisMatch
267 }
268 }
269}
270
271#[derive(Clone)]
273pub struct ChaChaPoly1305<const ROUNDS: usize> {
274 finished: bool,
275 context: Context<ROUNDS>,
276}
277
278fn pad16(mac: &mut Poly1305, len: u64) {
279 if (len % 16) != 0 {
280 let padding = [0u8; 15];
281 let sz = 16 - (len % 16) as usize;
282 mac.input(&padding[0..sz]);
283 }
284}
285
286pub type ChaCha20Poly1305 = ChaChaPoly1305<20>;
288
289impl<const ROUNDS: usize> ChaChaPoly1305<ROUNDS> {
290 pub fn new(key: &[u8], nonce: &[u8; 12], aad: &[u8]) -> Self {
296 let mut context = Context::new(key, nonce);
297 context.add_data(aad);
298 ChaChaPoly1305 {
299 context: context,
300 finished: false,
301 }
302 }
303
304 pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
331 assert!(input.len() == output.len());
332 assert!(!self.finished);
333 assert!(out_tag.len() == 16);
334
335 self.finished = true;
336
337 let mut ctx = self.context.clone().to_encryption();
338 ctx.encrypt(input, output);
339
340 let Tag(tag) = ctx.finalize();
341 out_tag.copy_from_slice(&tag[..])
342 }
343
344 pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
374 assert!(tag.len() == 16);
375 assert!(input.len() == output.len());
376 assert!(!self.finished);
377
378 self.finished = true;
379
380 let mut tag_data = [0u8; 16];
381 tag_data.copy_from_slice(tag);
382
383 let mut ctx = self.context.clone().to_decryption();
384
385 ctx.decrypt(input, output);
386 ctx.finalize(&Tag(tag_data)) == DecryptionResult::Match
387 }
388}
389
390#[cfg(test)]
391mod test {
392 use super::ChaCha20Poly1305;
393
394 struct TestVector {
395 key: [u8; 32],
396 nonce: [u8; 12],
397 tag: [u8; 16],
398 plain_text: &'static [u8],
399 cipher_text: &'static [u8],
400 aad: &'static [u8],
401 }
402
403 fn test_vector(v: &TestVector) {
404 let mut tag = [0u8; 16];
405 let mut ciphertext = vec![0u8; v.cipher_text.len()];
406
407 let mut context = ChaCha20Poly1305::new(&v.key, &v.nonce, &v.aad);
408 let mut dcontext = context.clone();
409
410 context.encrypt(&v.plain_text, &mut ciphertext, &mut tag[..]);
412
413 assert_eq!(&ciphertext[..], &v.cipher_text[..]);
414 assert_eq!(&tag[..], &v.tag[..]);
415
416 let mut output = vec![0u8; v.plain_text.len()];
418 assert_eq!(dcontext.decrypt(&ciphertext, &mut output, &v.tag[..]), true);
419
420 assert_eq!(&output[..], &v.plain_text[..]);
421 }
422
423 #[test]
424 fn test_vectors() {
425 let tests = [
426 TestVector {
428 key: [
429 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c,
430 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
431 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
432 ],
433 nonce: [
434 0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
435 ],
436 plain_text: &[
437 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65,
438 0x6e, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
439 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39,
440 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63, 0x6f, 0x75, 0x6c, 0x64,
441 0x20, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f, 0x6e,
442 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x74, 0x69, 0x70, 0x20, 0x66, 0x6f,
443 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x2c,
444 0x20, 0x73, 0x75, 0x6e, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f,
445 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, 0x74, 0x2e,
446 ],
447 aad: &[
448 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
449 ],
450 cipher_text: &[
451 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53,
452 0xef, 0x7e, 0xc2, 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe, 0xa9, 0xe2,
453 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67,
454 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, 0x1a, 0x71, 0xde, 0x0a,
455 0x9e, 0x06, 0x0b, 0x29, 0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36, 0x92,
456 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, 0x98, 0x03, 0xae, 0xe3, 0x28, 0x09,
457 0x1b, 0x58, 0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, 0x80,
458 0x8b, 0x48, 0x31, 0xd7, 0xbc, 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d,
459 0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b, 0x61, 0x16,
460 ],
461 tag: [
462 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0,
463 0x60, 0x06, 0x91,
464 ],
465 },
466 TestVector {
468 key: [
469 0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a, 0xf3, 0x33, 0x88, 0x86, 0x04,
470 0xf6, 0xb5, 0xf0, 0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09, 0x9d, 0xca,
471 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0,
472 ],
473 nonce: [
474 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
475 ],
476 tag: [
477 0xee, 0xad, 0x9d, 0x67, 0x89, 0x0c, 0xbb, 0x22, 0x39, 0x23, 0x36, 0xfe, 0xa1,
478 0x85, 0x1f, 0x38,
479 ],
480 plain_text: &[
481 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2d, 0x44, 0x72, 0x61, 0x66,
482 0x74, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x72, 0x61, 0x66, 0x74, 0x20,
483 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x76, 0x61, 0x6c,
484 0x69, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x6d, 0x61, 0x78, 0x69,
485 0x6d, 0x75, 0x6d, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x69, 0x78, 0x20, 0x6d, 0x6f,
486 0x6e, 0x74, 0x68, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x61, 0x79, 0x20,
487 0x62, 0x65, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x72,
488 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x6f,
489 0x62, 0x73, 0x6f, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x6f,
490 0x74, 0x68, 0x65, 0x72, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
491 0x73, 0x20, 0x61, 0x74, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x74, 0x69, 0x6d, 0x65,
492 0x2e, 0x20, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x61, 0x70, 0x70,
493 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x75,
494 0x73, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2d, 0x44,
495 0x72, 0x61, 0x66, 0x74, 0x73, 0x20, 0x61, 0x73, 0x20, 0x72, 0x65, 0x66, 0x65,
496 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61,
497 0x6c, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x69, 0x74, 0x65, 0x20,
498 0x74, 0x68, 0x65, 0x6d, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68,
499 0x61, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x2f, 0xe2, 0x80, 0x9c, 0x77, 0x6f, 0x72,
500 0x6b, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73,
501 0x2e, 0x2f, 0xe2, 0x80, 0x9d,
502 ],
503 cipher_text: &[
504 0x64, 0xa0, 0x86, 0x15, 0x75, 0x86, 0x1a, 0xf4, 0x60, 0xf0, 0x62, 0xc7, 0x9b,
505 0xe6, 0x43, 0xbd, 0x5e, 0x80, 0x5c, 0xfd, 0x34, 0x5c, 0xf3, 0x89, 0xf1, 0x08,
506 0x67, 0x0a, 0xc7, 0x6c, 0x8c, 0xb2, 0x4c, 0x6c, 0xfc, 0x18, 0x75, 0x5d, 0x43,
507 0xee, 0xa0, 0x9e, 0xe9, 0x4e, 0x38, 0x2d, 0x26, 0xb0, 0xbd, 0xb7, 0xb7, 0x3c,
508 0x32, 0x1b, 0x01, 0x00, 0xd4, 0xf0, 0x3b, 0x7f, 0x35, 0x58, 0x94, 0xcf, 0x33,
509 0x2f, 0x83, 0x0e, 0x71, 0x0b, 0x97, 0xce, 0x98, 0xc8, 0xa8, 0x4a, 0xbd, 0x0b,
510 0x94, 0x81, 0x14, 0xad, 0x17, 0x6e, 0x00, 0x8d, 0x33, 0xbd, 0x60, 0xf9, 0x82,
511 0xb1, 0xff, 0x37, 0xc8, 0x55, 0x97, 0x97, 0xa0, 0x6e, 0xf4, 0xf0, 0xef, 0x61,
512 0xc1, 0x86, 0x32, 0x4e, 0x2b, 0x35, 0x06, 0x38, 0x36, 0x06, 0x90, 0x7b, 0x6a,
513 0x7c, 0x02, 0xb0, 0xf9, 0xf6, 0x15, 0x7b, 0x53, 0xc8, 0x67, 0xe4, 0xb9, 0x16,
514 0x6c, 0x76, 0x7b, 0x80, 0x4d, 0x46, 0xa5, 0x9b, 0x52, 0x16, 0xcd, 0xe7, 0xa4,
515 0xe9, 0x90, 0x40, 0xc5, 0xa4, 0x04, 0x33, 0x22, 0x5e, 0xe2, 0x82, 0xa1, 0xb0,
516 0xa0, 0x6c, 0x52, 0x3e, 0xaf, 0x45, 0x34, 0xd7, 0xf8, 0x3f, 0xa1, 0x15, 0x5b,
517 0x00, 0x47, 0x71, 0x8c, 0xbc, 0x54, 0x6a, 0x0d, 0x07, 0x2b, 0x04, 0xb3, 0x56,
518 0x4e, 0xea, 0x1b, 0x42, 0x22, 0x73, 0xf5, 0x48, 0x27, 0x1a, 0x0b, 0xb2, 0x31,
519 0x60, 0x53, 0xfa, 0x76, 0x99, 0x19, 0x55, 0xeb, 0xd6, 0x31, 0x59, 0x43, 0x4e,
520 0xce, 0xbb, 0x4e, 0x46, 0x6d, 0xae, 0x5a, 0x10, 0x73, 0xa6, 0x72, 0x76, 0x27,
521 0x09, 0x7a, 0x10, 0x49, 0xe6, 0x17, 0xd9, 0x1d, 0x36, 0x10, 0x94, 0xfa, 0x68,
522 0xf0, 0xff, 0x77, 0x98, 0x71, 0x30, 0x30, 0x5b, 0xea, 0xba, 0x2e, 0xda, 0x04,
523 0xdf, 0x99, 0x7b, 0x71, 0x4d, 0x6c, 0x6f, 0x2c, 0x29, 0xa6, 0xad, 0x5c, 0xb4,
524 0x02, 0x2b, 0x02, 0x70, 0x9b,
525 ],
526 aad: &[
527 0xf3, 0x33, 0x88, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x91,
528 ],
529 },
530 ];
531 for tv in tests.iter() {
532 test_vector(&tv)
533 }
534 }
535}
536
537#[cfg(all(test, feature = "with-bench"))]
538mod bench {
539 use super::ChaCha20Poly1305;
540 use test::Bencher;
541
542 #[bench]
543 pub fn chacha20poly1305_10(bh: &mut Bencher) {
544 let input = [1u8; 10];
545 let aad = [3u8; 10];
546 bh.iter(|| {
547 let mut cipher = ChaCha20Poly1305::new(&[0; 32], &[0; 12], &aad);
548 let mut decipher = ChaCha20Poly1305::new(&[0; 32], &[0; 12], &aad);
549
550 let mut output = [0u8; 10];
551 let mut tag = [0u8; 16];
552 let mut output2 = [0u8; 10];
553 cipher.encrypt(&input, &mut output, &mut tag);
554 decipher.decrypt(&output, &mut output2, &tag);
555 });
556 bh.bytes = 10u64;
557 }
558
559 #[bench]
560 pub fn chacha20poly1305_1k(bh: &mut Bencher) {
561 let input = [1u8; 1024];
562 let aad = [3u8; 1024];
563 bh.iter(|| {
564 let mut cipher = ChaCha20Poly1305::new(&[0; 32], &[0; 12], &aad);
565 let mut decipher = ChaCha20Poly1305::new(&[0; 32], &[0; 12], &aad);
566
567 let mut output = [0u8; 1024];
568 let mut tag = [0u8; 16];
569 let mut output2 = [0u8; 1024];
570
571 cipher.encrypt(&input, &mut output, &mut tag);
572 decipher.decrypt(&output, &mut output2, &tag);
573 });
574 bh.bytes = 1024u64;
575 }
576
577 #[bench]
578 pub fn chacha20poly1305_64k(bh: &mut Bencher) {
579 let input = [1u8; 65536];
580 let aad = [3u8; 65536];
581 bh.iter(|| {
582 let mut cipher = ChaCha20Poly1305::new(&[0; 32], &[0; 12], &aad);
583 let mut decipher = ChaCha20Poly1305::new(&[0; 32], &[0; 12], &aad);
584
585 let mut output = [0u8; 65536];
586 let mut tag = [0u8; 16];
587 let mut output2 = [0u8; 65536];
588
589 cipher.encrypt(&input, &mut output, &mut tag);
590 decipher.decrypt(&output, &mut output2, &tag);
591 });
592 bh.bytes = 65536u64;
593 }
594}