Skip to content

Commit 2b8d24d

Browse files
committed
perf: Decryptor can now read from input slice when appropriate
Previously, all input data was written to the read buffer before any decryption was attempted. This patch will use the input data solely in the event that there is no previous data in the internal read buffer. This also converts all tests to use the public interface instead of the previously used decrypt_single_message This was design feedback from the original 494 review.
1 parent 3ab11f2 commit 2b8d24d

File tree

2 files changed

+91
-51
lines changed

2 files changed

+91
-51
lines changed

fuzz/src/peer_crypt.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,23 +138,33 @@ fn do_conduit_tests(generator: &mut FuzzGen, initiator_conduit: &mut Conduit, re
138138
// randomly choose sender of message
139139
let receiver_unencrypted_msg = if generator.generate_bool()? {
140140
let encrypted_msg = initiator_conduit.encrypt(sender_unencrypted_msg);
141-
if let Ok(msg) = responder_conduit.decrypt_single_message(Some(&encrypted_msg)) {
142-
msg
141+
if let Ok(_) = responder_conduit.decryptor.read(&encrypted_msg) {
142+
if let Some(msg) = responder_conduit.decryptor.next() {
143+
msg
144+
} else {
145+
assert!(failures_expected);
146+
return Ok(());
147+
}
143148
} else {
144149
assert!(failures_expected);
145150
return Ok(());
146151
}
147152
} else {
148153
let encrypted_msg = responder_conduit.encrypt(sender_unencrypted_msg);
149-
if let Ok(msg) = initiator_conduit.decrypt_single_message(Some(&encrypted_msg)) {
150-
msg
154+
if let Ok(_) = initiator_conduit.decryptor.read(&encrypted_msg) {
155+
if let Some(msg) = initiator_conduit.decryptor.next() {
156+
msg
157+
} else {
158+
assert!(failures_expected);
159+
return Ok(());
160+
}
151161
} else {
152162
assert!(failures_expected);
153163
return Ok(());
154164
}
155165
};
156166

157-
assert_eq!(sender_unencrypted_msg, receiver_unencrypted_msg.unwrap().as_slice());
167+
assert_eq!(sender_unencrypted_msg, receiver_unencrypted_msg.as_slice());
158168
}
159169
}
160170

lightning/src/ln/peers/conduit.rs

Lines changed: 76 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ const KEY_ROTATION_INDEX: u32 = 1000;
3131
/// For decryption, it is recommended to call `decrypt_message_stream` for automatic buffering.
3232
pub struct Conduit {
3333
pub(super) encryptor: Encryptor,
34-
pub(super) decryptor: Decryptor
34+
35+
#[cfg(feature = "fuzztarget")]
36+
pub decryptor: Decryptor,
37+
#[cfg(not(feature = "fuzztarget"))]
38+
pub(super) decryptor: Decryptor,
3539

3640
}
3741

@@ -41,7 +45,7 @@ pub(super) struct Encryptor {
4145
sending_nonce: u32,
4246
}
4347

44-
pub(super) struct Decryptor {
48+
pub struct Decryptor {
4549
receiving_key: SymmetricKey,
4650
receiving_chaining_key: SymmetricKey,
4751
receiving_nonce: u32,
@@ -72,7 +76,7 @@ impl Conduit {
7276
receiving_key,
7377
receiving_chaining_key: chaining_key,
7478
receiving_nonce: 0,
75-
read_buffer: None,
79+
read_buffer: Some(vec![]),
7680
pending_message_length: None,
7781
decrypted_payloads: VecDeque::new(),
7882
}
@@ -88,15 +92,6 @@ impl Conduit {
8892
self.decryptor.read(data)
8993
}
9094

91-
/// Decrypt a single message. If data containing more than one message has been received,
92-
/// only the first message will be returned, and the rest stored in the internal buffer.
93-
/// If a message pending in the buffer still hasn't been decrypted, that message will be
94-
/// returned in lieu of anything new, even if new data is provided.
95-
#[cfg(any(test, feature = "fuzztarget"))]
96-
pub fn decrypt_single_message(&mut self, new_data: Option<&[u8]>) -> Result<Option<Vec<u8>>, String> {
97-
Ok(self.decryptor.decrypt_single_message(new_data)?)
98-
}
99-
10095
fn increment_nonce(nonce: &mut u32, chaining_key: &mut SymmetricKey, key: &mut SymmetricKey) {
10196
*nonce += 1;
10297
if *nonce == KEY_ROTATION_INDEX {
@@ -138,53 +133,48 @@ impl Encryptor {
138133
}
139134

140135
impl Decryptor {
141-
pub(super) fn read(&mut self, data: &[u8]) -> Result<(), String> {
142-
let mut input_data = Some(data);
143136

137+
// Read in new encrypted data and process it. This attempts to decrypt the input data and any
138+
// existing data in the internal read buffer and can return an error if there is an error raised
139+
// from the decryption code.
140+
pub fn read(&mut self, data: &[u8]) -> Result<(), String> {
141+
let mut read_buffer = self.read_buffer.take().unwrap();
142+
143+
let buffer = if read_buffer.is_empty() {
144+
data
145+
} else {
146+
read_buffer.extend_from_slice(data);
147+
read_buffer.as_slice()
148+
};
149+
150+
let mut read_offset = 0;
144151
loop {
145-
match self.decrypt_single_message(input_data) {
146-
Ok(Some(result)) => {
152+
match self.decrypt_next(&buffer[read_offset..]) {
153+
Ok((Some(result), bytes_read)) => {
154+
read_offset += bytes_read;
147155
self.decrypted_payloads.push_back(result);
148156
},
149-
Ok(None) => {
157+
Ok((None, 0)) => {
158+
self.read_buffer = Some(buffer[read_offset..].to_vec());
150159
break;
151160
}
152161
Err(e) => {
153162
return Err(e);
154163
}
164+
Ok((None, _)) => { panic!("Invalid return from decrypt_next()") }
155165
}
156-
input_data = None;
157166
}
158167

159168
Ok(())
160169
}
161170

162-
/// Decrypt a single message. If data containing more than one message has been received,
163-
/// only the first message will be returned, and the rest stored in the internal buffer.
164-
/// If a message pending in the buffer still hasn't been decrypted, that message will be
165-
/// returned in lieu of anything new, even if new data is provided.
166-
pub fn decrypt_single_message(&mut self, new_data: Option<&[u8]>) -> Result<Option<Vec<u8>>, String> {
167-
let mut read_buffer = if let Some(buffer) = self.read_buffer.take() {
168-
buffer
169-
} else {
170-
Vec::new()
171-
};
172-
173-
if let Some(data) = new_data {
174-
read_buffer.extend_from_slice(data);
175-
}
176-
177-
if read_buffer.len() > LN_MAX_MSG_LEN + 16 {
171+
// Decrypt the next payload from the slice returning the number of bytes consumed during the
172+
// operation. This will always be (None, 0) if no payload could be decrypted.
173+
fn decrypt_next(&mut self, buffer: &[u8]) -> Result<(Option<Vec<u8>>, usize), String> {
174+
if buffer.len() > LN_MAX_MSG_LEN + 16 {
178175
panic!("Attempted to decrypt message longer than 65535 + 16 bytes!");
179176
}
180177

181-
let (current_message, offset) = self.decrypt(&read_buffer[..])?;
182-
read_buffer.drain(..offset); // drain the read buffer
183-
self.read_buffer = Some(read_buffer); // assign the new value to the built-in buffer
184-
Ok(current_message)
185-
}
186-
187-
fn decrypt(&mut self, buffer: &[u8]) -> Result<(Option<Vec<u8>>, usize), String> {
188178
let message_length = if let Some(length) = self.pending_message_length {
189179
// we have already decrypted the header
190180
length
@@ -272,10 +262,47 @@ mod tests {
272262
let encrypted_message = connected_peer.encrypt(&message);
273263
assert_eq!(encrypted_message.len(), 2 + 16 + 16);
274264

275-
let decrypted_message = remote_peer.decrypt_single_message(Some(&encrypted_message)).unwrap().unwrap();
265+
remote_peer.decryptor.read(&encrypted_message[..]).unwrap();
266+
let decrypted_message = remote_peer.decryptor.next().unwrap();
276267
assert_eq!(decrypted_message, Vec::<u8>::new());
277268
}
278269

270+
// Test that descrypting from a slice that is the partial data followed by another decrypt call
271+
// with the remaining data works. This exercises the slow-path for decryption and ensures the
272+
// data is written to the read_buffer properly.
273+
#[test]
274+
fn test_decrypt_from_slice_two_calls_no_header_then_rest() {
275+
let (mut connected_peer, mut remote_peer) = setup_peers();
276+
277+
let message: Vec<u8> = vec![1];
278+
let encrypted_message = connected_peer.encrypt(&message);
279+
280+
remote_peer.decryptor.read(&encrypted_message[..1]).unwrap();
281+
assert!(remote_peer.decryptor.next().is_none());
282+
283+
remote_peer.decryptor.read(&encrypted_message[1..]).unwrap();
284+
let decrypted_message = remote_peer.decryptor.next().unwrap();
285+
286+
assert_eq!(decrypted_message, vec![1]);
287+
}
288+
289+
// Include the header in the first slice
290+
#[test]
291+
fn test_decrypt_from_slice_two_calls_header_then_rest() {
292+
let (mut connected_peer, mut remote_peer) = setup_peers();
293+
294+
let message: Vec<u8> = vec![1];
295+
let encrypted_message = connected_peer.encrypt(&message);
296+
297+
remote_peer.decryptor.read(&encrypted_message[..20]).unwrap();
298+
assert!(remote_peer.decryptor.next().is_none());
299+
300+
remote_peer.decryptor.read(&encrypted_message[20..]).unwrap();
301+
let decrypted_message = remote_peer.decryptor.next().unwrap();
302+
303+
assert_eq!(decrypted_message, vec![1]);
304+
}
305+
279306
#[test]
280307
fn test_nonce_chaining() {
281308
let (mut connected_peer, _remote_peer) = setup_peers();
@@ -325,13 +352,16 @@ mod tests {
325352
let mut current_encrypted_message = encrypted_messages.remove(0);
326353
let next_encrypted_message = encrypted_messages.remove(0);
327354
current_encrypted_message.extend_from_slice(&next_encrypted_message);
328-
let decrypted_message = remote_peer.decrypt_single_message(Some(&current_encrypted_message)).unwrap().unwrap();
355+
remote_peer.read(&current_encrypted_message[..]).unwrap();
356+
357+
let decrypted_message = remote_peer.decryptor.next().unwrap();
329358
assert_eq!(decrypted_message, message);
330359
}
331360

332361
for _ in 0..501 {
333362
// decrypt messages directly from buffer without adding to it
334-
let decrypted_message = remote_peer.decrypt_single_message(None).unwrap().unwrap();
363+
remote_peer.read(&[]).unwrap();
364+
let decrypted_message = remote_peer.decryptor.next().unwrap();
335365
assert_eq!(decrypted_message, message);
336366
}
337367
}
@@ -376,7 +406,7 @@ mod tests {
376406
fn max_message_len_encryption() {
377407
let (mut connected_peer, _) = setup_peers();
378408
let msg = [4u8; LN_MAX_MSG_LEN + 1];
379-
connected_peer.encrypt(&msg);
409+
let _should_panic = connected_peer.encrypt(&msg);
380410
}
381411

382412
#[test]
@@ -386,6 +416,6 @@ mod tests {
386416

387417
// MSG should not exceed LN_MAX_MSG_LEN + 16
388418
let msg = [4u8; LN_MAX_MSG_LEN + 17];
389-
connected_peer.decrypt_single_message(Some(&msg)).unwrap();
419+
connected_peer.read(&msg).unwrap();
390420
}
391421
}

0 commit comments

Comments
 (0)