Skip to content

Commit 293543b

Browse files
committed
Use different iv_bytes for blinded path metadata
Best practice is to use different IV bytes for different contexts. Update Offer and Refund metadata computation to use different IV bytes when the metadata is included in a blinded path. For invoice requests, the metatdata will always be in the blinded path, so it remains the same.
1 parent 8849efe commit 293543b

File tree

3 files changed

+38
-25
lines changed

3 files changed

+38
-25
lines changed

lightning/src/offers/invoice.rs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ use crate::blinded_path::BlindedPath;
114114
use crate::ln::types::PaymentHash;
115115
use crate::ln::channelmanager::PaymentId;
116116
use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures, InvoiceRequestFeatures, OfferFeatures};
117-
use crate::ln::inbound_payment::ExpandedKey;
117+
use crate::ln::inbound_payment::{ExpandedKey, IV_LEN};
118118
use crate::ln::msgs::DecodeError;
119119
use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_methods_common};
120120
use crate::offers::invoice_request::{INVOICE_REQUEST_PAYER_ID_TYPE, INVOICE_REQUEST_TYPES, IV_BYTES as INVOICE_REQUEST_IV_BYTES, InvoiceRequest, InvoiceRequestContents, InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef};
@@ -123,7 +123,7 @@ use crate::offers::nonce::Nonce;
123123
use crate::offers::offer::{Amount, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity};
124124
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
125125
use crate::offers::payer::{PAYER_METADATA_TYPE, PayerTlvStream, PayerTlvStreamRef};
126-
use crate::offers::refund::{IV_BYTES as REFUND_IV_BYTES, Refund, RefundContents};
126+
use crate::offers::refund::{IV_BYTES_WITH_METADATA as REFUND_IV_BYTES_WITH_METADATA, IV_BYTES_WITHOUT_METADATA as REFUND_IV_BYTES_WITHOUT_METADATA, Refund, RefundContents};
127127
use crate::offers::signer::{Metadata, self};
128128
use crate::util::ser::{HighZeroBytesDroppedBigSize, Iterable, Readable, SeekReadable, WithoutLength, Writeable, Writer};
129129
use crate::util::string::PrintableString;
@@ -778,11 +778,15 @@ impl Bolt12Invoice {
778778
pub fn verify_using_metadata<T: secp256k1::Signing>(
779779
&self, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
780780
) -> Result<PaymentId, ()> {
781-
let metadata = match &self.contents {
782-
InvoiceContents::ForOffer { invoice_request, .. } => &invoice_request.inner.payer.0,
783-
InvoiceContents::ForRefund { refund, .. } => &refund.payer.0,
781+
let (metadata, iv_bytes) = match &self.contents {
782+
InvoiceContents::ForOffer { invoice_request, .. } => {
783+
(&invoice_request.inner.payer.0, INVOICE_REQUEST_IV_BYTES)
784+
},
785+
InvoiceContents::ForRefund { refund, .. } => {
786+
(&refund.payer.0, REFUND_IV_BYTES_WITH_METADATA)
787+
},
784788
};
785-
self.contents.verify(TlvStream::new(&self.bytes), metadata, key, secp_ctx)
789+
self.contents.verify(TlvStream::new(&self.bytes), metadata, key, iv_bytes, secp_ctx)
786790
}
787791

788792
/// Verifies that the invoice was for a request or refund created using the given key by
@@ -792,7 +796,11 @@ impl Bolt12Invoice {
792796
&self, payment_id: PaymentId, nonce: Nonce, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
793797
) -> Result<PaymentId, ()> {
794798
let metadata = Metadata::payer_data(payment_id, nonce, key);
795-
self.contents.verify(TlvStream::new(&self.bytes), &metadata, key, secp_ctx)
799+
let iv_bytes = match &self.contents {
800+
InvoiceContents::ForOffer { .. } => INVOICE_REQUEST_IV_BYTES,
801+
InvoiceContents::ForRefund { .. } => REFUND_IV_BYTES_WITHOUT_METADATA,
802+
};
803+
self.contents.verify(TlvStream::new(&self.bytes), &metadata, key, iv_bytes, secp_ctx)
796804
.and_then(|extracted_payment_id| (payment_id == extracted_payment_id)
797805
.then(|| payment_id)
798806
.ok_or(())
@@ -1028,7 +1036,7 @@ impl InvoiceContents {
10281036

10291037
fn verify<T: secp256k1::Signing>(
10301038
&self, tlv_stream: TlvStream<'_>, metadata: &Metadata, key: &ExpandedKey,
1031-
secp_ctx: &Secp256k1<T>
1039+
iv_bytes: &[u8; IV_LEN], secp_ctx: &Secp256k1<T>
10321040
) -> Result<PaymentId, ()> {
10331041
let offer_records = tlv_stream.clone().range(OFFER_TYPES);
10341042
let invreq_records = tlv_stream.range(INVOICE_REQUEST_TYPES).filter(|record| {
@@ -1041,11 +1049,6 @@ impl InvoiceContents {
10411049
let tlv_stream = offer_records.chain(invreq_records);
10421050

10431051
let payer_id = self.payer_id();
1044-
let iv_bytes = match self {
1045-
InvoiceContents::ForOffer { .. } => INVOICE_REQUEST_IV_BYTES,
1046-
InvoiceContents::ForRefund { .. } => REFUND_IV_BYTES,
1047-
};
1048-
10491052
signer::verify_payer_metadata(
10501053
metadata.as_ref(), key, iv_bytes, payer_id, tlv_stream, secp_ctx,
10511054
)

lightning/src/offers/offer.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ use crate::prelude::*;
112112
#[cfg(feature = "std")]
113113
use std::time::SystemTime;
114114

115-
pub(super) const IV_BYTES: &[u8; IV_LEN] = b"LDK Offer ~~~~~~";
115+
pub(super) const IV_BYTES_WITH_METADATA: &[u8; IV_LEN] = b"LDK Offer ~~~~~~";
116+
pub(super) const IV_BYTES_WITHOUT_METADATA: &[u8; IV_LEN] = b"LDK Offer v2~~~~";
116117

117118
/// An identifier for an [`Offer`] built using [`DerivedMetadata`].
118119
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -391,9 +392,12 @@ macro_rules! offer_builder_methods { (
391392

392393
// Don't derive keys if no blinded paths were given since this means the signing
393394
// pubkey must be the node id of an announced node.
394-
if $self.offer.paths.is_none() {
395+
let iv_bytes = if $self.offer.paths.is_none() {
395396
metadata = metadata.without_keys();
396-
}
397+
IV_BYTES_WITH_METADATA
398+
} else {
399+
IV_BYTES_WITHOUT_METADATA
400+
};
397401

398402
let mut tlv_stream = $self.offer.as_tlv_stream();
399403
debug_assert_eq!(tlv_stream.metadata, None);
@@ -406,7 +410,7 @@ macro_rules! offer_builder_methods { (
406410
// for verification. In the former case, the blinded paths must include
407411
// `OffersContext::InvoiceRequest` instead.
408412
let (derived_metadata, keys) =
409-
metadata.derive_from(IV_BYTES, tlv_stream, $self.secp_ctx);
413+
metadata.derive_from(iv_bytes, tlv_stream, $self.secp_ctx);
410414
match keys {
411415
Some(keys) => $self.offer.signing_pubkey = Some(keys.public_key()),
412416
None => $self.offer.metadata = Some(derived_metadata),
@@ -919,18 +923,20 @@ impl OfferContents {
919923
pub(super) fn verify_using_metadata<T: secp256k1::Signing>(
920924
&self, bytes: &[u8], key: &ExpandedKey, secp_ctx: &Secp256k1<T>
921925
) -> Result<(OfferId, Option<Keypair>), ()> {
922-
self.verify(bytes, self.metadata.as_ref(), key, secp_ctx)
926+
self.verify(bytes, self.metadata.as_ref(), key, IV_BYTES_WITH_METADATA, secp_ctx)
923927
}
924928

925929
pub(super) fn verify_using_recipient_data<T: secp256k1::Signing>(
926930
&self, bytes: &[u8], nonce: Nonce, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
927931
) -> Result<(OfferId, Option<Keypair>), ()> {
928-
self.verify(bytes, Some(&Metadata::RecipientData(nonce)), key, secp_ctx)
932+
let metadata = Metadata::RecipientData(nonce);
933+
self.verify(bytes, Some(&metadata), key, IV_BYTES_WITHOUT_METADATA, secp_ctx)
929934
}
930935

931936
/// Verifies that the offer metadata was produced from the offer in the TLV stream.
932937
fn verify<T: secp256k1::Signing>(
933-
&self, bytes: &[u8], metadata: Option<&Metadata>, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
938+
&self, bytes: &[u8], metadata: Option<&Metadata>, key: &ExpandedKey,
939+
iv_bytes: &[u8; IV_LEN], secp_ctx: &Secp256k1<T>
934940
) -> Result<(OfferId, Option<Keypair>), ()> {
935941
match metadata {
936942
Some(metadata) => {
@@ -946,7 +952,7 @@ impl OfferContents {
946952
None => return Err(()),
947953
};
948954
let keys = signer::verify_recipient_metadata(
949-
metadata.as_ref(), key, IV_BYTES, signing_pubkey, tlv_stream, secp_ctx
955+
metadata.as_ref(), key, iv_bytes, signing_pubkey, tlv_stream, secp_ctx
950956
)?;
951957

952958
let offer_id = OfferId::from_valid_invreq_tlv_stream(bytes);

lightning/src/offers/refund.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ use crate::prelude::*;
122122
#[cfg(feature = "std")]
123123
use std::time::SystemTime;
124124

125-
pub(super) const IV_BYTES: &[u8; IV_LEN] = b"LDK Refund ~~~~~";
125+
pub(super) const IV_BYTES_WITH_METADATA: &[u8; IV_LEN] = b"LDK Refund ~~~~~";
126+
pub(super) const IV_BYTES_WITHOUT_METADATA: &[u8; IV_LEN] = b"LDK Refund v2~~~";
126127

127128
/// Builds a [`Refund`] for the "offer for money" flow.
128129
///
@@ -306,9 +307,12 @@ macro_rules! refund_builder_methods { (
306307
if $self.refund.payer.0.has_derivation_material() {
307308
let mut metadata = core::mem::take(&mut $self.refund.payer.0);
308309

309-
if $self.refund.paths.is_none() {
310+
let iv_bytes = if $self.refund.paths.is_none() {
310311
metadata = metadata.without_keys();
311-
}
312+
IV_BYTES_WITH_METADATA
313+
} else {
314+
IV_BYTES_WITHOUT_METADATA
315+
};
312316

313317
let mut tlv_stream = $self.refund.as_tlv_stream();
314318
tlv_stream.0.metadata = None;
@@ -317,7 +321,7 @@ macro_rules! refund_builder_methods { (
317321
}
318322

319323
let (derived_metadata, keys) =
320-
metadata.derive_from(IV_BYTES, tlv_stream, $self.secp_ctx);
324+
metadata.derive_from(iv_bytes, tlv_stream, $self.secp_ctx);
321325
metadata = derived_metadata;
322326
if let Some(keys) = keys {
323327
$self.refund.payer_id = keys.public_key();

0 commit comments

Comments
 (0)