Skip to content

Commit 3c9538f

Browse files
committed
Add a ChaChaReader adapter to read an encrypted stream & use it
This prepares for variable-length per-hop-data by wrapping the full hop_data field in a decrypting stream, with a few minor optimizations and redundant allocations to boot.
1 parent a166eca commit 3c9538f

File tree

2 files changed

+61
-16
lines changed

2 files changed

+61
-16
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,19 @@ use chain::keysinterface::{ChannelKeys, KeysInterface, InMemoryChannelKeys};
3838
use util::config::UserConfig;
3939
use util::{byte_utils, events};
4040
use util::ser::{Readable, ReadableArgs, Writeable, Writer};
41-
use util::chacha20::ChaCha20;
41+
use util::chacha20::{ChaCha20, ChaChaReader};
4242
use util::logger::Logger;
4343
use util::errors::APIError;
4444

4545
use std::{cmp, mem};
4646
use std::collections::{HashMap, hash_map, HashSet};
47-
use std::io::Cursor;
47+
use std::io::{Cursor, Read};
4848
use std::sync::{Arc, Mutex, MutexGuard, RwLock};
4949
use std::sync::atomic::{AtomicUsize, Ordering};
5050
use std::time::Duration;
5151
use std::marker::{Sync, Send};
5252
use std::ops::Deref;
5353

54-
const SIXTY_FIVE_ZEROS: [u8; 65] = [0; 65];
55-
5654
// We hold various information about HTLC relay in the HTLC objects in Channel itself:
5755
//
5856
// Upon receipt of an HTLC from a peer, we'll give it a PendingHTLCStatus indicating if it should
@@ -906,20 +904,23 @@ impl<ChanSigner: ChannelKeys, M: Deref> ChannelManager<ChanSigner, M> where M::T
906904
}
907905

908906
let mut chacha = ChaCha20::new(&rho, &[0u8; 8]);
907+
let mut chacha_stream = ChaChaReader { chacha: &mut chacha, read: Cursor::new(&msg.onion_routing_packet.hop_data[..]) };
909908
let (next_hop_data, next_hop_hmac) = {
910-
let mut decoded = [0; 65];
911-
chacha.process(&msg.onion_routing_packet.hop_data[0..65], &mut decoded);
912-
let mut hmac = [0; 32];
913-
hmac.copy_from_slice(&decoded[33..]);
914-
match msgs::OnionHopData::read(&mut Cursor::new(&decoded[..33])) {
909+
match msgs::OnionHopData::read(&mut chacha_stream) {
915910
Err(err) => {
916911
let error_code = match err {
917912
msgs::DecodeError::UnknownVersion => 0x4000 | 1, // unknown realm byte
918913
_ => 0x2000 | 2, // Should never happen
919914
};
920915
return_err!("Unable to decode our hop data", error_code, &[0;0]);
921916
},
922-
Ok(msg) => (msg, hmac)
917+
Ok(msg) => {
918+
let mut hmac = [0; 32];
919+
if let Err(_) = chacha_stream.read_exact(&mut hmac[..]) {
920+
return_err!("Unable to decode hop data", 0x4000 | 1, &[0;0]);
921+
}
922+
(msg, hmac)
923+
},
923924
}
924925
};
925926

@@ -933,10 +934,11 @@ impl<ChanSigner: ChannelKeys, M: Deref> ChannelManager<ChanSigner, M> where M::T
933934
// as-is (and were originally 0s).
934935
// Of course reverse path calculation is still pretty easy given naive routing
935936
// algorithms, but this fixes the most-obvious case.
936-
let mut new_packet_data = [0; 19*65];
937-
chacha.process(&msg.onion_routing_packet.hop_data[65..], &mut new_packet_data[0..19*65]);
938-
assert_ne!(new_packet_data[0..65], [0; 65][..]);
939-
assert_ne!(new_packet_data[..], [0; 19*65][..]);
937+
let mut next_bytes = [0; 32];
938+
chacha_stream.read_exact(&mut next_bytes).unwrap();
939+
assert_ne!(next_bytes[..], [0; 32][..]);
940+
chacha_stream.read_exact(&mut next_bytes).unwrap();
941+
assert_ne!(next_bytes[..], [0; 32][..]);
940942
}
941943

942944
// OUR PAYMENT!
@@ -968,8 +970,10 @@ impl<ChanSigner: ChannelKeys, M: Deref> ChannelManager<ChanSigner, M> where M::T
968970
})
969971
} else {
970972
let mut new_packet_data = [0; 20*65];
971-
chacha.process(&msg.onion_routing_packet.hop_data[65..], &mut new_packet_data[0..19*65]);
972-
chacha.process(&SIXTY_FIVE_ZEROS[..], &mut new_packet_data[19*65..]);
973+
let read_pos = chacha_stream.read(&mut new_packet_data).unwrap();
974+
// Once we've emptied the set of bytes our peer gave us, encrypt 0 bytes until we
975+
// fill the onion hop data we'll forward to our next-hop peer.
976+
chacha_stream.chacha.process_inline(&mut new_packet_data[read_pos..]);
973977

974978
let mut new_pubkey = msg.onion_routing_packet.public_key.unwrap();
975979

lightning/src/util/chacha20.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
// option. This file may not be copied, modified, or distributed
1010
// except according to those terms.
1111

12+
use std::io;
13+
1214
#[cfg(not(feature = "fuzztarget"))]
1315
mod real_chacha {
1416
use std::cmp;
@@ -249,6 +251,29 @@ mod real_chacha {
249251
self.offset += count;
250252
}
251253
}
254+
255+
pub fn process_inline(&mut self, input_output: &mut [u8]) {
256+
let len = input_output.len();
257+
let mut i = 0;
258+
while i < len {
259+
// If there is no keystream available in the output buffer,
260+
// generate the next block.
261+
if self.offset == 64 {
262+
self.update();
263+
}
264+
265+
// Process the min(available keystream, remaining input length).
266+
let count = cmp::min(64 - self.offset, len - i);
267+
// explicitly assert lengths to avoid bounds checks:
268+
assert!(input_output.len() >= i + count);
269+
assert!(self.output.len() >= self.offset + count);
270+
for j in 0..count {
271+
input_output[i + j] ^= self.output[self.offset + j];
272+
}
273+
i += count;
274+
self.offset += count;
275+
}
276+
}
252277
}
253278
}
254279
#[cfg(not(feature = "fuzztarget"))]
@@ -268,11 +293,27 @@ mod fuzzy_chacha {
268293
pub fn process(&mut self, input: &[u8], output: &mut [u8]) {
269294
output.copy_from_slice(input);
270295
}
296+
297+
pub fn process_inline(&mut self, _input_output: &mut [u8]) {}
271298
}
272299
}
273300
#[cfg(feature = "fuzztarget")]
274301
pub use self::fuzzy_chacha::ChaCha20;
275302

303+
pub(crate) struct ChaChaReader<'a, R: io::Read> {
304+
pub chacha: &'a mut ChaCha20,
305+
pub read: R,
306+
}
307+
impl<'a, R: io::Read> io::Read for ChaChaReader<'a, R> {
308+
fn read(&mut self, dest: &mut [u8]) -> Result<usize, io::Error> {
309+
let res = self.read.read(dest)?;
310+
if res > 0 {
311+
self.chacha.process_inline(&mut dest[0..res]);
312+
}
313+
Ok(res)
314+
}
315+
}
316+
276317
#[cfg(test)]
277318
mod test {
278319
use std::iter::repeat;

0 commit comments

Comments
 (0)