@@ -66,10 +66,10 @@ use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
6666use crate :: ln:: msgs:: DecodeError ;
6767use crate :: offers:: invoice:: { BlindedPayInfo , InvoiceBuilder } ;
6868use crate :: offers:: merkle:: { SignError , SignatureTlvStream , SignatureTlvStreamRef , TlvStream , self } ;
69- use crate :: offers:: offer:: { Offer , OfferContents , OfferTlvStream , OfferTlvStreamRef } ;
69+ use crate :: offers:: offer:: { OFFER_TYPES , Offer , OfferContents , OfferTlvStream , OfferTlvStreamRef } ;
7070use crate :: offers:: parse:: { ParseError , ParsedMessage , SemanticError } ;
71- use crate :: offers:: payer:: { PayerContents , PayerTlvStream , PayerTlvStreamRef } ;
72- use crate :: offers:: signer:: { Metadata , MetadataMaterial } ;
71+ use crate :: offers:: payer:: { PAYER_METADATA_TYPE , PayerContents , PayerTlvStream , PayerTlvStreamRef } ;
72+ use crate :: offers:: signer:: { Metadata , MetadataMaterial , self } ;
7373use crate :: onion_message:: BlindedPath ;
7474use crate :: util:: ser:: { HighZeroBytesDroppedBigSize , SeekReadable , WithoutLength , Writeable , Writer } ;
7575use crate :: util:: string:: PrintableString ;
@@ -529,6 +529,22 @@ impl InvoiceRequestContents {
529529 self . inner . chain ( )
530530 }
531531
532+ /// Verifies that the payer metadata was produced from the invoice request in the TLV stream.
533+ pub ( super ) fn verify < T : secp256k1:: Signing > (
534+ & self , tlv_stream : TlvStream < ' _ > , key : & ExpandedKey , secp_ctx : & Secp256k1 < T >
535+ ) -> bool {
536+ let offer_records = tlv_stream. clone ( ) . range ( OFFER_TYPES ) ;
537+ let invreq_records = tlv_stream. range ( INVOICE_REQUEST_TYPES ) . filter ( |record| {
538+ match record. r#type {
539+ PAYER_METADATA_TYPE => false , // Should be outside range
540+ INVOICE_REQUEST_PAYER_ID_TYPE => false ,
541+ _ => true ,
542+ }
543+ } ) ;
544+ let tlv_stream = offer_records. chain ( invreq_records) ;
545+ signer:: verify_metadata ( self . metadata ( ) , key, IV_BYTES , self . payer_id , tlv_stream, secp_ctx)
546+ }
547+
532548 pub ( super ) fn as_tlv_stream ( & self ) -> PartialInvoiceRequestTlvStreamRef {
533549 let ( payer, offer, mut invoice_request) = self . inner . as_tlv_stream ( ) ;
534550 invoice_request. payer_id = Some ( & self . payer_id ) ;
@@ -582,12 +598,20 @@ impl Writeable for InvoiceRequestContents {
582598 }
583599}
584600
585- tlv_stream ! ( InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef , 80 ..160 , {
601+ /// Valid type range for invoice_request TLV records.
602+ const INVOICE_REQUEST_TYPES : core:: ops:: Range < u64 > = 80 ..160 ;
603+
604+ /// TLV record type for [`InvoiceRequest::payer_id`] and [`Refund::payer_id`].
605+ ///
606+ /// [`Refund::payer_id`]: crate::offers::refund::Refund::payer_id
607+ const INVOICE_REQUEST_PAYER_ID_TYPE : u64 = 88 ;
608+
609+ tlv_stream ! ( InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef , INVOICE_REQUEST_TYPES , {
586610 ( 80 , chain: ChainHash ) ,
587611 ( 82 , amount: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
588612 ( 84 , features: ( InvoiceRequestFeatures , WithoutLength ) ) ,
589613 ( 86 , quantity: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
590- ( 88 , payer_id: PublicKey ) ,
614+ ( INVOICE_REQUEST_PAYER_ID_TYPE , payer_id: PublicKey ) ,
591615 ( 89 , payer_note: ( String , WithoutLength ) ) ,
592616} ) ;
593617
@@ -699,8 +723,11 @@ mod tests {
699723 use core:: num:: NonZeroU64 ;
700724 #[ cfg( feature = "std" ) ]
701725 use core:: time:: Duration ;
726+ use crate :: chain:: keysinterface:: KeyMaterial ;
702727 use crate :: ln:: features:: InvoiceRequestFeatures ;
728+ use crate :: ln:: inbound_payment:: ExpandedKey ;
703729 use crate :: ln:: msgs:: { DecodeError , MAX_VALUE_MSAT } ;
730+ use crate :: offers:: invoice:: { Invoice , SIGNATURE_TAG as INVOICE_SIGNATURE_TAG } ;
704731 use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , self } ;
705732 use crate :: offers:: offer:: { Amount , OfferBuilder , OfferTlvStreamRef , Quantity } ;
706733 use crate :: offers:: parse:: { ParseError , SemanticError } ;
@@ -797,6 +824,148 @@ mod tests {
797824 }
798825 }
799826
827+ #[ test]
828+ fn builds_invoice_request_with_derived_metadata ( ) {
829+ let payer_id = payer_pubkey ( ) ;
830+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
831+ let entropy = FixedEntropy { } ;
832+ let secp_ctx = Secp256k1 :: new ( ) ;
833+
834+ let offer = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
835+ . amount_msats ( 1000 )
836+ . build ( ) . unwrap ( ) ;
837+ let invoice_request = offer
838+ . request_invoice_deriving_metadata ( payer_id, & expanded_key, & entropy)
839+ . unwrap ( )
840+ . build ( ) . unwrap ( )
841+ . sign ( payer_sign) . unwrap ( ) ;
842+ assert_eq ! ( invoice_request. payer_id( ) , payer_pubkey( ) ) ;
843+
844+ let invoice = invoice_request. respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) )
845+ . unwrap ( )
846+ . build ( ) . unwrap ( )
847+ . sign ( recipient_sign) . unwrap ( ) ;
848+ assert ! ( invoice. verify( & expanded_key, & secp_ctx) ) ;
849+
850+ // Fails verification with altered fields
851+ let (
852+ payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream,
853+ mut invoice_tlv_stream, mut signature_tlv_stream
854+ ) = invoice. as_tlv_stream ( ) ;
855+ invoice_request_tlv_stream. amount = Some ( 2000 ) ;
856+ invoice_tlv_stream. amount = Some ( 2000 ) ;
857+
858+ let tlv_stream =
859+ ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream) ;
860+ let mut bytes = Vec :: new ( ) ;
861+ tlv_stream. write ( & mut bytes) . unwrap ( ) ;
862+
863+ let signature = merkle:: sign_message (
864+ recipient_sign, INVOICE_SIGNATURE_TAG , & bytes, recipient_pubkey ( )
865+ ) . unwrap ( ) ;
866+ signature_tlv_stream. signature = Some ( & signature) ;
867+
868+ let mut encoded_invoice = bytes;
869+ signature_tlv_stream. write ( & mut encoded_invoice) . unwrap ( ) ;
870+
871+ let invoice = Invoice :: try_from ( encoded_invoice) . unwrap ( ) ;
872+ assert ! ( !invoice. verify( & expanded_key, & secp_ctx) ) ;
873+
874+ // Fails verification with altered metadata
875+ let (
876+ mut payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
877+ mut signature_tlv_stream
878+ ) = invoice. as_tlv_stream ( ) ;
879+ let metadata = payer_tlv_stream. metadata . unwrap ( ) . iter ( ) . copied ( ) . rev ( ) . collect ( ) ;
880+ payer_tlv_stream. metadata = Some ( & metadata) ;
881+
882+ let tlv_stream =
883+ ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream) ;
884+ let mut bytes = Vec :: new ( ) ;
885+ tlv_stream. write ( & mut bytes) . unwrap ( ) ;
886+
887+ let signature = merkle:: sign_message (
888+ recipient_sign, INVOICE_SIGNATURE_TAG , & bytes, recipient_pubkey ( )
889+ ) . unwrap ( ) ;
890+ signature_tlv_stream. signature = Some ( & signature) ;
891+
892+ let mut encoded_invoice = bytes;
893+ signature_tlv_stream. write ( & mut encoded_invoice) . unwrap ( ) ;
894+
895+ let invoice = Invoice :: try_from ( encoded_invoice) . unwrap ( ) ;
896+ assert ! ( !invoice. verify( & expanded_key, & secp_ctx) ) ;
897+ }
898+
899+ #[ test]
900+ fn builds_invoice_request_with_derived_payer_id ( ) {
901+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
902+ let entropy = FixedEntropy { } ;
903+ let secp_ctx = Secp256k1 :: new ( ) ;
904+
905+ let offer = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
906+ . amount_msats ( 1000 )
907+ . build ( ) . unwrap ( ) ;
908+ let invoice_request = offer
909+ . request_invoice_deriving_payer_id ( & expanded_key, & entropy, & secp_ctx)
910+ . unwrap ( )
911+ . build_and_sign ( )
912+ . unwrap ( ) ;
913+
914+ let invoice = invoice_request. respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) )
915+ . unwrap ( )
916+ . build ( ) . unwrap ( )
917+ . sign ( recipient_sign) . unwrap ( ) ;
918+ assert ! ( invoice. verify( & expanded_key, & secp_ctx) ) ;
919+
920+ // Fails verification with altered fields
921+ let (
922+ payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream,
923+ mut invoice_tlv_stream, mut signature_tlv_stream
924+ ) = invoice. as_tlv_stream ( ) ;
925+ invoice_request_tlv_stream. amount = Some ( 2000 ) ;
926+ invoice_tlv_stream. amount = Some ( 2000 ) ;
927+
928+ let tlv_stream =
929+ ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream) ;
930+ let mut bytes = Vec :: new ( ) ;
931+ tlv_stream. write ( & mut bytes) . unwrap ( ) ;
932+
933+ let signature = merkle:: sign_message (
934+ recipient_sign, INVOICE_SIGNATURE_TAG , & bytes, recipient_pubkey ( )
935+ ) . unwrap ( ) ;
936+ signature_tlv_stream. signature = Some ( & signature) ;
937+
938+ let mut encoded_invoice = bytes;
939+ signature_tlv_stream. write ( & mut encoded_invoice) . unwrap ( ) ;
940+
941+ let invoice = Invoice :: try_from ( encoded_invoice) . unwrap ( ) ;
942+ assert ! ( !invoice. verify( & expanded_key, & secp_ctx) ) ;
943+
944+ // Fails verification with altered payer id
945+ let (
946+ payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream, invoice_tlv_stream,
947+ mut signature_tlv_stream
948+ ) = invoice. as_tlv_stream ( ) ;
949+ let payer_id = pubkey ( 1 ) ;
950+ invoice_request_tlv_stream. payer_id = Some ( & payer_id) ;
951+
952+ let tlv_stream =
953+ ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream) ;
954+ let mut bytes = Vec :: new ( ) ;
955+ tlv_stream. write ( & mut bytes) . unwrap ( ) ;
956+
957+ let signature = merkle:: sign_message (
958+ recipient_sign, INVOICE_SIGNATURE_TAG , & bytes, recipient_pubkey ( )
959+ ) . unwrap ( ) ;
960+ signature_tlv_stream. signature = Some ( & signature) ;
961+
962+ let mut encoded_invoice = bytes;
963+ signature_tlv_stream. write ( & mut encoded_invoice) . unwrap ( ) ;
964+
965+ let invoice = Invoice :: try_from ( encoded_invoice) . unwrap ( ) ;
966+ assert ! ( !invoice. verify( & expanded_key, & secp_ctx) ) ;
967+ }
968+
800969 #[ test]
801970 fn builds_invoice_request_with_chain ( ) {
802971 let mainnet = ChainHash :: using_genesis_block ( Network :: Bitcoin ) ;
0 commit comments