Skip to content

Commit 91e633a

Browse files
committed
fix(bolt12): Add ASCII validation for offer currency field
Validate that offer_currency contains valid ASCII bytes as required by BOLT12 specification. ISO 4217 currency codes must be valid 3-letter ASCII strings, which are a subset of UTF-8.
1 parent 78fee88 commit 91e633a

File tree

2 files changed

+41
-0
lines changed

2 files changed

+41
-0
lines changed

lightning/src/offers/offer.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,15 @@ impl TryFrom<FullOfferTlvStream> for OfferContents {
12161216
return Err(Bolt12SemanticError::MissingDescription);
12171217
}
12181218

1219+
if let Some(currency_bytes) = currency {
1220+
let currency_str = core::str::from_utf8(&currency_bytes)
1221+
.map_err(|_| Bolt12SemanticError::InvalidCurrencyCode)?;
1222+
1223+
if !currency_str.chars().all(|c| c.is_ascii_uppercase()) {
1224+
return Err(Bolt12SemanticError::InvalidCurrencyCode);
1225+
}
1226+
}
1227+
12191228
let features = features.unwrap_or_else(OfferFeatures::empty);
12201229

12211230
let absolute_expiry =
@@ -1820,6 +1829,36 @@ mod tests {
18201829
Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidAmount)
18211830
),
18221831
}
1832+
1833+
let mut tlv_stream = offer.as_tlv_stream();
1834+
tlv_stream.0.amount = Some(1000);
1835+
tlv_stream.0.currency = Some(b"\xFF\xFE\xFD"); // invalid UTF-8 bytes
1836+
1837+
let mut encoded_offer = Vec::new();
1838+
tlv_stream.write(&mut encoded_offer).unwrap();
1839+
1840+
match Offer::try_from(encoded_offer) {
1841+
Ok(_) => panic!("expected error"),
1842+
Err(e) => assert_eq!(
1843+
e,
1844+
Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidCurrencyCode)
1845+
),
1846+
}
1847+
1848+
let mut tlv_stream = offer.as_tlv_stream();
1849+
tlv_stream.0.amount = Some(1000);
1850+
tlv_stream.0.currency = Some(b"usd"); // invalid ISO 4217 code
1851+
1852+
let mut encoded_offer = Vec::new();
1853+
tlv_stream.write(&mut encoded_offer).unwrap();
1854+
1855+
match Offer::try_from(encoded_offer) {
1856+
Ok(_) => panic!("expected error"),
1857+
Err(e) => assert_eq!(
1858+
e,
1859+
Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidCurrencyCode)
1860+
),
1861+
}
18231862
}
18241863

18251864
#[test]

lightning/src/offers/parse.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ pub enum Bolt12SemanticError {
149149
MissingAmount,
150150
/// The amount exceeded the total bitcoin supply or didn't match an expected amount.
151151
InvalidAmount,
152+
/// The currency code did not contain valid ASCII uppercase letters.
153+
InvalidCurrencyCode,
152154
/// An amount was provided but was not sufficient in value.
153155
InsufficientAmount,
154156
/// An amount was provided but was not expected.

0 commit comments

Comments
 (0)