Skip to content

Commit c7fcdf6

Browse files
authored
Merge pull request #4701 from joostjager/fix-lsps5-case-replay
Reject case-varied LSPS5 replay signatures
2 parents 7cdcad2 + 3c128ed commit c7fcdf6

2 files changed

Lines changed: 15 additions & 3 deletions

File tree

lightning-liquidity/src/lsps5/validator.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
1212
use super::msgs::LSPS5ClientError;
1313

14-
use crate::alloc::string::ToString;
1514
use crate::lsps0::ser::LSPSDateTime;
1615
use crate::lsps5::msgs::WebhookNotification;
1716
use crate::sync::Mutex;
@@ -91,14 +90,17 @@ impl LSPS5Validator {
9190
}
9291

9392
fn check_for_replay_attack(&self, signature: &str) -> Result<(), LSPS5ClientError> {
93+
// zbase32 decoding accepts case aliases, so canonicalize the cache key
94+
// to match verification semantics without decoding the signature again.
95+
let signature = signature.to_ascii_lowercase();
9496
let mut signatures = self.recent_signatures.lock().unwrap();
95-
if signatures.contains(&signature.to_string()) {
97+
if signatures.contains(&signature) {
9698
return Err(LSPS5ClientError::ReplayAttack);
9799
}
98100
if signatures.len() == MAX_RECENT_SIGNATURES {
99101
signatures.pop_back();
100102
}
101-
signatures.push_front(signature.to_string());
103+
signatures.push_front(signature);
102104
Ok(())
103105
}
104106
}

lightning-liquidity/tests/lsps5_integration_tests.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,16 @@ fn replay_prevention_test() {
988988
assert!(replay_result.is_err(), "Immediate replay attack should be detected");
989989
assert_eq!(replay_result.unwrap_err(), LSPS5ClientError::ReplayAttack);
990990

991+
let case_modified_signature = signature.to_ascii_uppercase();
992+
assert_ne!(case_modified_signature, signature);
993+
let case_modified_replay_result =
994+
validator.validate(service_node_id, &timestamp, &case_modified_signature, &body);
995+
assert!(
996+
case_modified_replay_result.is_err(),
997+
"Immediate replay attack should be detected when the signature case changes"
998+
);
999+
assert_eq!(case_modified_replay_result.unwrap_err(), LSPS5ClientError::ReplayAttack);
1000+
9911001
// Fill up the validator's signature cache to push out the original signature.
9921002
for i in 0..MAX_RECENT_SIGNATURES {
9931003
// Advance time, allowing for another notification

0 commit comments

Comments
 (0)