Skip to content

Commit 8da7a63

Browse files
committed
fix: Conduit::Decryptor should not panic on decryption failure
introduced in eb6a371 On decryption failure, the Decryptor should return an error to the caller so it can disconnect instead of panicking. Implement this behavior by plumbing Result through the iterator code path and adding the appropriate tests and comments.
1 parent b2cffa5 commit 8da7a63

File tree

2 files changed

+186
-43
lines changed

2 files changed

+186
-43
lines changed

lightning/src/ln/peers/conduit.rs

Lines changed: 141 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,29 @@ pub(super) struct Decryptor {
3838

3939
pending_message_length: Option<usize>,
4040
read_buffer: Option<Vec<u8>>,
41+
poisoned: bool, // signal an error has occurred so None is returned on iteration after failure
4142
}
4243

4344
impl Iterator for Decryptor {
44-
type Item = Vec<u8>;
45+
type Item = Result<Option<Vec<u8>>, String>;
4546

4647
fn next(&mut self) -> Option<Self::Item> {
47-
self.decrypt_single_message(None)
48+
if self.poisoned {
49+
return None;
50+
}
51+
52+
match self.decrypt_single_message(None) {
53+
Ok(Some(result)) => {
54+
Some(Ok(Some(result)))
55+
},
56+
Ok(None) => {
57+
None
58+
}
59+
Err(e) => {
60+
self.poisoned = true;
61+
Some(Err(e))
62+
}
63+
}
4864
}
4965
}
5066

