@@ -64,10 +64,10 @@ use crate::ln::inbound_payment::{ExpandedKey, Nonce};
64
64
use crate :: ln:: msgs:: DecodeError ;
65
65
use crate :: offers:: invoice:: { BlindedPayInfo , InvoiceBuilder } ;
66
66
use 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 } ;
68
68
use 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 } ;
71
71
use crate :: onion_message:: BlindedPath ;
72
72
use crate :: util:: ser:: { HighZeroBytesDroppedBigSize , SeekReadable , WithoutLength , Writeable , Writer } ;
73
73
use crate :: util:: string:: PrintableString ;
@@ -446,6 +446,20 @@ impl InvoiceRequestContents {
446
446
self . chain . unwrap_or_else ( || self . offer . implied_chain ( ) )
447
447
}
448
448
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
+
449
463
pub ( super ) fn as_tlv_stream ( & self ) -> PartialInvoiceRequestTlvStreamRef {
450
464
let payer = PayerTlvStreamRef {
451
465
metadata : Some ( & self . payer . 0 ) ,
@@ -483,12 +497,20 @@ impl Writeable for InvoiceRequestContents {
483
497
}
484
498
}
485
499
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 , {
487
509
( 80 , chain: ChainHash ) ,
488
510
( 82 , amount: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
489
511
( 84 , features: ( InvoiceRequestFeatures , WithoutLength ) ) ,
490
512
( 86 , quantity: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
491
- ( 88 , payer_id: PublicKey ) ,
513
+ ( INVOICE_REQUEST_PAYER_ID_TYPE , payer_id: PublicKey ) ,
492
514
( 89 , payer_note: ( String , WithoutLength ) ) ,
493
515
} ) ;
494
516
@@ -597,12 +619,16 @@ mod tests {
597
619
use core:: num:: NonZeroU64 ;
598
620
#[ cfg( feature = "std" ) ]
599
621
use core:: time:: Duration ;
622
+ use crate :: chain:: keysinterface:: KeyMaterial ;
600
623
use crate :: ln:: features:: InvoiceRequestFeatures ;
624
+ use crate :: ln:: inbound_payment:: { ExpandedKey , Nonce } ;
601
625
use crate :: ln:: msgs:: { DecodeError , MAX_VALUE_MSAT } ;
626
+ use crate :: offers:: invoice:: { Invoice , SIGNATURE_TAG as INVOICE_SIGNATURE_TAG } ;
602
627
use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , self } ;
603
628
use crate :: offers:: offer:: { Amount , OfferBuilder , OfferTlvStreamRef , Quantity } ;
604
629
use crate :: offers:: parse:: { ParseError , SemanticError } ;
605
630
use crate :: offers:: payer:: PayerTlvStreamRef ;
631
+ use crate :: offers:: signer:: DerivedPubkey ;
606
632
use crate :: offers:: test_utils:: * ;
607
633
use crate :: util:: ser:: { BigSize , Writeable } ;
608
634
use crate :: util:: string:: PrintableString ;
@@ -695,6 +721,120 @@ mod tests {
695
721
}
696
722
}
697
723
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
+
698
838
#[ test]
699
839
fn builds_invoice_request_with_chain ( ) {
700
840
let mainnet = ChainHash :: using_genesis_block ( Network :: Bitcoin ) ;
0 commit comments