|
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;
|
@@ -70,6 +73,26 @@ mod real_chachapoly {
|
70 | 73 | self.mac.raw_result(out_tag);
|
71 | 74 | }
|
72 | 75 |
|
| 76 | + // Encrypt `input_output` in-place. To finish and calculate the tag, use `finish_and_get_tag` |
| 77 | + // below. |
| 78 | + pub(super) fn encrypt_in_place(&mut self, input_output: &mut [u8]) { |
| 79 | + debug_assert!(self.finished == false); |
| 80 | + self.cipher.process_in_place(input_output); |
| 81 | + self.data_len += input_output.len(); |
| 82 | + self.mac.input(input_output); |
| 83 | + } |
| 84 | + |
| 85 | + // If we were previously encrypting with `encrypt_in_place`, this method can be used to finish |
| 86 | + // encrypting and calculate the tag. |
| 87 | + pub(super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) { |
| 88 | + debug_assert!(self.finished == false); |
| 89 | + ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len); |
| 90 | + self.finished = true; |
| 91 | + self.mac.input(&self.aad_len.to_le_bytes()); |
| 92 | + self.mac.input(&(self.data_len as u64).to_le_bytes()); |
| 93 | + self.mac.raw_result(out_tag); |
| 94 | + } |
| 95 | + |
73 | 96 | pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
|
74 | 97 | assert!(input.len() == output.len());
|
75 | 98 | assert!(self.finished == false);
|
@@ -97,6 +120,57 @@ mod real_chachapoly {
|
97 | 120 | #[cfg(not(fuzzing))]
|
98 | 121 | pub use self::real_chachapoly::ChaCha20Poly1305RFC;
|
99 | 122 |
|
| 123 | +/// Enables simultaneously writing and encrypting a byte stream into a Writer. |
| 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 | + // Encrypt then write bytes from `src` into Self::write. |
| 131 | + // `ChaCha20Poly1305RFC::finish_and_get_tag` can be called to retrieve the tag after all writes |
| 132 | + // complete. |
| 133 | + fn write_all(&mut self, src: &[u8]) -> Result<(), io::Error> { |
| 134 | + let mut src_idx = 0; |
| 135 | + while src_idx < src.len() { |
| 136 | + let mut write_buffer = [0; 8192]; |
| 137 | + let bytes_written = (&mut write_buffer[..]).write(&src[src_idx..]).expect("In-memory writes can't fail"); |
| 138 | + self.chacha.encrypt_in_place(&mut write_buffer[..bytes_written]); |
| 139 | + self.write.write_all(&write_buffer[..bytes_written])?; |
| 140 | + src_idx += bytes_written; |
| 141 | + } |
| 142 | + Ok(()) |
| 143 | + } |
| 144 | +} |
| 145 | + |
| 146 | +/// Enables the use of the serialization macros for objects that need to be simultaneously encrypted and |
| 147 | +/// serialized. This allows us to avoid an intermediate Vec allocation. |
| 148 | +pub(crate) struct ChaChaPolyWriteAdapter<'a, W: Writeable> { |
| 149 | + pub rho: [u8; 32], |
| 150 | + pub writeable: &'a W, |
| 151 | +} |
| 152 | + |
| 153 | +impl<'a, W: Writeable> ChaChaPolyWriteAdapter<'a, W> { |
| 154 | + #[allow(unused)] // This will be used for onion messages soon |
| 155 | + pub fn new(rho: [u8; 32], writeable: &'a W) -> ChaChaPolyWriteAdapter<'a, W> { |
| 156 | + Self { rho, writeable } |
| 157 | + } |
| 158 | +} |
| 159 | + |
| 160 | +impl<'a, T: Writeable> Writeable for ChaChaPolyWriteAdapter<'a, T> { |
| 161 | + // Simultaneously write and encrypt Self::writeable. |
| 162 | + fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> { |
| 163 | + let mut chacha = ChaCha20Poly1305RFC::new(&self.rho, &[0; 12], &[]); |
| 164 | + let mut chacha_stream = ChaChaPolyWriter { chacha: &mut chacha, write: w }; |
| 165 | + self.writeable.write(&mut chacha_stream)?; |
| 166 | + let mut tag = [0 as u8; 16]; |
| 167 | + chacha.finish_and_get_tag(&mut tag); |
| 168 | + tag.write(w)?; |
| 169 | + |
| 170 | + Ok(()) |
| 171 | + } |
| 172 | +} |
| 173 | + |
100 | 174 | #[cfg(fuzzing)]
|
101 | 175 | mod fuzzy_chachapoly {
|
102 | 176 | #[derive(Clone, Copy)]
|
@@ -130,6 +204,16 @@ mod fuzzy_chachapoly {
|
130 | 204 | self.finished = true;
|
131 | 205 | }
|
132 | 206 |
|
| 207 | + pub(super) fn encrypt_in_place(&mut self, _input_output: &mut [u8]) { |
| 208 | + assert!(self.finished == false); |
| 209 | + self.finished = true; |
| 210 | + } |
| 211 | + |
| 212 | + pub(super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) { |
| 213 | + out_tag.copy_from_slice(&self.tag); |
| 214 | + self.finished = true; |
| 215 | + } |
| 216 | + |
133 | 217 | pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
|
134 | 218 | assert!(input.len() == output.len());
|
135 | 219 | assert!(self.finished == false);
|
|
0 commit comments