diff --git a/lightning/src/offers/offer.rs b/lightning/src/offers/offer.rs index 7ad3c282c77..5592c50a264 100644 --- a/lightning/src/offers/offer.rs +++ b/lightning/src/offers/offer.rs @@ -2528,5 +2528,12 @@ mod bolt12_tests { "lno1pgx9getnwss8vetrw3hhyucsespjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k8qzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqpqqqqqqqqqqqqqqqqqqqqqqqqqqqzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqzq3zyg3zyg3zygszqqqqyqqqqsqqvpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsq".parse::(), Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)), ); + + // Bech32 padding exceeds 4-bit limit (BOLT 12 test vector) + // See: https://github.com/lightning/bolts/pull/1312 + assert!(matches!( + "lno1zcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pkseseq".parse::(), + Err(Bolt12ParseError::InvalidPadding(_)) + )); } } diff --git a/lightning/src/offers/parse.rs b/lightning/src/offers/parse.rs index 99dd1bb938d..df71e860d2d 100644 --- a/lightning/src/offers/parse.rs +++ b/lightning/src/offers/parse.rs @@ -12,7 +12,7 @@ use crate::io; use crate::ln::msgs::DecodeError; use crate::util::ser::CursorReadable; -use bech32::primitives::decode::CheckedHrpstringError; +use bech32::primitives::decode::{CheckedHrpstringError, PaddingError}; use bitcoin::secp256k1; #[allow(unused_imports)] @@ -76,6 +76,10 @@ mod sealed { return Err(Bolt12ParseError::InvalidBech32Hrp); } + // Validate that bech32 padding is valid per BIP-173: + // "Any incomplete group at the end MUST be 4 bits or less, MUST be all zeroes" + parsed.validate_segwit_padding()?; + let data = parsed.byte_iter().collect::>(); Self::try_from(data) } @@ -146,6 +150,11 @@ pub enum Bolt12ParseError { /// This is not exported to bindings users as the details don't matter much CheckedHrpstringError, ), + /// The bech32 data has invalid padding per BIP-173 (more than 4 bits or non-zero padding). + InvalidPadding( + /// This is not exported to bindings users as the details don't matter much + PaddingError, + ), /// The bech32 decoded string could not be decoded as the expected message type. Decode(DecodeError), /// The parsed message has invalid semantics. @@ -232,6 +241,12 @@ impl From for Bolt12ParseError { } } +impl From for Bolt12ParseError { + fn from(error: PaddingError) -> Self { + Self::InvalidPadding(error) + } +} + impl From for Bolt12ParseError { fn from(error: DecodeError) -> Self { Self::Decode(error) @@ -326,7 +341,7 @@ mod bolt12_tests { #[cfg(test)] mod tests { - use super::Bolt12ParseError; + use super::{Bolt12ParseError, PaddingError}; use crate::ln::msgs::DecodeError; use crate::offers::offer::Offer; use bech32::primitives::decode::{CharError, CheckedHrpstringError, UncheckedHrpstringError}; @@ -371,4 +386,15 @@ mod tests { Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::InvalidValue)), } } + + #[test] + fn fails_parsing_bech32_encoded_offer_with_invalid_padding() { + // BOLT 12 test vector for invalid bech32 padding + // See: https://github.com/lightning/bolts/pull/1312 + let encoded_offer = "lno1zcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pkseseq"; + match encoded_offer.parse::() { + Ok(_) => panic!("Valid offer: {}", encoded_offer), + Err(e) => assert_eq!(e, Bolt12ParseError::InvalidPadding(PaddingError::TooMuch)), + } + } }