@@ -66,10 +66,10 @@ use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
66
66
use crate :: ln:: msgs:: DecodeError ;
67
67
use crate :: offers:: invoice:: { BlindedPayInfo , InvoiceBuilder } ;
68
68
use 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 } ;
70
70
use 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 } ;
73
73
use crate :: onion_message:: BlindedPath ;
74
74
use crate :: util:: ser:: { HighZeroBytesDroppedBigSize , SeekReadable , WithoutLength , Writeable , Writer } ;
75
75
use crate :: util:: string:: PrintableString ;
@@ -529,6 +529,22 @@ impl InvoiceRequestContents {
529
529
self . inner . chain ( )
530
530
}
531
531
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
+
532
548
pub ( super ) fn as_tlv_stream ( & self ) -> PartialInvoiceRequestTlvStreamRef {
533
549
let ( payer, offer, mut invoice_request) = self . inner . as_tlv_stream ( ) ;
534
550
invoice_request. payer_id = Some ( & self . payer_id ) ;
@@ -582,12 +598,20 @@ impl Writeable for InvoiceRequestContents {
582
598
}
583
599
}
584
600
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 , {
586
610
( 80 , chain: ChainHash ) ,
587
611
( 82 , amount: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
588
612
( 84 , features: ( InvoiceRequestFeatures , WithoutLength ) ) ,
589
613
( 86 , quantity: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
590
- ( 88 , payer_id: PublicKey ) ,
614
+ ( INVOICE_REQUEST_PAYER_ID_TYPE , payer_id: PublicKey ) ,
591
615
( 89 , payer_note: ( String , WithoutLength ) ) ,
592
616
} ) ;
593
617
@@ -699,8 +723,11 @@ mod tests {
699
723
use core:: num:: NonZeroU64 ;
700
724
#[ cfg( feature = "std" ) ]
701
725
use core:: time:: Duration ;
726
+ use crate :: chain:: keysinterface:: KeyMaterial ;
702
727
use crate :: ln:: features:: InvoiceRequestFeatures ;
728
+ use crate :: ln:: inbound_payment:: ExpandedKey ;
703
729
use crate :: ln:: msgs:: { DecodeError , MAX_VALUE_MSAT } ;
730
+ use crate :: offers:: invoice:: { Invoice , SIGNATURE_TAG as INVOICE_SIGNATURE_TAG } ;
704
731
use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , self } ;
705
732
use crate :: offers:: offer:: { Amount , OfferBuilder , OfferTlvStreamRef , Quantity } ;
706
733
use crate :: offers:: parse:: { ParseError , SemanticError } ;
@@ -797,6 +824,148 @@ mod tests {
797
824
}
798
825
}
799
826
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
+
800
969
#[ test]
801
970
fn builds_invoice_request_with_chain ( ) {
802
971
let mainnet = ChainHash :: using_genesis_block ( Network :: Bitcoin ) ;
0 commit comments