Skip to content

Commit d205518

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 7bd8f8c commit d205518

File tree

1 file changed

+84
-0
lines changed

1 file changed

+84
-0
lines changed

lightning/src/util/chacha20poly1305rfc.rs

+84
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;
@@ -70,6 +73,26 @@ mod real_chachapoly {
7073
self.mac.raw_result(out_tag);
7174
}
7275

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+
7396
pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
7497
assert!(input.len() == output.len());
7598
assert!(self.finished == false);
@@ -97,6 +120,57 @@ mod real_chachapoly {
97120
#[cfg(not(fuzzing))]
98121
pub use self::real_chachapoly::ChaCha20Poly1305RFC;
99122

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+
100174
#[cfg(fuzzing)]
101175
mod fuzzy_chachapoly {
102176
#[derive(Clone, Copy)]
@@ -130,6 +204,16 @@ mod fuzzy_chachapoly {
130204
self.finished = true;
131205
}
132206

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

0 commit comments

Comments
 (0)