Skip to content

Commit e716218

Browse files
chacha20poly1305: enable simultaneous writing+encryption
In the upcoming onion messages PR, this will allow us to avoid encoding onion message encrypted data into an intermediate Vec before encrypting it. Instead we encode and encrypt at the same time using this new ChaChaPolyWriteAdapter object.
1 parent 362ddab commit e716218

File tree

1 file changed

+83
-3
lines changed

1 file changed

+83
-3
lines changed

lightning/src/util/chacha20poly1305rfc.rs

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
// This is a port of Andrew Moons poly1305-donna
1111
// https://github.com/floodyberry/poly1305-donna
1212

13+
use util::ser::{Writeable, Writer};
14+
use io::{self, Write};
15+
1316
#[cfg(not(fuzzing))]
1417
mod real_chachapoly {
1518
use util::chacha20::ChaCha20;
@@ -58,11 +61,32 @@ mod real_chachapoly {
5861
}
5962

6063
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);
6364
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+
}
6483
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]) {
6690
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
6791
self.finished = true;
6892
self.mac.input(&self.aad_len.to_le_bytes());
@@ -97,6 +121,49 @@ mod real_chachapoly {
97121
#[cfg(not(fuzzing))]
98122
pub use self::real_chachapoly::ChaCha20Poly1305RFC;
99123

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+
100167
#[cfg(fuzzing)]
101168
mod fuzzy_chachapoly {
102169
#[derive(Clone, Copy)]
@@ -130,6 +197,19 @@ mod fuzzy_chachapoly {
130197
self.finished = true;
131198
}
132199

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+
133213
pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
134214
assert!(input.len() == output.len());
135215
assert!(self.finished == false);

0 commit comments

Comments
 (0)