Drop rust-crypto trait usage
[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 #[cfg(not(feature = "fuzztarget"))]
14 mod real_chachapoly {
15         use util::chacha20::ChaCha20;
16         use util::poly1305::Poly1305;
17
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                 pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
64                         assert!(input.len() == output.len());
65                         assert!(self.finished == false);
66                         self.cipher.process(input, output);
67                         self.data_len += input.len();
68                         self.mac.input(output);
69                         ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
70                         self.finished = true;
71                         self.mac.input(&byte_utils::le64_to_array(self.aad_len));
72                         self.mac.input(&byte_utils::le64_to_array(self.data_len as u64));
73                         self.mac.raw_result(out_tag);
74                 }
75
76                 pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
77                         assert!(input.len() == output.len());
78                         assert!(self.finished == false);
79
80                         self.finished = true;
81
82                         self.mac.input(input);
83
84                         self.data_len += input.len();
85                         ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
86                         self.mac.input(&byte_utils::le64_to_array(self.aad_len));
87                         self.mac.input(&byte_utils::le64_to_array(self.data_len as u64));
88
89                         let mut calc_tag =  [0u8; 16];
90                         self.mac.raw_result(&mut calc_tag);
91                         if fixed_time_eq(&calc_tag, tag) {
92                                 self.cipher.process(input, output);
93                                 true
94                         } else {
95                                 false
96                         }
97                 }
98         }
99 }
100 #[cfg(not(feature = "fuzztarget"))]
101 pub use self::real_chachapoly::ChaCha20Poly1305RFC;
102
103 #[cfg(feature = "fuzztarget")]
104 mod fuzzy_chachapoly {
105         #[derive(Clone, Copy)]
106         pub struct ChaCha20Poly1305RFC {
107                 tag: [u8; 16],
108                 finished: bool,
109         }
110         impl ChaCha20Poly1305RFC {
111                 pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC {
112                         assert!(key.len() == 16 || key.len() == 32);
113                         assert!(nonce.len() == 12);
114
115                         // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
116                         assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
117
118                         let mut tag = [0; 16];
119                         tag.copy_from_slice(&key[0..16]);
120
121                         ChaCha20Poly1305RFC {
122                                 tag,
123                                 finished: false,
124                         }
125                 }
126
127                 pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
128                         assert!(input.len() == output.len());
129                         assert!(self.finished == false);
130
131                         output.copy_from_slice(&input);
132                         out_tag.copy_from_slice(&self.tag);
133                         self.finished = true;
134                 }
135
136                 pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
137                         assert!(input.len() == output.len());
138                         assert!(self.finished == false);
139
140                         if tag[..] != self.tag[..] { return false; }
141                         output.copy_from_slice(input);
142                         self.finished = true;
143                         true
144                 }
145         }
146 }
147 #[cfg(feature = "fuzztarget")]
148 pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC;