@@ -64,10 +64,10 @@ use crate::ln::inbound_payment::{ExpandedKey, Nonce};
6464use crate :: ln:: msgs:: DecodeError ;
6565use crate :: offers:: invoice:: { BlindedPayInfo , InvoiceBuilder } ;
6666use crate :: offers:: merkle:: { SignError , SignatureTlvStream , SignatureTlvStreamRef , TlvStream , self } ;
67- use crate :: offers:: offer:: { Offer , OfferContents , OfferTlvStream , OfferTlvStreamRef } ;
67+ use crate :: offers:: offer:: { OFFER_TYPES , Offer , OfferContents , OfferTlvStream , OfferTlvStreamRef } ;
6868use crate :: offers:: parse:: { ParseError , ParsedMessage , SemanticError } ;
69- use crate :: offers:: payer:: { PayerContents , PayerTlvStream , PayerTlvStreamRef } ;
70- use crate :: offers:: signer:: { MetadataMaterial , DerivedPubkey } ;
69+ use crate :: offers:: payer:: { PAYER_METADATA_TYPE , PayerContents , PayerTlvStream , PayerTlvStreamRef } ;
70+ use crate :: offers:: signer:: { MetadataMaterial , DerivedPubkey , self } ;
7171use crate :: onion_message:: BlindedPath ;
7272use crate :: util:: ser:: { HighZeroBytesDroppedBigSize , SeekReadable , WithoutLength , Writeable , Writer } ;
7373use crate :: util:: string:: PrintableString ;
@@ -446,6 +446,20 @@ impl InvoiceRequestContents {
446446 self . chain . unwrap_or_else ( || self . offer . implied_chain ( ) )
447447 }
448448
449+ /// Verifies that the payer metadata was produced from the invoice request in the TLV stream.
450+ pub ( super ) fn verify ( & self , tlv_stream : TlvStream < ' _ > , key : & ExpandedKey ) -> bool {
451+ let offer_records = tlv_stream. clone ( ) . range ( OFFER_TYPES ) ;
452+ let invreq_records = tlv_stream. range ( INVOICE_REQUEST_TYPES ) . filter ( |record| {
453+ match record. r#type {
454+ PAYER_METADATA_TYPE => false , // Should be outside range
455+ INVOICE_REQUEST_PAYER_ID_TYPE => false ,
456+ _ => true ,
457+ }
458+ } ) ;
459+ let tlv_stream = offer_records. chain ( invreq_records) ;
460+ signer:: verify_metadata ( & self . payer . 0 , key, tlv_stream)
461+ }
462+
449463 pub ( super ) fn as_tlv_stream ( & self ) -> PartialInvoiceRequestTlvStreamRef {
450464 let payer = PayerTlvStreamRef {
451465 metadata : Some ( & self . payer . 0 ) ,
@@ -483,12 +497,20 @@ impl Writeable for InvoiceRequestContents {
483497 }
484498}
485499
486- tlv_stream ! ( InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef , 80 ..160 , {
500+ /// Valid type range for invoice_request TLV records.
501+ const INVOICE_REQUEST_TYPES : core:: ops:: Range < u64 > = 80 ..160 ;
502+
503+ /// TLV record type for [`InvoiceRequest::payer_id`] and [`Refund::payer_id`].
504+ ///
505+ /// [`Refund::payer_id`]: crate::offers::refund::Refund::payer_id
506+ const INVOICE_REQUEST_PAYER_ID_TYPE : u64 = 88 ;
507+
508+ tlv_stream ! ( InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef , INVOICE_REQUEST_TYPES , {
487509 ( 80 , chain: ChainHash ) ,
488510 ( 82 , amount: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
489511 ( 84 , features: ( InvoiceRequestFeatures , WithoutLength ) ) ,
490512 ( 86 , quantity: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
491- ( 88 , payer_id: PublicKey ) ,
513+ ( INVOICE_REQUEST_PAYER_ID_TYPE , payer_id: PublicKey ) ,
492514 ( 89 , payer_note: ( String , WithoutLength ) ) ,
493515} ) ;
494516
@@ -597,12 +619,16 @@ mod tests {
597619 use core:: num:: NonZeroU64 ;
598620 #[ cfg( feature = "std" ) ]
599621 use core:: time:: Duration ;
622+ use crate :: chain:: keysinterface:: KeyMaterial ;
600623 use crate :: ln:: features:: InvoiceRequestFeatures ;
624+ use crate :: ln:: inbound_payment:: { ExpandedKey , Nonce } ;
601625 use crate :: ln:: msgs:: { DecodeError , MAX_VALUE_MSAT } ;
626+ use crate :: offers:: invoice:: { Invoice , SIGNATURE_TAG as INVOICE_SIGNATURE_TAG } ;
602627 use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , self } ;
603628 use crate :: offers:: offer:: { Amount , OfferBuilder , OfferTlvStreamRef , Quantity } ;
604629 use crate :: offers:: parse:: { ParseError , SemanticError } ;
605630 use crate :: offers:: payer:: PayerTlvStreamRef ;
631+ use crate :: offers:: signer:: DerivedPubkey ;
606632 use crate :: offers:: test_utils:: * ;
607633 use crate :: util:: ser:: { BigSize , Writeable } ;
608634 use crate :: util:: string:: PrintableString ;
@@ -695,6 +721,120 @@ mod tests {
695721 }
696722 }
697723
724+ #[ test]
725+ fn builds_invoice_request_with_metadata_derived ( ) {
726+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
727+ let nonce = Nonce ( [ 42 ; Nonce :: LENGTH ] ) ;
728+
729+ let offer = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
730+ . amount_msats ( 1000 )
731+ . build ( ) . unwrap ( ) ;
732+ let invoice_request = offer. request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
733+ . metadata_derived ( & expanded_key, nonce) . unwrap ( )
734+ . build ( ) . unwrap ( )
735+ . sign ( payer_sign) . unwrap ( ) ;
736+ assert_eq ! ( invoice_request. metadata( ) [ ..Nonce :: LENGTH ] , nonce. 0 ) ;
737+ assert_eq ! ( invoice_request. payer_id( ) , payer_pubkey( ) ) ;
738+
739+ let invoice = invoice_request. respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) )
740+ . unwrap ( )
741+ . build ( ) . unwrap ( )
742+ . sign ( recipient_sign) . unwrap ( ) ;
743+ assert ! ( invoice. verify( & expanded_key) ) ;
744+
745+ let (
746+ payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream,
747+ mut invoice_tlv_stream, mut signature_tlv_stream
748+ ) = invoice. as_tlv_stream ( ) ;
749+ invoice_request_tlv_stream. amount = Some ( 2000 ) ;
750+ invoice_tlv_stream. amount = Some ( 2000 ) ;
751+
752+ let tlv_stream =
753+ ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream) ;
754+ let mut bytes = Vec :: new ( ) ;
755+ tlv_stream. write ( & mut bytes) . unwrap ( ) ;
756+
757+ let signature = merkle:: sign_message (
758+ recipient_sign, INVOICE_SIGNATURE_TAG , & bytes, recipient_pubkey ( )
759+ ) . unwrap ( ) ;
760+ signature_tlv_stream. signature = Some ( & signature) ;
761+
762+ let mut encoded_invoice = bytes;
763+ signature_tlv_stream. write ( & mut encoded_invoice) . unwrap ( ) ;
764+
765+ let invoice = Invoice :: try_from ( encoded_invoice) . unwrap ( ) ;
766+ assert ! ( !invoice. verify( & expanded_key) ) ;
767+
768+ match OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
769+ . build ( ) . unwrap ( )
770+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
771+ . metadata_derived ( & expanded_key, nonce) . unwrap ( )
772+ . metadata_derived ( & expanded_key, nonce)
773+ {
774+ Ok ( _) => panic ! ( "expected error" ) ,
775+ Err ( e) => assert_eq ! ( e, SemanticError :: UnexpectedMetadata ) ,
776+ }
777+ }
778+
779+ #[ test]
780+ fn builds_invoice_request_with_derived_payer_id ( ) {
781+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
782+ let nonce = Nonce ( [ 42 ; Nonce :: LENGTH ] ) ;
783+ let payer_pubkey = DerivedPubkey :: new ( & expanded_key, nonce) ;
784+
785+ let secp_ctx = Secp256k1 :: new ( ) ;
786+ let keys = expanded_key. signing_keypair_for_offer ( nonce) ;
787+
788+ let offer = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
789+ . amount_msats ( 1000 )
790+ . build ( ) . unwrap ( ) ;
791+ let invoice_request = offer. request_invoice_deriving_payer_id ( payer_pubkey) . unwrap ( )
792+ . build ( ) . unwrap ( )
793+ . sign :: < _ , Infallible > ( |digest| Ok ( secp_ctx. sign_schnorr_no_aux_rand ( digest, & keys) ) )
794+ . unwrap ( ) ;
795+ assert_eq ! ( invoice_request. metadata( ) [ ..Nonce :: LENGTH ] , nonce. 0 ) ;
796+ assert_eq ! ( invoice_request. payer_id( ) , keys. public_key( ) ) ;
797+
798+ let invoice = invoice_request. respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) )
799+ . unwrap ( )
800+ . build ( ) . unwrap ( )
801+ . sign ( recipient_sign) . unwrap ( ) ;
802+ assert ! ( invoice. verify( & expanded_key) ) ;
803+
804+ let (
805+ payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream,
806+ mut invoice_tlv_stream, mut signature_tlv_stream
807+ ) = invoice. as_tlv_stream ( ) ;
808+ invoice_request_tlv_stream. amount = Some ( 2000 ) ;
809+ invoice_tlv_stream. amount = Some ( 2000 ) ;
810+
811+ let tlv_stream =
812+ ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream) ;
813+ let mut bytes = Vec :: new ( ) ;
814+ tlv_stream. write ( & mut bytes) . unwrap ( ) ;
815+
816+ let signature = merkle:: sign_message (
817+ recipient_sign, INVOICE_SIGNATURE_TAG , & bytes, recipient_pubkey ( )
818+ ) . unwrap ( ) ;
819+ signature_tlv_stream. signature = Some ( & signature) ;
820+
821+ let mut encoded_invoice = bytes;
822+ signature_tlv_stream. write ( & mut encoded_invoice) . unwrap ( ) ;
823+
824+ let invoice = Invoice :: try_from ( encoded_invoice) . unwrap ( ) ;
825+ assert ! ( !invoice. verify( & expanded_key) ) ;
826+
827+ let payer_pubkey = DerivedPubkey :: new ( & expanded_key, nonce) ;
828+ match OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
829+ . build ( ) . unwrap ( )
830+ . request_invoice_deriving_payer_id ( payer_pubkey) . unwrap ( )
831+ . metadata_derived ( & expanded_key, nonce)
832+ {
833+ Ok ( _) => panic ! ( "expected error" ) ,
834+ Err ( e) => assert_eq ! ( e, SemanticError :: UnexpectedMetadata ) ,
835+ }
836+ }
837+
698838 #[ test]
699839 fn builds_invoice_request_with_chain ( ) {
700840 let mainnet = ChainHash :: using_genesis_block ( Network :: Bitcoin ) ;
0 commit comments