]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Use different iv_bytes for blinded path metadata
authorJeffrey Czyz <jkczyz@gmail.com>
Tue, 23 Jul 2024 22:54:31 +0000 (17:54 -0500)
committerJeffrey Czyz <jkczyz@gmail.com>
Tue, 30 Jul 2024 21:06:24 +0000 (16:06 -0500)
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.

lightning/src/offers/invoice.rs
lightning/src/offers/offer.rs
lightning/src/offers/refund.rs

index eee71995e321c0325b1e5baa3fe961d8ce47cabe..76a4769cb58a1d783a6c8b63361242512a45d649 100644 (file)
@@ -114,7 +114,7 @@ use crate::blinded_path::BlindedPath;
 use crate::ln::types::PaymentHash;
 use crate::ln::channelmanager::PaymentId;
 use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures, InvoiceRequestFeatures, OfferFeatures};
-use crate::ln::inbound_payment::ExpandedKey;
+use crate::ln::inbound_payment::{ExpandedKey, IV_LEN};
 use crate::ln::msgs::DecodeError;
 use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_methods_common};
 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;
 use crate::offers::offer::{Amount, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity};
 use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
 use crate::offers::payer::{PAYER_METADATA_TYPE, PayerTlvStream, PayerTlvStreamRef};
-use crate::offers::refund::{IV_BYTES as REFUND_IV_BYTES, Refund, RefundContents};
+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};
 use crate::offers::signer::{Metadata, self};
 use crate::util::ser::{HighZeroBytesDroppedBigSize, Iterable, Readable, SeekReadable, WithoutLength, Writeable, Writer};
 use crate::util::string::PrintableString;