@@ -62,7 +78,8 @@ impl Conduit {
6278
receiving_chaining_key: chaining_key,
6379
receiving_nonce: 0,
6480
read_buffer: None,
65-
pending_message_length: None
81+
pending_message_length: None,
82+
poisoned: false
6683
}
6784
}
6885
}
@@ -81,8 +98,8 @@ impl Conduit {
8198
/// If a message pending in the buffer still hasn't been decrypted, that message will be
8299
/// returned in lieu of anything new, even if new data is provided.
83100
#[cfg(any(test, feature = "fuzztarget"))]
84-
pub fn decrypt_single_message(&mut self, new_data: Option<&[u8]>) -> Option<Vec<u8>> {
85-
self.decryptor.decrypt_single_message(new_data)
101+
pub fn decrypt_single_message(&mut self, new_data: Option<&[u8]>) -> Result<Option<Vec<u8>>, String> {
102+
Ok(self.decryptor.decrypt_single_message(new_data)?)
86103
}
87104

88105
fn increment_nonce(nonce: &mut u32, chaining_key: &mut SymmetricKey, key: &mut SymmetricKey) {
@@ -135,7 +152,7 @@ impl Decryptor {
135152
/// only the first message will be returned, and the rest stored in the internal buffer.
136153
/// If a message pending in the buffer still hasn't been decrypted, that message will be
137154
/// returned in lieu of anything new, even if new data is provided.
138-
pub fn decrypt_single_message(&mut self, new_data: Option<&[u8]>) -> Option<Vec<u8>> {
155+
pub fn decrypt_single_message(&mut self, new_data: Option<&[u8]>) -> Result<Option<Vec<u8>>, String> {
139156
let mut read_buffer = if let Some(buffer) = self.read_buffer.take() {
140157
buffer
141158
} else {
@@ -150,25 +167,25 @@ impl Decryptor {
150167
panic!("Attempted to decrypt message longer than 65535 + 16 bytes!");
151168
}
152169

153-
let (current_message, offset) = self.decrypt(&read_buffer[..]);
170+
let (current_message, offset) = self.decrypt(&read_buffer[..])?;
154171
read_buffer.drain(..offset); // drain the read buffer
155172
self.read_buffer = Some(read_buffer); // assign the new value to the built-in buffer
156-
current_message
173+
Ok(current_message)
157174
}
158175

159-
fn decrypt(&mut self, buffer: &[u8]) -> (Option<Vec<u8>>, usize) {
176+
fn decrypt(&mut self, buffer: &[u8]) -> Result<(Option<Vec<u8>>, usize), String> {
160177
let message_length = if let Some(length) = self.pending_message_length {
161178
// we have already decrypted the header
162179
length
163180
} else {
164181
if buffer.len() < TAGGED_MESSAGE_LENGTH_HEADER_SIZE {
165182
// A message must be at least 18 bytes (2 for encrypted length, 16 for the tag)
166-
return (None, 0);
183+
return Ok((None, 0));
167184
}
168185

169186
let encrypted_length = &buffer[0..TAGGED_MESSAGE_LENGTH_HEADER_SIZE];
170187
let mut length_bytes = [0u8; MESSAGE_LENGTH_HEADER_SIZE];
171-
length_bytes.copy_from_slice(&chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_length).unwrap());
188+
length_bytes.copy_from_slice(&chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_length)?);
172189

173190
self.increment_nonce();
174191

@@ -180,18 +197,18 @@ impl Decryptor {
180197

181198
if buffer.len() < message_end_index {
182199
self.pending_message_length = Some(message_length);
183-
return (None, 0);
200+
return Ok((None, 0));
184201
}
185202

186203
self.pending_message_length = None;
187204

188205
let encrypted_message = &buffer[TAGGED_MESSAGE_LENGTH_HEADER_SIZE..message_end_index];
189206

190-
let message = chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_message).unwrap();
207+
let message = chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_message)?;
191208

192209
self.increment_nonce();
193210

194-
(Some(message), message_end_index)
211+
Ok((Some(message), message_end_index))
195212
}
196213

197214
fn increment_nonce(&mut self) {
@@ -243,7 +260,7 @@ mod tests {
243260
let encrypted_message = connected_peer.encrypt(&message);
244261
assert_eq!(encrypted_message.len(), 2 + 16 + 16);
245262

246-
let decrypted_message = remote_peer.decrypt_single_message(Some(&encrypted_message)).unwrap();
263+
let decrypted_message = remote_peer.decrypt_single_message(Some(&encrypted_message)).unwrap().unwrap();
247264
assert_eq!(decrypted_message, Vec::<u8>::new());
248265
}
249266

@@ -296,17 +313,123 @@ mod tests {
296313
let mut current_encrypted_message = encrypted_messages.remove(0);
297314
let next_encrypted_message = encrypted_messages.remove(0);
298315
current_encrypted_message.extend_from_slice(&next_encrypted_message);
299-
let decrypted_message = remote_peer.decrypt_single_message(Some(&current_encrypted_message)).unwrap();
316+
let decrypted_message = remote_peer.decrypt_single_message(Some(&current_encrypted_message)).unwrap().unwrap();
300317
assert_eq!(decrypted_message, message);
301318
}
302319

303320
for _ in 0..501 {
304321
// decrypt messages directly from buffer without adding to it
305-
let decrypted_message = remote_peer.decrypt_single_message(None).unwrap();
322+
let decrypted_message = remote_peer.decrypt_single_message(None).unwrap().unwrap();
306323
assert_eq!(decrypted_message, message);
307324
}
308325
}
309326

327+
// Decryption errors should result in Err
328+
#[test]
329+
fn decryption_failure_errors() {
330+
let (mut connected_peer, mut remote_peer) = setup_peers();
331+
let encrypted = remote_peer.encrypt(&[1]);
332+
333+
connected_peer.decryptor.receiving_key = [0; 32];
334+
assert_eq!(connected_peer.decrypt_single_message(Some(&encrypted)), Err("invalid hmac".to_string()));
335+
}
336+
337+
// Test next()::None
338+
#[test]
339+
fn decryptor_iterator_empty() {
340+
let (mut connected_peer, _) = setup_peers();
341+
342+
assert_eq!(connected_peer.decryptor.next(), None);
343+
}
344+
345+
// Test next() -> next()::None
346+
#[test]
347+
fn decryptor_iterator_one_item_valid() {
348+
let (mut connected_peer, mut remote_peer) = setup_peers();
349+
let encrypted = remote_peer.encrypt(&[1]);
350+
connected_peer.read(&encrypted);
351+
352+
assert_eq!(connected_peer.decryptor.next(), Some(Ok(Some(vec![1]))));
353+
assert_eq!(connected_peer.decryptor.next(), None);
354+
}
355+
356+
// Test next()::err -> next()::None
357+
#[test]
358+
fn decryptor_iterator_error() {
359+
let (mut connected_peer, mut remote_peer) = setup_peers();
360+
let encrypted = remote_peer.encrypt(&[1]);
361+
connected_peer.read(&encrypted);
362+
363+
connected_peer.decryptor.receiving_key = [0; 32];
364+
assert_eq!(connected_peer.decryptor.next(), Some(Err("invalid hmac".to_string())));
365+
assert_eq!(connected_peer.decryptor.next(), None);
366+
}
367+
368+
// Test next()::Some -> next()::err -> next()::None
369+
#[test]
370+
fn decryptor_iterator_error_after_success() {
371+
let (mut connected_peer, mut remote_peer) = setup_peers();
372+
let encrypted = remote_peer.encrypt(&[1]);
373+
connected_peer.read(&encrypted);
374+
let encrypted = remote_peer.encrypt(&[2]);
375+
connected_peer.read(&encrypted);
376+
377+
assert_eq!(connected_peer.decryptor.next(), Some(Ok(Some(vec![1]))));
378+
connected_peer.decryptor.receiving_key = [0; 32];
379+
assert_eq!(connected_peer.decryptor.next(), Some(Err("invalid hmac".to_string())));
380+
assert_eq!(connected_peer.decryptor.next(), None);
381+
}
382+
383+
// Test that next()::Some -> next()::err -> next()::None
384+
// Error should poison decryptor
385+
#[test]
386+
fn decryptor_iterator_next_after_error_returns_none() {
387+
let (mut connected_peer, mut remote_peer) = setup_peers();
388+
let encrypted = remote_peer.encrypt(&[1]);
389+
connected_peer.read(&encrypted);
390+
let encrypted = remote_peer.encrypt(&[2]);
391+
connected_peer.read(&encrypted);
392+
let encrypted = remote_peer.encrypt(&[3]);
393+
connected_peer.read(&encrypted);
394+
395+
// Get one valid value
396+
assert_eq!(connected_peer.decryptor.next(), Some(Ok(Some(vec![1]))));
397+
let valid_receiving_key = connected_peer.decryptor.receiving_key;
398+
399+
// Corrupt the receiving key and ensure we get a failure
400+
connected_peer.decryptor.receiving_key = [0; 32];
401+
assert_eq!(connected_peer.decryptor.next(), Some(Err("invalid hmac".to_string())));
402+
403+
// Restore the receiving key, do a read and ensure None is returned (poisoned)
404+
connected_peer.decryptor.receiving_key = valid_receiving_key;
405+
assert_eq!(connected_peer.decryptor.next(), None);
406+
}
407+
408+
// Test next()::Some -> next()::err -> read() -> next()::None
409+
// Error should poison decryptor even after future reads
410+
#[test]
411+
fn decryptor_iterator_read_next_after_error_returns_none() {
412+
let (mut connected_peer, mut remote_peer) = setup_peers();
413+
let encrypted = remote_peer.encrypt(&[1]);
414+
connected_peer.read(&encrypted);
415+
let encrypted = remote_peer.encrypt(&[2]);
416+
connected_peer.read(&encrypted);
417+
418+
// Get one valid value
419+
assert_eq!(connected_peer.decryptor.next(), Some(Ok(Some(vec![1]))));
420+
let valid_receiving_key = connected_peer.decryptor.receiving_key;
421+
422+
// Corrupt the receiving key and ensure we get a failure
423+
connected_peer.decryptor.receiving_key = [0; 32];
424+
assert_eq!(connected_peer.decryptor.next(), Some(Err("invalid hmac".to_string())));
425+
426+
// Restore the receiving key, do a read and ensure None is returned (poisoned)
427+
let encrypted = remote_peer.encrypt(&[3]);
428+
connected_peer.read(&encrypted);
429+
connected_peer.decryptor.receiving_key = valid_receiving_key;
430+
assert_eq!(connected_peer.decryptor.next(), None);
431+
}
432+
310433
#[test]
311434
fn max_msg_len_limit_value() {
312435
assert_eq!(LN_MAX_MSG_LEN, 65535);
@@ -328,6 +451,6 @@ mod tests {
328451

329452
// MSG should not exceed LN_MAX_MSG_LEN + 16
330453
let msg = [4u8; LN_MAX_MSG_LEN + 17];
331-
connected_peer.decrypt_single_message(Some(&msg));
454+
connected_peer.decrypt_single_message(Some(&msg)).unwrap();
332455
}
333456
}

lightning/src/ln/peers/handler.rs

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -603,33 +603,53 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, L: Deref> PeerManager<D
603603

604604
let mut received_messages = vec![];
605605
if let &mut PeerState::Connected(ref mut conduit) = &mut peer.encryptor {
606-
for msg_data in &mut conduit.decryptor {
607-
let mut reader = ::std::io::Cursor::new(&msg_data[..]);
608-
let message_result = wire::read(&mut reader);
609-
let message = match message_result {
610-
Ok(x) => x,
611-
Err(e) => {
612-
match e {
613-
msgs::DecodeError::UnknownVersion => return Err(PeerHandleError { no_connection_possible: false }),
614-
msgs::DecodeError::UnknownRequiredFeature => {
615-
log_debug!(self.logger, "Got a channel/node announcement with an known required feature flag, you may want to update!");
616-
continue;
617-
}
618-
msgs::DecodeError::InvalidValue => {
619-
log_debug!(self.logger, "Got an invalid value while deserializing message");
620-
return Err(PeerHandleError { no_connection_possible: false });
621-
}
622-
msgs::DecodeError::ShortRead => {
623-
log_debug!(self.logger, "Deserialization failed due to shortness of message");
624-
return Err(PeerHandleError { no_connection_possible: false });
606+
// Using Iterators that can error requires special handling
607+
// The item returned from next() has type Option<Result<Option<Vec>, String>>
608+
// The Some wrapper is stripped for each item inside the loop
609+
// There are 3 valid match cases:
610+
// 1) Some(Ok(Some(msg_data))) => Indicates a valid decrypted msg accessed via msg_data
611+
// 2) Some(Err(_)) => Indicates an error during decryption that should be handled
612+
// 3) None -> Indicates there were no messages available to decrypt
613+
// Invalid Cases
614+
// 1) Some(Ok(None)) => Translated to None case above so users of iterators can stop correctly
615+
for msg_data_result in &mut conduit.decryptor {
616+
match msg_data_result {
617+
Ok(Some(msg_data)) => {
618+
let mut reader = ::std::io::Cursor::new(&msg_data[..]);
619+
let message_result = wire::read(&mut reader);
620+
let message = match message_result {
621+
Ok(x) => x,
622+
Err(e) => {
623+
match e {
624+
msgs::DecodeError::UnknownVersion => return Err(PeerHandleError { no_connection_possible: false }),
625+
msgs::DecodeError::UnknownRequiredFeature => {
626+
log_debug!(self.logger, "Got a channel/node announcement with an known required feature flag, you may want to update!");
627+
continue;
628+
}
629+
msgs::DecodeError::InvalidValue => {
630+
log_debug!(self.logger, "Got an invalid value while deserializing message");
631+
return Err(PeerHandleError { no_connection_possible: false });
632+
}
633+
msgs::DecodeError::ShortRead => {
634+
log_debug!(self.logger, "Deserialization failed due to shortness of message");
635+
return Err(PeerHandleError { no_connection_possible: false });
636+
}
637+
msgs::DecodeError::BadLengthDescriptor => return Err(PeerHandleError { no_connection_possible: false }),
638+
msgs::DecodeError::Io(_) => return Err(PeerHandleError { no_connection_possible: false }),
639+
}
625640
}
626-
msgs::DecodeError::BadLengthDescriptor => return Err(PeerHandleError { no_connection_possible: false }),
627-
msgs::DecodeError::Io(_) => return Err(PeerHandleError { no_connection_possible: false }),
628-
}
629-
}
630-
};
641+
};
631642

632-
received_messages.push(message);
643+
received_messages.push(message);
644+
},
645+
Err(e) => {
646+
log_trace!(self.logger, "Message decryption failed due to: {}", e);
647+
return Err(PeerHandleError { no_connection_possible: false });
648+
}
649+
Ok(None) => {
650+
panic!("Invalid behavior. Conduit iterator should never return this match.")
651+
}
652+
}
633653
}
634654
}
635655

0 commit comments

Comments
 (0)