|
12 | 12 | use crate::io; |
13 | 13 | use crate::ln::msgs::DecodeError; |
14 | 14 | use crate::util::ser::CursorReadable; |
15 | | -use bech32::primitives::decode::CheckedHrpstringError; |
| 15 | +use bech32::primitives::decode::{CheckedHrpstringError, PaddingError}; |
16 | 16 | use bitcoin::secp256k1; |
17 | 17 |
|
18 | 18 | #[allow(unused_imports)] |
@@ -76,6 +76,10 @@ mod sealed { |
76 | 76 | return Err(Bolt12ParseError::InvalidBech32Hrp); |
77 | 77 | } |
78 | 78 |
|
| 79 | + // Validate that bech32 padding is valid per BIP-173: |
| 80 | + // "Any incomplete group at the end MUST be 4 bits or less, MUST be all zeroes" |
| 81 | + parsed.validate_segwit_padding()?; |
| 82 | + |
79 | 83 | let data = parsed.byte_iter().collect::<Vec<u8>>(); |
80 | 84 | Self::try_from(data) |
81 | 85 | } |
@@ -146,6 +150,11 @@ pub enum Bolt12ParseError { |
146 | 150 | /// This is not exported to bindings users as the details don't matter much |
147 | 151 | CheckedHrpstringError, |
148 | 152 | ), |
| 153 | + /// The bech32 data has invalid padding per BIP-173 (more than 4 bits or non-zero padding). |
| 154 | + InvalidPadding( |
| 155 | + /// This is not exported to bindings users as the details don't matter much |
| 156 | + PaddingError, |
| 157 | + ), |
149 | 158 | /// The bech32 decoded string could not be decoded as the expected message type. |
150 | 159 | Decode(DecodeError), |
151 | 160 | /// The parsed message has invalid semantics. |
@@ -232,6 +241,12 @@ impl From<CheckedHrpstringError> for Bolt12ParseError { |
232 | 241 | } |
233 | 242 | } |
234 | 243 |
|
| 244 | +impl From<PaddingError> for Bolt12ParseError { |
| 245 | + fn from(error: PaddingError) -> Self { |
| 246 | + Self::InvalidPadding(error) |
| 247 | + } |
| 248 | +} |
| 249 | + |
235 | 250 | impl From<DecodeError> for Bolt12ParseError { |
236 | 251 | fn from(error: DecodeError) -> Self { |
237 | 252 | Self::Decode(error) |
@@ -326,7 +341,7 @@ mod bolt12_tests { |
326 | 341 |
|
327 | 342 | #[cfg(test)] |
328 | 343 | mod tests { |
329 | | - use super::Bolt12ParseError; |
| 344 | + use super::{Bolt12ParseError, PaddingError}; |
330 | 345 | use crate::ln::msgs::DecodeError; |
331 | 346 | use crate::offers::offer::Offer; |
332 | 347 | use bech32::primitives::decode::{CharError, CheckedHrpstringError, UncheckedHrpstringError}; |
@@ -371,4 +386,15 @@ mod tests { |
371 | 386 | Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::InvalidValue)), |
372 | 387 | } |
373 | 388 | } |
| 389 | + |
| 390 | + #[test] |
| 391 | + fn fails_parsing_bech32_encoded_offer_with_invalid_padding() { |
| 392 | + // BOLT 12 test vector for invalid bech32 padding |
| 393 | + // See: https://github.com/lightning/bolts/pull/1312 |
| 394 | + let encoded_offer = "lno1zcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pkseseq"; |
| 395 | + match encoded_offer.parse::<Offer>() { |
| 396 | + Ok(_) => panic!("Valid offer: {}", encoded_offer), |
| 397 | + Err(e) => assert_eq!(e, Bolt12ParseError::InvalidPadding(PaddingError::TooMuch)), |
| 398 | + } |
| 399 | + } |
374 | 400 | } |
0 commit comments