initial checkin
[rust-lightning] / src / util / chacha20poly1305rfc.rs
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.
3
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.
9
10 // This is a port of Andrew Moons poly1305-donna
11 // https://github.com/floodyberry/poly1305-donna
12
13 use crypto::aead::{AeadEncryptor,AeadDecryptor};
14 use crypto::chacha20::ChaCha20;
15 use crypto::symmetriccipher::SynchronousStreamCipher;
16 use crypto::poly1305::Poly1305;
17 use crypto::mac::Mac;
18 use crypto::util::fixed_time_eq;
19
20 use util::byte_utils;
21
22 #[derive(Clone, Copy)]
23 pub struct ChaCha20Poly1305RFC {
24     cipher  : ChaCha20,
25     mac: Poly1305,
26     finished: bool,
27     data_len: usize,
28     aad_len: u64,
29 }
30
31 impl ChaCha20Poly1305RFC {
32   #[inline]
33   fn pad_mac_16(mac: &mut Poly1305, len: usize) {
34       if len % 16 != 0 {
35         mac.input(&[0; 16][0..16 - (len % 16)]);
36       }
37   }
38   pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
39       assert!(key.len() == 16 || key.len() == 32);
40       assert!(nonce.len() == 12);
41
42       // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
43       assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
44
45       let mut cipher = ChaCha20::new(key, &nonce[4..]);
46       let mut mac_key = [0u8; 64];
47       let zero_key = [0u8; 64];
48       cipher.process(&zero_key, &mut mac_key);
49
50       let mut mac = Poly1305::new(&mac_key[..32]);
51       mac.input(aad);
52       ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());
53
54       ChaCha20Poly1305RFC {
55         cipher: cipher,
56         mac: mac,
57         finished: false,
58         data_len: 0,
59         aad_len: aad.len() as u64,
60       }
61   }
62 }
63
64 impl AeadEncryptor for ChaCha20Poly1305RFC {
65     fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
66         assert!(input.len() == output.len());
67         assert!(self.finished == false);
68         self.cipher.process(input, output);
69         self.data_len += input.len();
70         self.mac.input(output);
71         ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
72         self.finished = true;
73         self.mac.input(&byte_utils::le64_to_array(self.aad_len));
74         self.mac.input(&byte_utils::le64_to_array(self.data_len as u64));
75         self.mac.raw_result(out_tag);
76     }
77 }
78
79 impl AeadDecryptor for ChaCha20Poly1305RFC {
80     fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
81         assert!(input.len() == output.len());
82         assert!(self.finished == false);
83
84         self.finished = true;
85
86         self.mac.input(input);
87
88         self.data_len += input.len();
89         ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
90         self.mac.input(&byte_utils::le64_to_array(self.aad_len));
91         self.mac.input(&byte_utils::le64_to_array(self.data_len as u64));
92
93         let mut calc_tag =  [0u8; 16];
94         self.mac.raw_result(&mut calc_tag);
95         if fixed_time_eq(&calc_tag, tag) {
96             self.cipher.process(input, output);
97             true
98         } else {
99             false
100         }
101     }
102 }