Skip to content

Commit 87ea15c

Browse files
committed
Check metadata length for bolt11 invoices
Check that the metadata when creating a bolt11 invoice is below the bolt11 limit of 639 bytes for tagged fields.
1 parent 1c1a4ad commit 87ea15c

1 file changed

Lines changed: 19 additions & 3 deletions

File tree

lightning-invoice/src/lib.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA: u64 = 18;
159159
/// consistency is more important.
160160
pub const MAX_LENGTH: usize = 7089;
161161

162+
/// The maximum length of a tagged field in a BOLT11 invoice. This is 1023 * 5 bits (i.e., 639
163+
/// bytes).
164+
pub const MAX_TAGGED_FIELD_DATA_BYTES: usize = 639;
165+
162166
/// The [`bech32::Bech32`] checksum algorithm, with extended max length suitable
163167
/// for BOLT11 invoices.
164168
pub enum Bolt11Bech32 {}
@@ -886,7 +890,11 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool>
886890
pub fn optional_payment_metadata(
887891
mut self, payment_metadata: Vec<u8>,
888892
) -> InvoiceBuilder<D, H, T, C, S, tb::True> {
889-
self.tagged_fields.push(TaggedField::PaymentMetadata(payment_metadata));
893+
if payment_metadata.len() > MAX_TAGGED_FIELD_DATA_BYTES {
894+
self.error = Some(CreationError::PaymentMetadataTooLong);
895+
} else {
896+
self.tagged_fields.push(TaggedField::PaymentMetadata(payment_metadata));
897+
}
890898
let mut found_features = false;
891899
for field in self.tagged_fields.iter_mut() {
892900
if let TaggedField::Features(f) = field {
@@ -1676,12 +1684,12 @@ impl TaggedField {
16761684
}
16771685

16781686
impl Description {
1679-
/// Creates a new `Description` if `description` is at most 1023 * 5 bits (i.e., 639 bytes)
1687+
/// Creates a new `Description` if `description` is at most [`MAX_TAGGED_FIELD_DATA_BYTES`]
16801688
/// long, and returns [`CreationError::DescriptionTooLong`] otherwise.
16811689
///
16821690
/// Please note that single characters may use more than one byte due to UTF8 encoding.
16831691
pub fn new(description: String) -> Result<Description, CreationError> {
1684-
if description.len() > 639 {
1692+
if description.len() > MAX_TAGGED_FIELD_DATA_BYTES {
16851693
Err(CreationError::DescriptionTooLong)
16861694
} else {
16871695
Ok(Description(UntrustedString(description)))
@@ -1798,6 +1806,9 @@ pub enum CreationError {
17981806
/// The supplied description string was longer than 639 __bytes__ (see [`Description::new`])
17991807
DescriptionTooLong,
18001808

1809+
/// The supplied payment metadata was longer than 639 __bytes__
1810+
PaymentMetadataTooLong,
1811+
18011812
/// The specified route has too many hops and can't be encoded
18021813
RouteTooLong,
18031814

@@ -1820,6 +1831,7 @@ impl Display for CreationError {
18201831
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
18211832
match self {
18221833
CreationError::DescriptionTooLong => f.write_str("The supplied description string was longer than 639 bytes"),
1834+
CreationError::PaymentMetadataTooLong => f.write_str("The supplied payment metadata was longer than 639 bytes"),
18231835
CreationError::RouteTooLong => f.write_str("The specified route has too many hops and can't be encoded"),
18241836
CreationError::TimestampOutOfBounds => f.write_str("The Unix timestamp of the supplied date is less than zero or greater than 35-bits"),
18251837
CreationError::InvalidAmount => f.write_str("The supplied millisatoshi amount was greater than the total bitcoin supply"),
@@ -2276,6 +2288,10 @@ mod test {
22762288
let long_desc_res = builder.clone().description(too_long_string).build_raw();
22772289
assert_eq!(long_desc_res, Err(CreationError::DescriptionTooLong));
22782290

2291+
let long_metadata_res =
2292+
builder.clone().description("Test".into()).payment_metadata(vec![0u8; 640]).build_raw();
2293+
assert_eq!(long_metadata_res, Err(CreationError::PaymentMetadataTooLong));
2294+
22792295
let route_hop = RouteHintHop {
22802296
src_node_id: PublicKey::from_slice(
22812297
&[

0 commit comments

Comments
 (0)