Skip to content

Commit 49bccc2

Browse files
committed
Don't over-allocate invoice bytes
When allocating space for an invoice's bytes, it was assumed that the bytes used for the invoice request signature are the same length as those used for the invoice's signature. However, fuzz testing revealed that this isn't always the case since an invoice request could contain more than one signature TLV. Account for this when determining the number of bytes to allocate for the invoice. This comes at the expense of an additional traversal of all TLVs in the invoice request through the end of SIGNATURE_TYPES (i.e., every TLV except experimental ones).
1 parent 42cc4e7 commit 49bccc2

File tree

1 file changed

+19
-4
lines changed

1 file changed

+19
-4
lines changed

lightning/src/offers/invoice.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_me
122122
#[cfg(test)]
123123
use crate::offers::invoice_macros::invoice_builder_methods_test;
124124
use crate::offers::invoice_request::{EXPERIMENTAL_INVOICE_REQUEST_TYPES, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceRequestTlvStreamRef, INVOICE_REQUEST_PAYER_ID_TYPE, INVOICE_REQUEST_TYPES, IV_BYTES as INVOICE_REQUEST_IV_BYTES, InvoiceRequest, InvoiceRequestContents, InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef};
125-
use crate::offers::merkle::{SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, self, SIGNATURE_TLV_RECORD_SIZE};
125+
use crate::offers::merkle::{SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, self, SIGNATURE_TLV_RECORD_SIZE, SIGNATURE_TYPES};
126126
use crate::offers::nonce::Nonce;
127127
use crate::offers::offer::{Amount, EXPERIMENTAL_OFFER_TYPES, ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity};
128128
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
@@ -502,17 +502,32 @@ impl UnsignedBolt12Invoice {
502502
let (_, _, _, invoice_tlv_stream, _, _, experimental_invoice_tlv_stream) =
503503
contents.as_tlv_stream();
504504

505+
let mut signature_tlv_stream = TlvStream::new(invreq_bytes)
506+
.range(SIGNATURE_TYPES)
507+
.peekable();
508+
let signature_len = signature_tlv_stream
509+
.peek()
510+
.map_or(SIGNATURE_TLV_RECORD_SIZE, |record| record.end - record.start);
511+
let signature_tlv_stream_start = signature_tlv_stream
512+
.peek()
513+
.map_or(0, |first_record| first_record.start);
514+
let signature_tlv_stream_end = signature_tlv_stream
515+
.last()
516+
.map_or(0, |last_record| last_record.end);
517+
let signature_tlv_stream_len = signature_tlv_stream_end - signature_tlv_stream_start;
518+
505519
// Allocate enough space for the invoice, which will include:
506520
// - all TLV records from `invreq_bytes` except signatures,
507521
// - all invoice-specific TLV records, and
508522
// - a signature TLV record once the invoice is signed.
509523
//
510-
// This assumes both the invoice request and the invoice will each only have one signature
511-
// using SIGNATURE_TYPES.start as the TLV record. Thus, it is accounted for by invreq_bytes.
524+
// This assumes the invoice will only have one signature using the same number of bytes as
525+
// the first (and probably only) signature from the invoice request.
512526
let mut bytes = Vec::with_capacity(
513527
invreq_bytes.len()
528+
- signature_tlv_stream_len
514529
+ invoice_tlv_stream.serialized_length()
515-
+ if contents.is_for_offer() { 0 } else { SIGNATURE_TLV_RECORD_SIZE }
530+
+ signature_len
516531
+ experimental_invoice_tlv_stream.serialized_length(),
517532
);
518533

0 commit comments

Comments
 (0)