Skip to content

Commit 6952dd7

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 243ccd4 commit 6952dd7

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
@@ -22,7 +22,11 @@ const KEY_ROTATION_INDEX: u32 = 1000;
2222
/// For decryption, it is recommended to call `decrypt_message_stream` for automatic buffering.
2323
pub struct Conduit {
2424
pub(super) encryptor: Encryptor,
25-
pub(super) decryptor: Decryptor
25+
26+
#[cfg(feature = "fuzztarget")]
27+
pub decryptor: Decryptor,
28+
#[cfg(not(feature = "fuzztarget"))]
29+
pub(super) decryptor: Decryptor,
2630

2731
}
2832

@@ -32,7 +36,7 @@ pub(super) struct Encryptor {
3236
sending_nonce: u32,
3337
}
3438

35-
pub(super) struct Decryptor {
39+
pub struct Decryptor {
3640
receiving_key: SymmetricKey,
3741
receiving_chaining_key: SymmetricKey,
3842
receiving_nonce: u32,
@@ -63,7 +67,7 @@ impl Conduit {
6367
receiving_key,
6468
receiving_chaining_key: chaining_key,
6569
receiving_nonce: 0,
66-
read_buffer: None,
70+
read_buffer: Some(vec![]),
6771
pending_message_length: None,
6872
decrypted_payloads: VecDeque::new(),
6973
}
@@ -79,15 +83,6 @@ impl Conduit {
7983
self.decryptor.read(data)
8084
}
8185

82-
/// Decrypt a single message. If data containing more than one message has been received,
83-
/// only the first message will be returned, and the rest stored in the internal buffer.
84-
/// If a message pending in the buffer still hasn't been decrypted, that message will be
85-
/// returned in lieu of anything new, even if new data is provided.
86-
#[cfg(any(test, feature = "fuzztarget"))]
87-
pub fn decrypt_single_message(&mut self, new_data: Option<&[u8]>) -> Result<Option<Vec<u8>>, String> {
88-
Ok(self.decryptor.decrypt_single_message(new_data)?)
89-
}
90-
9186
fn increment_nonce(nonce: &mut u32, chaining_key: &mut SymmetricKey, key: &mut SymmetricKey) {
9287
*nonce += 1;
9388
if *nonce == KEY_ROTATION_INDEX {
@@ -129,53 +124,48 @@ impl Encryptor {
129124
}
130125

131126
impl Decryptor {
132-
pub(super) fn read(&mut self, data: &[u8]) -> Result<(), String> {
133-
let mut input_data = Some(data);
134127

128+
// Read in new encrypted data and process it. This attempts to decrypt the input data and any
129+
// existing data in the internal read buffer and can return an error if there is an error raised
130+
// from the decryption code.
131+
pub fn read(&mut self, data: &[u8]) -> Result<(), String> {
132+
let mut read_buffer = self.read_buffer.take().unwrap();
133+
134+
let buffer = if read_buffer.is_empty() {
135+
data
136+
} else {
137+
read_buffer.extend_from_slice(data);
138+
read_buffer.as_slice()
139+
};
140+
141+
let mut read_offset = 0;
135142
loop {
136-
match self.decrypt_single_message(input_data) {
137-
Ok(Some(result)) => {
143+
match self.decrypt_next(&buffer[read_offset..]) {
144+
Ok((Some(result), bytes_read)) => {
145+
read_offset += bytes_read;
138146
self.decrypted_payloads.push_back(result);
139147
},
140-
Ok(None) => {
148+
Ok((None, 0)) => {
149+
self.read_buffer = Some(buffer[read_offset..].to_vec());
141150
break;
142151
}
143152
Err(e) => {
144153
return Err(e);
145154
}
155+
Ok((None, _)) => { panic!("Invalid return from decrypt_next()") }
146156
}
147-
input_data = None;
148157
}
149158

150159
Ok(())
151160
}
152161

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

172-
let (current_message, offset) = self.decrypt(&read_buffer[..])?;
173-
read_buffer.drain(..offset); // drain the read buffer
174-
self.read_buffer = Some(read_buffer); // assign the new value to the built-in buffer
175-
Ok(current_message)
176-
}
177-
178-
fn decrypt(&mut self, buffer: &[u8]) -> Result<(Option<Vec<u8>>, usize), String> {
179169
let message_length = if let Some(length) = self.pending_message_length {
180170
// we have already decrypted the header
181171
length
@@ -263,10 +253,47 @@ mod tests {
263253
let encrypted_message = connected_peer.encrypt(&message);
264254
assert_eq!(encrypted_message.len(), 2 + 16 + 16);
265255

266-
let decrypted_message = remote_peer.decrypt_single_message(Some(&encrypted_message)).unwrap().unwrap();
256+
remote_peer.decryptor.read(&encrypted_message[..]).unwrap();
257+
let decrypted_message = remote_peer.decryptor.next().unwrap();
267258
assert_eq!(decrypted_message, Vec::<u8>::new());
268259
}
269260

261+
// Test that descrypting from a slice that is the partial data followed by another decrypt call
262+
// with the remaining data works. This exercises the slow-path for decryption and ensures the
263+
// data is written to the read_buffer properly.
264+
#[test]
265+
fn test_decrypt_from_slice_two_calls_no_header_then_rest() {
266+
let (mut connected_peer, mut remote_peer) = setup_peers();
267+
268+
let message: Vec<u8> = vec![1];
269+
let encrypted_message = connected_peer.encrypt(&message);
270+
271+
remote_peer.decryptor.read(&encrypted_message[..1]).unwrap();
272+
assert!(remote_peer.decryptor.next().is_none());
273+
274+
remote_peer.decryptor.read(&encrypted_message[1..]).unwrap();
275+
let decrypted_message = remote_peer.decryptor.next().unwrap();
276+
277+
assert_eq!(decrypted_message, vec![1]);
278+
}
279+
280+
// Include the header in the first slice
281+
#[test]
282+
fn test_decrypt_from_slice_two_calls_header_then_rest() {
283+
let (mut connected_peer, mut remote_peer) = setup_peers();
284+
285+
let message: Vec<u8> = vec![1];
286+
let encrypted_message = connected_peer.encrypt(&message);
287+
288+
remote_peer.decryptor.read(&encrypted_message[..20]).unwrap();
289+
assert!(remote_peer.decryptor.next().is_none());
290+
291+
remote_peer.decryptor.read(&encrypted_message[20..]).unwrap();
292+
let decrypted_message = remote_peer.decryptor.next().unwrap();
293+
294+
assert_eq!(decrypted_message, vec![1]);
295+
}
296+
270297
#[test]
271298
fn test_nonce_chaining() {
272299
let (mut connected_peer, _remote_peer) = setup_peers();
@@ -316,13 +343,16 @@ mod tests {
316343
let mut current_encrypted_message = encrypted_messages.remove(0);
317344
let next_encrypted_message = encrypted_messages.remove(0);
318345
current_encrypted_message.extend_from_slice(&next_encrypted_message);
319-
let decrypted_message = remote_peer.decrypt_single_message(Some(&current_encrypted_message)).unwrap().unwrap();
346+
remote_peer.read(&current_encrypted_message[..]).unwrap();
347+
348+
let decrypted_message = remote_peer.decryptor.next().unwrap();
320349
assert_eq!(decrypted_message, message);
321350
}
322351

323352
for _ in 0..501 {
324353
// decrypt messages directly from buffer without adding to it
325-
let decrypted_message = remote_peer.decrypt_single_message(None).unwrap().unwrap();
354+
remote_peer.read(&[]).unwrap();
355+
let decrypted_message = remote_peer.decryptor.next().unwrap();
326356
assert_eq!(decrypted_message, message);
327357
}
328358
}
@@ -367,7 +397,7 @@ mod tests {
367397
fn max_message_len_encryption() {
368398
let (mut connected_peer, _) = setup_peers();
369399
let msg = [4u8; LN_MAX_MSG_LEN + 1];
370-
connected_peer.encrypt(&msg);
400+
let _should_panic = connected_peer.encrypt(&msg);
371401
}
372402

373403
#[test]
@@ -377,6 +407,6 @@ mod tests {
377407

378408
// MSG should not exceed LN_MAX_MSG_LEN + 16
379409
let msg = [4u8; LN_MAX_MSG_LEN + 17];
380-
connected_peer.decrypt_single_message(Some(&msg)).unwrap();
410+
connected_peer.read(&msg).unwrap();
381411
}
382412
}

0 commit comments

Comments
 (0)