Skip to content

Commit 91c2906

Browse files
committed
WIP: Limit TLV stream decoding to type ranges
1 parent 3db190e commit 91c2906

File tree

7 files changed

+95
-18
lines changed

7 files changed

+95
-18
lines changed

lightning/src/offers/invoice_request.rs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ use offers::merkle::{SignatureTlvStream, SignatureTlvStreamRef, self};
6060
use offers::offer::{Amount, Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef};
6161
use offers::parse::{ParseError, SemanticError};
6262
use offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef};
63-
use util::ser::{HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
63+
use util::ser::{HighZeroBytesDroppedBigSize, SeekReadable, WithoutLength, Writeable, Writer};
6464

6565
use prelude::*;
6666

@@ -341,12 +341,14 @@ impl TryFrom<Vec<u8>> for InvoiceRequest {
341341
type Error = ParseError;
342342

343343
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
344-
let tlv_stream: FullInvoiceRequestTlvStream = Readable::read(&mut &bytes[..])?;
344+
let mut cursor = io::Cursor::new(bytes);
345+
let tlv_stream: FullInvoiceRequestTlvStream = SeekReadable::read(&mut cursor)?;
346+
let bytes = cursor.into_inner();
345347
InvoiceRequest::try_from((bytes, tlv_stream))
346348
}
347349
}
348350

349-
tlv_stream!(InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef, {
351+
tlv_stream!(InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef, 80..160, {
350352
(80, chain: ChainHash),
351353
(82, amount: (u64, HighZeroBytesDroppedBigSize)),
352354
(84, features: OfferFeatures),
@@ -448,3 +450,35 @@ impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
448450
})
449451
}
450452
}
453+
454+
#[cfg(test)]
455+
mod tests {
456+
use super::InvoiceRequest;
457+
458+
use bitcoin::secp256k1::{KeyPair, Secp256k1, SecretKey};
459+
use core::convert::TryFrom;
460+
use offers::offer::OfferBuilder;
461+
use util::ser::Writeable;
462+
463+
#[test]
464+
fn builds_invoice_request_with_amount_msats() {
465+
let secp_ctx = Secp256k1::new();
466+
let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
467+
let invoice_request = OfferBuilder::new("foo".into(), keys.public_key())
468+
.build()
469+
.unwrap()
470+
.request_invoice(keys.public_key())
471+
.amount_msats(1000)
472+
.build()
473+
.unwrap()
474+
.sign(|digest| secp_ctx.sign_schnorr_no_aux_rand(digest, &keys))
475+
.unwrap();
476+
assert_eq!(invoice_request.amount_msats(), Some(1000));
477+
478+
let mut buffer = Vec::new();
479+
invoice_request.write(&mut buffer).unwrap();
480+
481+
let invoice_request = InvoiceRequest::try_from(buffer).unwrap();
482+
assert_eq!(invoice_request.amount_msats(), Some(1000));
483+
}
484+
}

lightning/src/offers/merkle.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use prelude::*;
1919
/// Valid type range for signature TLV records.
2020
const SIGNATURE_TYPES: core::ops::RangeInclusive<u64> = 240..=1000;
2121

