1 // ring has a garbage API so its use is avoided, but rust-crypto doesn't have RFC-variant poly1305
2 // Instead, we steal rust-crypto's implementation and tweak it to match the RFC.
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
10 // This is a port of Andrew Moons poly1305-donna
11 // https://github.com/floodyberry/poly1305-donna
13 #[cfg(not(feature = "fuzztarget"))]
15 use crypto::aead::{AeadEncryptor,AeadDecryptor};
16 use crypto::symmetriccipher::SynchronousStreamCipher;
17 use crypto::poly1305::Poly1305;
19 use crypto::util::fixed_time_eq;
21 pub use crypto::chacha20::ChaCha20;
25 #[derive(Clone, Copy)]
26 pub struct ChaCha20Poly1305RFC {
34 impl ChaCha20Poly1305RFC {
36 fn pad_mac_16(mac: &mut Poly1305, len: usize) {
38 mac.input(&[0; 16][0..16 - (len % 16)]);
41 pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
42 assert!(key.len() == 16 || key.len() == 32);
43 assert!(nonce.len() == 12);
45 // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
46 assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
48 let mut cipher = ChaCha20::new(key, &nonce[4..]);
49 let mut mac_key = [0u8; 64];
50 let zero_key = [0u8; 64];
51 cipher.process(&zero_key, &mut mac_key);
53 let mut mac = Poly1305::new(&mac_key[..32]);
55 ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());
62 aad_len: aad.len() as u64,
67 impl AeadEncryptor for ChaCha20Poly1305RFC {
68 fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
69 assert!(input.len() == output.len());
70 assert!(self.finished == false);
71 self.cipher.process(input, output);
72 self.data_len += input.len();
73 self.mac.input(output);
74 ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
76 self.mac.input(&byte_utils::le64_to_array(self.aad_len));
77 self.mac.input(&byte_utils::le64_to_array(self.data_len as u64));
78 self.mac.raw_result(out_tag);
82 impl AeadDecryptor for ChaCha20Poly1305RFC {
83 fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
84 assert!(input.len() == output.len());
85 assert!(self.finished == false);
89 self.mac.input(input);
91 self.data_len += input.len();
92 ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
93 self.mac.input(&byte_utils::le64_to_array(self.aad_len));
94 self.mac.input(&byte_utils::le64_to_array(self.data_len as u64));
96 let mut calc_tag = [0u8; 16];
97 self.mac.raw_result(&mut calc_tag);
98 if fixed_time_eq(&calc_tag, tag) {
99 self.cipher.process(input, output);
107 #[cfg(not(feature = "fuzztarget"))]
108 pub use self::real_chachapoly::{ChaCha20Poly1305RFC, ChaCha20};
110 #[cfg(feature = "fuzztarget")]
111 mod fuzzy_chachapoly {
112 use crypto::aead::{AeadEncryptor,AeadDecryptor};
113 use crypto::symmetriccipher::SynchronousStreamCipher;
115 #[derive(Clone, Copy)]
116 pub struct ChaCha20Poly1305RFC {
120 impl ChaCha20Poly1305RFC {
121 pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC {
122 assert!(key.len() == 16 || key.len() == 32);
123 assert!(nonce.len() == 12);
125 // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
126 assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
128 let mut tag = [0; 16];
129 tag.copy_from_slice(&key[0..16]);
131 ChaCha20Poly1305RFC {
138 impl AeadEncryptor for ChaCha20Poly1305RFC {
139 fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
140 assert!(input.len() == output.len());
141 assert!(self.finished == false);
143 output.copy_from_slice(&input);
144 out_tag.copy_from_slice(&self.tag);
145 self.finished = true;
149 impl AeadDecryptor for ChaCha20Poly1305RFC {
150 fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
151 assert!(input.len() == output.len());
152 assert!(self.finished == false);
154 if tag[..] != self.tag[..] { return false; }
155 output.copy_from_slice(input);
156 self.finished = true;
161 pub struct ChaCha20 {}
164 pub fn new(key: &[u8], nonce: &[u8]) -> ChaCha20 {
165 assert!(key.len() == 16 || key.len() == 32);
166 assert!(nonce.len() == 8 || nonce.len() == 12);
171 impl SynchronousStreamCipher for ChaCha20 {
172 fn process(&mut self, input: &[u8], output: &mut [u8]) {
173 output.copy_from_slice(input);
177 #[cfg(feature = "fuzztarget")]
178 pub use self::fuzzy_chachapoly::{ChaCha20Poly1305RFC, ChaCha20};