|
10 | 10 | // This is a port of Andrew Moons poly1305-donna
|
11 | 11 | // https://github.com/floodyberry/poly1305-donna
|
12 | 12 |
|
| 13 | +use util::ser::{Writeable, Writer}; |
| 14 | +use io::{self, Write}; |
| 15 | + |
13 | 16 | #[cfg(not(fuzzing))]
|
14 | 17 | mod real_chachapoly {
|
15 | 18 | use util::chacha20::ChaCha20;
|
@@ -58,11 +61,32 @@ mod real_chachapoly {
|
58 | 61 | }
|
59 | 62 |
|
60 | 63 | 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 | 64 | self.cipher.process(input, output);
|
| 65 | + self.encrypt_inner(input, Some(output), Some(out_tag)); |
| 66 | + } |
| 67 | + |
| 68 | + pub fn encrypt_in_place(&mut self, input_output: &mut [u8], out_tag: Option<&mut [u8]>) { |
| 69 | + self.cipher.process_in_place(input_output); |
| 70 | + self.encrypt_inner(input_output, None, out_tag); |
| 71 | + } |
| 72 | + |
| 73 | + // Encrypt in place, and fill in the tag if it's provided. If the tag is not provided, then |
| 74 | + // `finish_and_get_tag` may be called to check it later. |
| 75 | + fn encrypt_inner(&mut self, input: &[u8], output: Option<&mut [u8]>, out_tag: Option<&mut [u8]>) { |
| 76 | + assert!(self.finished == false); |
| 77 | + if let Some(output) = output { |
| 78 | + assert!(input.len() == output.len()); |
| 79 | + self.mac.input(output); |
| 80 | + } else { |
| 81 | + self.mac.input(input); |
| 82 | + } |
64 | 83 | self.data_len += input.len();
|
65 |
| - self.mac.input(output); |
| 84 | + if let Some(tag) = out_tag { |
| 85 | + self.finish_and_get_tag(tag); |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + pub fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) { |
66 | 90 | ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
|
67 | 91 | self.finished = true;
|
68 | 92 | self.mac.input(&self.aad_len.to_le_bytes());
|
@@ -97,6 +121,49 @@ mod real_chachapoly {
|
97 | 121 | #[cfg(not(fuzzing))]
|
98 | 122 | pub use self::real_chachapoly::ChaCha20Poly1305RFC;
|
99 | 123 |
|
| 124 | +pub(crate) struct ChaChaPolyWriter<'a, W: Writer> { |
| 125 | + pub chacha: &'a mut ChaCha20Poly1305RFC, |
| 126 | + pub write: &'a mut W, |
| 127 | +} |
| 128 | + |
| 129 | +impl<'a, W: Writer> Writer for ChaChaPolyWriter<'a, W> { |
| 130 | + fn write_all(&mut self, src: &[u8]) -> Result<(), io::Error> { |
| 131 | + let num_writes = (src.len() + (8192 - 1)) / 8192; |
| 132 | + for i in 0..num_writes { |
| 133 | + let mut write_buffer = [0; 8192]; |
| 134 | + let bytes_written = (&mut write_buffer[..]).write(&src[i * 8192..])?; |
| 135 | + self.chacha.encrypt_in_place(&mut write_buffer[..bytes_written], None); |
| 136 | + self.write.write_all(&write_buffer[..bytes_written])?; |
| 137 | + } |
| 138 | + Ok(()) |
| 139 | + } |
| 140 | +} |
| 141 | + |
| 142 | +pub(crate) struct ChaChaPolyWriteAdapter<'a, W: Writeable> { |
| 143 | + pub rho: [u8; 32], |
| 144 | + pub writeable: &'a W, |
| 145 | +} |
| 146 | + |
| 147 | +impl<'a, W: Writeable> ChaChaPolyWriteAdapter<'a, W> { |
| 148 | + #[allow(unused)] // This will be used for onion messages soon |
| 149 | + pub fn new(rho: [u8; 32], writeable: &'a W) -> ChaChaPolyWriteAdapter<'a, W> { |
| 150 | + Self { rho, writeable } |
| 151 | + } |
| 152 | +} |
| 153 | + |
| 154 | +impl<'a, T: Writeable> Writeable for ChaChaPolyWriteAdapter<'a, T> { |
| 155 | + fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> { |
| 156 | + let mut chacha = ChaCha20Poly1305RFC::new(&self.rho, &[0; 12], &[]); |
| 157 | + let mut chacha_stream = ChaChaPolyWriter { chacha: &mut chacha, write: w }; |
| 158 | + self.writeable.write(&mut chacha_stream)?; |
| 159 | + let mut tag = [0 as u8; 16]; |
| 160 | + chacha.finish_and_get_tag(&mut tag); |
| 161 | + tag.write(w)?; |
| 162 | + |
| 163 | + Ok(()) |
| 164 | + } |
| 165 | +} |
| 166 | + |
100 | 167 | #[cfg(fuzzing)]
|
101 | 168 | mod fuzzy_chachapoly {
|
102 | 169 | #[derive(Clone, Copy)]
|
@@ -130,6 +197,19 @@ mod fuzzy_chachapoly {
|
130 | 197 | self.finished = true;
|
131 | 198 | }
|
132 | 199 |
|
| 200 | + pub fn encrypt_in_place(&mut self, _input_output: &mut [u8], out_tag: Option<&mut [u8]>) { |
| 201 | + assert!(self.finished == false); |
| 202 | + if let Some(tag) = out_tag { |
| 203 | + tag.copy_from_slice(&self.tag); |
| 204 | + self.finished = true; |
| 205 | + } |
| 206 | + } |
| 207 | + |
| 208 | + pub fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) { |
| 209 | + out_tag.copy_from_slice(&self.tag); |
| 210 | + self.finished = true; |
| 211 | + } |
| 212 | + |
133 | 213 | pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
|
134 | 214 | assert!(input.len() == output.len());
|
135 | 215 | assert!(self.finished == false);
|
|
0 commit comments