22-
tlv_stream!(SignatureTlvStream, SignatureTlvStreamRef, {
22+
tlv_stream!(SignatureTlvStream, SignatureTlvStreamRef, SIGNATURE_TYPES, {
2323
(240, signature: Signature),
2424
});
2525

lightning/src/offers/offer.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ use ln::msgs::MAX_VALUE_MSAT;
8181
use offers::invoice_request::InvoiceRequestBuilder;
8282
use offers::parse::{Bech32Encode, ParseError, SemanticError};
8383
use onion_message::BlindedPath;
84-
use util::ser::{HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
84+
use util::ser::{HighZeroBytesDroppedBigSize, SeekReadable, WithoutLength, Writeable, Writer};
8585

8686
use prelude::*;
8787

@@ -497,7 +497,9 @@ impl TryFrom<Vec<u8>> for Offer {
497497
type Error = ParseError;
498498

499499
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
500-
let tlv_stream: OfferTlvStream = Readable::read(&mut &bytes[..])?;
500+
let mut cursor = io::Cursor::new(bytes);
501+
let tlv_stream: OfferTlvStream = SeekReadable::read(&mut cursor)?;
502+
let bytes = cursor.into_inner();
501503
Offer::try_from((bytes, tlv_stream))
502504
}
503505
}
@@ -533,7 +535,7 @@ impl Amount {
533535
/// An ISO 4712 three-letter currency code (e.g., USD).
534536
pub type CurrencyCode = [u8; 3];
535537

536-
tlv_stream!(OfferTlvStream, OfferTlvStreamRef, {
538+
tlv_stream!(OfferTlvStream, OfferTlvStreamRef, 1..80, {
537539
(2, chains: (Vec<ChainHash>, WithoutLength)),
538540
(4, metadata: (Vec<u8>, WithoutLength)),
539541
(6, currency: CurrencyCode),

lightning/src/offers/parse.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@ use bitcoin::bech32;
1313
use bitcoin::bech32::{FromBase32, ToBase32};
1414
use bitcoin::secp256k1;
1515
use core::fmt;
16+
use io;
1617
use ln::msgs::DecodeError;
17-
use util::ser::Readable;
18+
use util::ser::SeekReadable;
1819

1920
use prelude::*;
2021

2122
/// Indicates a message can be encoded using bech32.
2223
pub(crate) trait Bech32Encode: AsRef<[u8]> {
2324
/// TLV stream that a bech32-encoded message is parsed into.
24-
type TlvStream: Readable;
25+
type TlvStream: SeekReadable;
2526

2627
/// Human readable part of the message's bech32 encoding.
2728
const BECH32_HRP: &'static str;
@@ -44,7 +45,10 @@ pub(crate) trait Bech32Encode: AsRef<[u8]> {
4445
}
4546

4647
let data = Vec::<u8>::from_base32(&data)?;
47-
Ok((Readable::read(&mut &data[..])?, data))
48+
let mut cursor = io::Cursor::new(data);
49+
let tlv_stream = SeekReadable::read(&mut cursor)?;
50+
let bytes = cursor.into_inner();
51+
Ok((tlv_stream, bytes))
4852
}
4953

5054
/// Formats the message using bech32-encoding.

lightning/src/offers/payer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ use prelude::*;
2020
#[derive(Clone, Debug)]
2121
pub(crate) struct PayerContents(pub Option<Vec<u8>>);
2222

23-
tlv_stream!(PayerTlvStream, PayerTlvStreamRef, {
23+
tlv_stream!(PayerTlvStream, PayerTlvStreamRef, 0..1, {
2424
(0, metadata: (Vec<u8>, WithoutLength)),
2525
});

lightning/src/util/ser.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//! as ChannelsManagers and ChannelMonitors.
1212
1313
use prelude::*;
14-
use io::{self, Read, Write};
14+
use io::{self, Read, Seek, Write};
1515
use io_extras::{copy, sink};
1616
use core::hash::Hash;
1717
use sync::Mutex;
@@ -220,6 +220,17 @@ pub trait Readable
220220
fn read<R: Read>(reader: &mut R) -> Result<Self, DecodeError>;
221221
}
222222

223+
/// A trait that various rust-lightning types implement allowing them to be read in from a
224+
/// `Read + Seek`.
225+
///
226+
/// (C-not exported) as we only export serialization to/from byte arrays instead
227+
pub trait SeekReadable
228+
where Self: Sized
229+
{
230+
/// Reads a Self in from the given Read
231+
fn read<R: Read + Seek>(reader: &mut R) -> Result<Self, DecodeError>;
232+
}
233+
223234
/// A trait that various higher-level rust-lightning types implement allowing them to be read in
224235
/// from a Read given some additional set of arguments which is required to deserialize.
225236
///
@@ -1026,6 +1037,16 @@ impl<A: Writeable, B: Writeable, C: Writeable, D: Writeable> Writeable for (A, B
10261037
}
10271038
}
10281039

1040+
impl<A: SeekReadable, B: SeekReadable, C: SeekReadable, D: SeekReadable> SeekReadable for (A, B, C, D) {
1041+
fn read<R: Read + Seek>(r: &mut R) -> Result<Self, DecodeError> {
1042+
let a: A = SeekReadable::read(r)?;
1043+
let b: B = SeekReadable::read(r)?;
1044+
let c: C = SeekReadable::read(r)?;
1045+
let d: D = SeekReadable::read(r)?;
1046+
Ok((a, b, c, d))
1047+
}
1048+
}
1049+
10291050
impl Writeable for () {
10301051
fn write<W: Writer>(&self, _: &mut W) -> Result<(), io::Error> {
10311052
Ok(())

lightning/src/util/ser_macros.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ macro_rules! decode_tlv {
197197

198198
macro_rules! decode_tlv_stream {
199199
($stream: expr, {$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}) => { {
200+
let rewind = |_, _| { unreachable!() };
201+
decode_tlv_stream_range!($stream, 0.., rewind, {$(($type, $field, $fieldty)),*});
202+
} }
203+
}
204+
205+
macro_rules! decode_tlv_stream_range {
206+
($stream: expr, $range: expr, $rewind: ident, {$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}) => { {
200207
use ln::msgs::DecodeError;
201208
let mut last_seen_type: Option<u64> = None;
202209
let mut stream_ref = $stream;
@@ -210,7 +217,7 @@ macro_rules! decode_tlv_stream {
210217
// UnexpectedEof. This should in every case be largely cosmetic, but its nice to
211218
// pass the TLV test vectors exactly, which requre this distinction.
212219
let mut tracking_reader = ser::ReadTrackingReader::new(&mut stream_ref);
213-
match ser::Readable::read(&mut tracking_reader) {
220+
match <ser::BigSize as ser::Readable>::read(&mut tracking_reader) {
214221
Err(DecodeError::ShortRead) => {
215222
if !tracking_reader.have_read {
216223
break 'tlv_read;
@@ -219,7 +226,13 @@ macro_rules! decode_tlv_stream {
219226
}
220227
},
221228
Err(e) => return Err(e),
222-
Ok(t) => t,
229+
Ok(t) => if $range.contains(&t.0) { t } else {
230+
use util::ser::Writeable;
231+
drop(tracking_reader);
232+
let bytes_read = t.serialized_length();
233+
$rewind(stream_ref, bytes_read);
234+
break 'tlv_read;
235+
},
223236
}
224237
};
225238

@@ -458,7 +471,7 @@ macro_rules! impl_writeable_tlv_based {
458471
/// [`Readable`]: crate::util::ser::Readable
459472
/// [`Writeable`]: crate::util::ser::Writeable
460473
macro_rules! tlv_stream {
461-
($name:ident, $nameref:ident, {
474+
($name:ident, $nameref:ident, $range:expr, {
462475
$(($type:expr, $field:ident : $fieldty:tt)),* $(,)*
463476
}) => {
464477
#[derive(Debug)]
@@ -483,12 +496,15 @@ macro_rules! tlv_stream {
483496
}
484497
}
485498

486-
impl ::util::ser::Readable for $name {
487-
fn read<R: $crate::io::Read>(reader: &mut R) -> Result<Self, ::ln::msgs::DecodeError> {
499+
impl ::util::ser::SeekReadable for $name {
500+
fn read<R: $crate::io::Read + $crate::io::Seek>(reader: &mut R) -> Result<Self, ::ln::msgs::DecodeError> {
488501
$(
489502
init_tlv_field_var!($field, option);
490503
)*
491-
decode_tlv_stream!(reader, {
504+
let rewind = |cursor: &mut R, offset: usize| {
505+
cursor.seek($crate::io::SeekFrom::Current(-(offset as i64))).expect("");
506+
};
507+
decode_tlv_stream_range!(reader, $range, rewind, {
492508
$(($type, $field, (option, encoding: $fieldty))),*
493509
});
494510

0 commit comments

Comments
 (0)