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 // This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5 // or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7 // You may not use this file except in accordance with one or both of these
10 // This is a port of Andrew Moons poly1305-donna
11 // https://github.com/floodyberry/poly1305-donna
15 use super::super::chacha20::ChaCha20;
16 use super::super::poly1305::Poly1305;
17 use super::super::fixed_time_eq;
19 #[derive(Clone, Copy)]
20 pub struct ChaCha20Poly1305RFC {
28 impl ChaCha20Poly1305RFC {
30 fn pad_mac_16(mac: &mut Poly1305, len: usize) {
32 mac.input(&[0; 16][0..16 - (len % 16)]);
35 pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
36 assert!(key.len() == 16 || key.len() == 32);
37 assert!(nonce.len() == 12);
39 // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
40 assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
42 let mut cipher = ChaCha20::new(key, &nonce[4..]);
43 let mut mac_key = [0u8; 64];
44 let zero_key = [0u8; 64];
45 cipher.process(&zero_key, &mut mac_key);
47 let mut mac = Poly1305::new(&mac_key[..32]);
49 ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());
56 aad_len: aad.len() as u64,
60 pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
61 assert!(input.len() == output.len());
62 assert!(self.finished == false);
63 self.cipher.process(input, output);
64 self.data_len += input.len();
65 self.mac.input(output);
66 ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
68 self.mac.input(&self.aad_len.to_le_bytes());
69 self.mac.input(&(self.data_len as u64).to_le_bytes());
70 self.mac.raw_result(out_tag);
73 pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) {
74 self.encrypt_in_place(input_output);
75 self.finish_and_get_tag(out_tag);
78 // Encrypt `input_output` in-place. To finish and calculate the tag, use `finish_and_get_tag`
80 pub(in super::super) fn encrypt_in_place(&mut self, input_output: &mut [u8]) {
81 debug_assert!(self.finished == false);
82 self.cipher.process_in_place(input_output);
83 self.data_len += input_output.len();
84 self.mac.input(input_output);
87 // If we were previously encrypting with `encrypt_in_place`, this method can be used to finish
88 // encrypting and calculate the tag.
89 pub(in super::super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) {
90 debug_assert!(self.finished == false);
91 ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
93 self.mac.input(&self.aad_len.to_le_bytes());
94 self.mac.input(&(self.data_len as u64).to_le_bytes());
95 self.mac.raw_result(out_tag);
98 /// Decrypt the `input`, checking the given `tag` prior to writing the decrypted contents
99 /// into `output`. Note that, because `output` is not touched until the `tag` is checked,
100 /// this decryption is *variable time*.
101 pub fn variable_time_decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
102 assert!(input.len() == output.len());
103 assert!(self.finished == false);
105 self.finished = true;
107 self.mac.input(input);
109 self.data_len += input.len();
110 ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
111 self.mac.input(&self.aad_len.to_le_bytes());
112 self.mac.input(&(self.data_len as u64).to_le_bytes());
114 let mut calc_tag = [0u8; 16];
115 self.mac.raw_result(&mut calc_tag);
116 if fixed_time_eq(&calc_tag, tag) {
117 self.cipher.process(input, output);
124 pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
125 self.decrypt_in_place(input_output);
126 if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) }
129 /// Decrypt in place, without checking the tag. Use `finish_and_check_tag` to check it
130 /// later when decryption finishes.
132 /// Should never be `pub` because the public API should always enforce tag checking.
133 pub(in super::super) fn decrypt_in_place(&mut self, input_output: &mut [u8]) {
134 debug_assert!(self.finished == false);
135 self.mac.input(input_output);
136 self.data_len += input_output.len();
137 self.cipher.process_in_place(input_output);
140 /// If we were previously decrypting with `just_decrypt_in_place`, this method must be used
141 /// to check the tag. Returns whether or not the tag is valid.
142 pub(in super::super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
143 debug_assert!(self.finished == false);
144 self.finished = true;
145 ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
146 self.mac.input(&self.aad_len.to_le_bytes());
147 self.mac.input(&(self.data_len as u64).to_le_bytes());
149 let mut calc_tag = [0u8; 16];
150 self.mac.raw_result(&mut calc_tag);
151 if fixed_time_eq(&calc_tag, tag) {
160 pub use self::real_chachapoly::ChaCha20Poly1305RFC;
163 mod fuzzy_chachapoly {
164 #[derive(Clone, Copy)]
165 pub struct ChaCha20Poly1305RFC {
169 impl ChaCha20Poly1305RFC {
170 pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC {
171 assert!(key.len() == 16 || key.len() == 32);
172 assert!(nonce.len() == 12);
174 // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
175 assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
177 let mut tag = [0; 16];
178 tag.copy_from_slice(&key[0..16]);
180 ChaCha20Poly1305RFC {
186 pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
187 assert!(input.len() == output.len());
188 assert!(self.finished == false);
190 output.copy_from_slice(&input);
191 out_tag.copy_from_slice(&self.tag);
192 self.finished = true;
195 pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) {
196 self.encrypt_in_place(input_output);
197 self.finish_and_get_tag(out_tag);
200 pub(in super::super) fn encrypt_in_place(&mut self, _input_output: &mut [u8]) {
201 assert!(self.finished == false);
204 pub(in super::super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) {
205 assert!(self.finished == false);
206 out_tag.copy_from_slice(&self.tag);
207 self.finished = true;
210 pub fn variable_time_decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
211 assert!(input.len() == output.len());
212 assert!(self.finished == false);
214 if tag[..] != self.tag[..] { return Err(()); }
215 output.copy_from_slice(input);
216 self.finished = true;
220 pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
221 self.decrypt_in_place(input_output);
222 if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) }
225 pub(in super::super) fn decrypt_in_place(&mut self, _input: &mut [u8]) {
226 assert!(self.finished == false);
229 pub(in super::super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
230 if tag[..] != self.tag[..] { return false; }
231 self.finished = true;
237 pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC;