Skip to content

Commit b2cffa5

Browse files
committed
fix: Allow partial act messages to be received
Fix regression introduced in eb6a371 that would error and subsequently cause a disconnect if a partial act was sent to the state machine, even if future calls could be added to the read buffer to create a valid act. Add appropriate unit tests and update fuzz testing to send partial acts some of the time.
1 parent 8c6666b commit b2cffa5

File tree

2 files changed

+76
-23
lines changed

2 files changed

+76
-23
lines changed

lightning/src/ln/peers/handshake/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,15 +173,17 @@ mod test {
173173
#[test]
174174
fn errors_properly_returned() {
175175
let mut test_ctx = TestCtx::new();
176-
assert_matches!(test_ctx.inbound_handshake.process_act(&[]).err(), Some(_));
176+
let invalid_act1 = [0; 50];
177+
assert_matches!(test_ctx.inbound_handshake.process_act(&invalid_act1).err(), Some(_));
177178
}
178179

179180
// Test that any use of the PeerHandshake after returning an error panics
180181
#[test]
181182
#[should_panic(expected = "called `Option::unwrap()` on a `None` value")]
182183
fn use_after_error_panics() {
183184
let mut test_ctx = TestCtx::new();
184-
assert_matches!(test_ctx.inbound_handshake.process_act(&[]).err(), Some(_));
185+
let invalid_act1 = [0; 50];
186+
assert_matches!(test_ctx.inbound_handshake.process_act(&invalid_act1).err(), Some(_));
185187
test_ctx.inbound_handshake.process_act(&[]).unwrap();
186188
}
187189
}