@@ -778,11 +778,15 @@ impl Bolt12Invoice {
        pub fn verify_using_metadata<T: secp256k1::Signing>(
                &self, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
        ) -> Result<PaymentId, ()> {
-               let metadata = match &self.contents {
-                       InvoiceContents::ForOffer { invoice_request, .. } => &invoice_request.inner.payer.0,
-                       InvoiceContents::ForRefund { refund, .. } => &refund.payer.0,
+               let (metadata, iv_bytes) = match &self.contents {
+                       InvoiceContents::ForOffer { invoice_request, .. } => {
+                               (&invoice_request.inner.payer.0, INVOICE_REQUEST_IV_BYTES)
+                       },
+                       InvoiceContents::ForRefund { refund, .. } => {
+                               (&refund.payer.0, REFUND_IV_BYTES_WITH_METADATA)
+                       },
                };
-               self.contents.verify(TlvStream::new(&self.bytes), metadata, key, secp_ctx)
+               self.contents.verify(TlvStream::new(&self.bytes), metadata, key, iv_bytes, secp_ctx)
        }
 
        /// Verifies that the invoice was for a request or refund created using the given key by
@@ -792,7 +796,11 @@ impl Bolt12Invoice {
                &self, payment_id: PaymentId, nonce: Nonce, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
        ) -> Result<PaymentId, ()> {
                let metadata = Metadata::payer_data(payment_id, nonce, key);
-               self.contents.verify(TlvStream::new(&self.bytes), &metadata, key, secp_ctx)
+               let iv_bytes = match &self.contents {
+                       InvoiceContents::ForOffer { .. } => INVOICE_REQUEST_IV_BYTES,
+                       InvoiceContents::ForRefund { .. } => REFUND_IV_BYTES_WITHOUT_METADATA,
+               };
+               self.contents.verify(TlvStream::new(&self.bytes), &metadata, key, iv_bytes, secp_ctx)
                        .and_then(|extracted_payment_id| (payment_id == extracted_payment_id)
                                .then(|| payment_id)
                                .ok_or(())
@@ -1028,7 +1036,7 @@ impl InvoiceContents {
 
        fn verify<T: secp256k1::Signing>(
                &self, tlv_stream: TlvStream<'_>, metadata: &Metadata, key: &ExpandedKey,
-               secp_ctx: &Secp256k1<T>
+               iv_bytes: &[u8; IV_LEN], secp_ctx: &Secp256k1<T>
        ) -> Result<PaymentId, ()> {
                let offer_records = tlv_stream.clone().range(OFFER_TYPES);
                let invreq_records = tlv_stream.range(INVOICE_REQUEST_TYPES).filter(|record| {
@@ -1041,11 +1049,6 @@ impl InvoiceContents {
                let tlv_stream = offer_records.chain(invreq_records);
 
                let payer_id = self.payer_id();
-               let iv_bytes = match self {
-                       InvoiceContents::ForOffer { .. } => INVOICE_REQUEST_IV_BYTES,
-                       InvoiceContents::ForRefund { .. } => REFUND_IV_BYTES,
-               };
-
                signer::verify_payer_metadata(
                        metadata.as_ref(), key, iv_bytes, payer_id, tlv_stream, secp_ctx,
                )
index deacacd15222b081f043b3decf73babb5757c65a..24f0346885c1b33994a81e35576cb30ee898ef2f 100644 (file)
@@ -112,7 +112,8 @@ use crate::prelude::*;
 #[cfg(feature = "std")]
 use std::time::SystemTime;
 
-pub(super) const IV_BYTES: &[u8; IV_LEN] = b"LDK Offer ~~~~~~";
+pub(super) const IV_BYTES_WITH_METADATA: &[u8; IV_LEN] = b"LDK Offer ~~~~~~";
+pub(super) const IV_BYTES_WITHOUT_METADATA: &[u8; IV_LEN] = b"LDK Offer v2~~~~";
 
 /// An identifier for an [`Offer`] built using [`DerivedMetadata`].
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -391,9 +392,12 @@ macro_rules! offer_builder_methods { (
 
                                // Don't derive keys if no blinded paths were given since this means the signing
                                // pubkey must be the node id of an announced node.
-                               if $self.offer.paths.is_none() {
+                               let iv_bytes = if $self.offer.paths.is_none() {
                                        metadata = metadata.without_keys();
-                               }
+                                       IV_BYTES_WITH_METADATA
+                               } else {
+                                       IV_BYTES_WITHOUT_METADATA
+                               };
 
                                let mut tlv_stream = $self.offer.as_tlv_stream();
                                debug_assert_eq!(tlv_stream.metadata, None);
@@ -406,7 +410,7 @@ macro_rules! offer_builder_methods { (
                                // for verification. In the former case, the blinded paths must include
                                // `OffersContext::InvoiceRequest` instead.
                                let (derived_metadata, keys) =
-                                       metadata.derive_from(IV_BYTES, tlv_stream, $self.secp_ctx);
+                                       metadata.derive_from(iv_bytes, tlv_stream, $self.secp_ctx);
                                match keys {
                                        Some(keys) => $self.offer.signing_pubkey = Some(keys.public_key()),
                                        None => $self.offer.metadata = Some(derived_metadata),
@@ -919,18 +923,20 @@ impl OfferContents {
        pub(super) fn verify_using_metadata<T: secp256k1::Signing>(
                &self, bytes: &[u8], key: &ExpandedKey, secp_ctx: &Secp256k1<T>
        ) -> Result<(OfferId, Option<Keypair>), ()> {
-               self.verify(bytes, self.metadata.as_ref(), key, secp_ctx)
+               self.verify(bytes, self.metadata.as_ref(), key, IV_BYTES_WITH_METADATA, secp_ctx)
        }
 
        pub(super) fn verify_using_recipient_data<T: secp256k1::Signing>(
                &self, bytes: &[u8], nonce: Nonce, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
        ) -> Result<(OfferId, Option<Keypair>), ()> {
-               self.verify(bytes, Some(&Metadata::RecipientData(nonce)), key, secp_ctx)
+               let metadata = Metadata::RecipientData(nonce);
+               self.verify(bytes, Some(&metadata), key, IV_BYTES_WITHOUT_METADATA, secp_ctx)
        }
 
        /// Verifies that the offer metadata was produced from the offer in the TLV stream.
        fn verify<T: secp256k1::Signing>(
-               &self, bytes: &[u8], metadata: Option<&Metadata>, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
+               &self, bytes: &[u8], metadata: Option<&Metadata>, key: &ExpandedKey,
+               iv_bytes: &[u8; IV_LEN], secp_ctx: &Secp256k1<T>
        ) -> Result<(OfferId, Option<Keypair>), ()> {
                match metadata {
                        Some(metadata) => {
@@ -946,7 +952,7 @@ impl OfferContents {
                                        None => return Err(()),
                                };
                                let keys = signer::verify_recipient_metadata(
-                                       metadata.as_ref(), key, IV_BYTES, signing_pubkey, tlv_stream, secp_ctx
+                                       metadata.as_ref(), key, iv_bytes, signing_pubkey, tlv_stream, secp_ctx
                                )?;
 
                                let offer_id = OfferId::from_valid_invreq_tlv_stream(bytes);
index 76605c77fbbf9062e57aff8f9936bfb753cec09e..242652577bd88e78cc620d4427e6e2e68c8adcb4 100644 (file)
@@ -122,7 +122,8 @@ use crate::prelude::*;
 #[cfg(feature = "std")]
 use std::time::SystemTime;
 
-pub(super) const IV_BYTES: &[u8; IV_LEN] = b"LDK Refund ~~~~~";
+pub(super) const IV_BYTES_WITH_METADATA: &[u8; IV_LEN] = b"LDK Refund ~~~~~";
+pub(super) const IV_BYTES_WITHOUT_METADATA: &[u8; IV_LEN] = b"LDK Refund v2~~~";
 
 /// Builds a [`Refund`] for the "offer for money" flow.
 ///
@@ -306,9 +307,12 @@ macro_rules! refund_builder_methods { (
                if $self.refund.payer.0.has_derivation_material() {
                        let mut metadata = core::mem::take(&mut $self.refund.payer.0);
 
-                       if $self.refund.paths.is_none() {
+                       let iv_bytes = if $self.refund.paths.is_none() {
                                metadata = metadata.without_keys();
-                       }
+                               IV_BYTES_WITH_METADATA
+                       } else {
+                               IV_BYTES_WITHOUT_METADATA
+                       };
 
                        let mut tlv_stream = $self.refund.as_tlv_stream();
                        tlv_stream.0.metadata = None;
@@ -317,7 +321,7 @@ macro_rules! refund_builder_methods { (
                        }
 
                        let (derived_metadata, keys) =
-                               metadata.derive_from(IV_BYTES, tlv_stream, $self.secp_ctx);
+                               metadata.derive_from(iv_bytes, tlv_stream, $self.secp_ctx);
                        metadata = derived_metadata;
                        if let Some(keys) = keys {
                                $self.refund.payer_id = keys.public_key();