lightning/src/ln/peers/handshake/states.rs

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,22 @@ impl IHandshakeState for ResponderAwaitingActOneState {
181181
let mut read_buffer = self.read_buffer;
182182
read_buffer.extend_from_slice(input);
183183

184+
// In the event of a partial fill, stay in the same state and wait for more data
185+
if read_buffer.len() < ACT_ONE_TWO_LENGTH {
186+
return Ok((
187+
None,
188+
HandshakeState::ResponderAwaitingActOne(Self {
189+
responder_static_private_key: self.responder_static_private_key,
190+
responder_ephemeral_private_key: self.responder_ephemeral_private_key,
191+
responder_ephemeral_public_key: self.responder_ephemeral_public_key,
192+
chaining_key: self.chaining_key,
193+
hash: self.hash,
194+
read_buffer
195+
})
196+
));
197+
}
198+
199+
184200
let hash = self.hash;
185201
let responder_static_private_key = self.responder_static_private_key;
186202
let chaining_key = self.chaining_key;
@@ -222,6 +238,22 @@ impl IHandshakeState for InitiatorAwaitingActTwoState {
222238
let mut read_buffer = self.read_buffer;
223239
read_buffer.extend_from_slice(input);
224240

241+
// In the event of a partial fill, stay in the same state and wait for more data
242+
if read_buffer.len() < ACT_ONE_TWO_LENGTH {
243+
return Ok((
244+
None,
245+
HandshakeState::InitiatorAwaitingActTwo(Self {
246+
initiator_static_private_key: self.initiator_static_private_key,
247+
initiator_static_public_key: self.initiator_static_public_key,
248+
initiator_ephemeral_private_key: self.initiator_ephemeral_private_key,
249+
responder_static_public_key: self.responder_static_public_key,
250+
chaining_key: self.chaining_key,
251+
hash: self.hash,
252+
read_buffer
253+
})
254+
));
255+
}
256+
225257
let initiator_static_private_key = self.initiator_static_private_key;
226258
let initiator_static_public_key = self.initiator_static_public_key;
227259
let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key;
@@ -287,8 +319,18 @@ impl IHandshakeState for ResponderAwaitingActThreeState {
287319
let mut read_buffer = self.read_buffer;
288320
read_buffer.extend_from_slice(input);
289321

322+
// In the event of a partial fill, stay in the same state and wait for more data
290323
if read_buffer.len() < ACT_THREE_LENGTH {
291-
return Err("need at least 66 bytes".to_string());
324+
return Ok((
325+
None,
326+
HandshakeState::ResponderAwaitingActThree(Self {
327+
hash: self.hash,
328+
responder_ephemeral_private_key: self.responder_ephemeral_private_key,
329+
chaining_key: self.chaining_key,
330+
temporary_key: self.temporary_key,
331+
read_buffer
332+
})
333+
));
292334
}
293335

294336
let hash = self.hash;
@@ -407,10 +449,6 @@ fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_e
407449
// https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (receiver)
408450
fn process_act_message(read_buffer: &mut Vec<u8>, local_private_key: &SecretKey, chaining_key: ChainingKey, hash: Sha256) -> Result<(PublicKey, Sha256, SymmetricKey, SymmetricKey), String> {
409451
// 1. Read exactly 50 bytes from the network buffer
410-
if read_buffer.len() < ACT_ONE_TWO_LENGTH {
411-
return Err("need at least 50 bytes".to_string());
412-
}
413-
414452
let act_bytes: Vec<u8> = read_buffer.drain(..ACT_ONE_TWO_LENGTH).collect();
415453

416454
// 2.Parse the read message (m) into v, re, and c
@@ -503,9 +541,9 @@ mod test {
503541
let responder = ResponderAwaitingActOneState::new(responder_static_private_key, responder_ephemeral_private_key);
504542

505543
TestCtx {
506-
initiator: HandshakeState::InitiatorStarting(initiator),
544+
initiator: InitiatorStarting(initiator),
507545
initiator_public_key,
508-
responder: HandshakeState::ResponderAwaitingActOne(responder),
546+
responder: ResponderAwaitingActOne(responder),
509547
responder_static_public_key,
510548
valid_act1: hex::decode("00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a").unwrap(),
511549
valid_act2: hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae").unwrap(),
@@ -565,8 +603,6 @@ mod test {
565603
// Responder::AwaitingActOne -> AwaitingActThree
566604
// TODO: Should this fail since we don't expect data > ACT_ONE_TWO_LENGTH and likely indicates
567605
// a bad peer?
568-
// TODO: Should the behavior be changed to handle act1 data that is striped across multiple
569-
// next() calls?
570606
#[test]
571607
fn awaiting_act_one_to_awaiting_act_three_input_extra_bytes() {
572608
let test_ctx = TestCtx::new();
@@ -578,13 +614,18 @@ mod test {
578614
assert_matches!(awaiting_act_three_state, ResponderAwaitingActThree(_));
579615
}
580616

581-
// Responder::AwaitingActOne -> Error (input too small)
617+
// Responder::AwaitingActOne -> AwaitingActThree (segmented calls)
582618
// RFC test vector: transport-responder act1 short read test
619+
// Divergence from RFC tests due to not reading directly from the socket (partial message OK)
583620
#[test]
584-
fn awaiting_act_one_to_awaiting_act_three_input_too_small() {
621+
fn awaiting_act_one_to_awaiting_act_three_segmented() {
585622
let test_ctx = TestCtx::new();
586-
let act1 = hex::decode("00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c").unwrap();
587-
assert_eq!(test_ctx.responder.next(&act1).err(), Some(String::from("need at least 50 bytes")))
623+
let act1_partial1 = &test_ctx.valid_act1[..25];
624+
let act1_partial2 = &test_ctx.valid_act1[25..];
625+
626+
let next_state = test_ctx.responder.next(&act1_partial1).unwrap();
627+
assert_matches!(next_state, (None, ResponderAwaitingActOne(_)));
628+
assert_matches!(next_state.1.next(&act1_partial2).unwrap(), (Some(_), ResponderAwaitingActThree(_)));
588629
}
589630

590631
// Responder::AwaitingActOne -> Error (bad version byte)
@@ -659,15 +700,20 @@ mod test {
659700
assert_eq!(100, conduit.decryptor.read_buffer_length());
660701
}
661702

662-
// Initiator::AwaitingActTwo -> Error (input too small)
703+
// Initiator::AwaitingActTwo -> Complete (segmented calls)
663704
// RFC test vector: transport-initiator act2 short read test
705+
// Divergence from RFC tests due to not reading directly from the socket (partial message OK)
664706
#[test]
665-
fn awaiting_act_two_input_too_small() {
707+
fn awaiting_act_two_to_complete_segmented() {
666708
let test_ctx = TestCtx::new();
667709
let (_act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]);
668-
let act2 = hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730").unwrap();
669710

670-
assert_eq!(awaiting_act_two_state.next(&act2).err(), Some(String::from("need at least 50 bytes")));
711+
let act2_partial1 = &test_ctx.valid_act2[..25];
712+
let act2_partial2 = &test_ctx.valid_act2[25..];
713+
714+
let next_state = awaiting_act_two_state.next(&act2_partial1).unwrap();
715+
assert_matches!(next_state, (None, InitiatorAwaitingActTwo(_)));
716+
assert_matches!(next_state.1.next(&act2_partial2).unwrap(), (Some(_), Complete(_)));
671717
}
672718

673719
// Initiator::AwaitingActTwo -> Error (bad version byte)
@@ -751,15 +797,20 @@ mod test {
751797
assert_eq!(awaiting_act_three_state.next(&act3).err(), Some(String::from("unexpected version")));
752798
}
753799

754-
// Responder::AwaitingActThree -> Error (input too small)
800+
// Responder::AwaitingActThree -> Complete (segmented calls)
755801
// RFC test vector: transport-responder act3 short read test
802+
// Divergence from RFC tests due to not reading directly from the socket (partial message OK)
756803
#[test]
757-
fn awaiting_act_three_input_too_small() {
804+
fn awaiting_act_three_to_complete_segmented() {
758805
let test_ctx = TestCtx::new();
759806
let (_act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &test_ctx.valid_act1);
760-
let act3 = hex::decode("00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139").unwrap();
761807

762-
assert_eq!(awaiting_act_three_state.next(&act3).err(), Some(String::from("need at least 66 bytes")));
808+
let act3_partial1 = &test_ctx.valid_act3[..35];
809+
let act3_partial2 = &test_ctx.valid_act3[35..];
810+
811+
let next_state = awaiting_act_three_state.next(&act3_partial1).unwrap();
812+
assert_matches!(next_state, (None, ResponderAwaitingActThree(_)));
813+
assert_matches!(next_state.1.next(&act3_partial2), Ok((None, Complete(_))));
763814
}
764815

765816
// Responder::AwaitingActThree -> Error (invalid hmac)

0 commit comments

Comments
 (0)