@@ -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 ;
@@ -532,6 +532,22 @@ impl InvoiceRequestContents {
532
532
self . inner . chain ( )
533
533
}
534
534
535
+ /// Verifies that the payer metadata was produced from the invoice request in the TLV stream.
536
+ pub ( super ) fn verify < T : secp256k1:: Signing > (
537
+ & self , tlv_stream : TlvStream < ' _ > , key : & ExpandedKey , secp_ctx : & Secp256k1 < T >
538
+ ) -> bool {
539
+ let offer_records = tlv_stream. clone ( ) . range ( OFFER_TYPES ) ;
540
+ let invreq_records = tlv_stream. range ( INVOICE_REQUEST_TYPES ) . filter ( |record| {
541
+ match record. r#type {
542
+ PAYER_METADATA_TYPE => false , // Should be outside range
543
+ INVOICE_REQUEST_PAYER_ID_TYPE => !self . inner . payer . 0 . derives_keys ( ) ,
544
+ _ => true ,
545
+ }
546
+ } ) ;
547
+ let tlv_stream = offer_records. chain ( invreq_records) ;
548
+ signer:: verify_metadata ( self . metadata ( ) , key, IV_BYTES , self . payer_id , tlv_stream, secp_ctx)
549
+ }
550
+
535
551
pub ( super ) fn as_tlv_stream ( & self ) -> PartialInvoiceRequestTlvStreamRef {
536
552
let ( payer, offer, mut invoice_request) = self . inner . as_tlv_stream ( ) ;
537
553
invoice_request. payer_id = Some ( & self . payer_id ) ;
@@ -585,12 +601,20 @@ impl Writeable for InvoiceRequestContents {
585
601
}
586
602
}
587
603
588
- tlv_stream ! ( InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef , 80 ..160 , {
604
+ /// Valid type range for invoice_request TLV records.
605
+ const INVOICE_REQUEST_TYPES : core:: ops:: Range < u64 > = 80 ..160 ;
606
+
607
+ /// TLV record type for [`InvoiceRequest::payer_id`] and [`Refund::payer_id`].
608
+ ///
609
+ /// [`Refund::payer_id`]: crate::offers::refund::Refund::payer_id
610
+ const INVOICE_REQUEST_PAYER_ID_TYPE : u64 = 88 ;
611
+
612
+ tlv_stream ! ( InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef , INVOICE_REQUEST_TYPES , {
589
613
( 80 , chain: ChainHash ) ,
590
614
( 82 , amount: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
591
615
( 84 , features: ( InvoiceRequestFeatures , WithoutLength ) ) ,
592
616
( 86 , quantity: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
593
- ( 88 , payer_id: PublicKey ) ,
617
+ ( INVOICE_REQUEST_PAYER_ID_TYPE , payer_id: PublicKey ) ,
594
618
( 89 , payer_note: ( String , WithoutLength ) ) ,
595
619
} ) ;
596
620
@@ -702,8 +726,11 @@ mod tests {
702
726
use core:: num:: NonZeroU64 ;
703
727
#[ cfg( feature = "std" ) ]
704
728
use core:: time:: Duration ;
729
+ use crate :: chain:: keysinterface:: KeyMaterial ;
705
730
use crate :: ln:: features:: InvoiceRequestFeatures ;
731
+ use crate :: ln:: inbound_payment:: ExpandedKey ;
706
732
use crate :: ln:: msgs:: { DecodeError , MAX_VALUE_MSAT } ;
733
+ use crate :: offers:: invoice:: { Invoice , SIGNATURE_TAG as INVOICE_SIGNATURE_TAG } ;
707
734
use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , self } ;
708
735
use crate :: offers:: offer:: { Amount , OfferBuilder , OfferTlvStreamRef , Quantity } ;
709
736
use crate :: offers:: parse:: { ParseError , SemanticError } ;
@@ -800,6 +827,148 @@ mod tests {
800
827
}
801
828
}
802
829
830
+ #[ test]
831
+ fn builds_invoice_request_with_derived_metadata ( ) {
832
+ let payer_id = payer_pubkey ( ) ;
833
+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
834
+ let entropy = FixedEntropy { } ;
835
+ let secp_ctx = Secp256k1 :: new ( ) ;
836
+
837
+ let offer = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
838
+ . amount_msats ( 1000 )
839
+ . build ( ) . unwrap ( ) ;
840
+ let invoice_request = offer
841
+ . request_invoice_deriving_metadata ( payer_id, & expanded_key, & entropy)
842
+ . unwrap ( )
843
+ . build ( ) . unwrap ( )
844
+ . sign ( payer_sign) . unwrap ( ) ;
845
+ assert_eq ! ( invoice_request. payer_id( ) , payer_pubkey( ) ) ;
846
+
847
+ let invoice = invoice_request. respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) )
848
+ . unwrap ( )
849
+ . build ( ) . unwrap ( )
850
+ . sign ( recipient_sign) . unwrap ( ) ;
851
+ assert ! ( invoice. verify( & expanded_key, & secp_ctx) ) ;
852
+
853
+ // Fails verification with altered fields
854
+ let (
855
+ payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream,
856
+ mut invoice_tlv_stream, mut signature_tlv_stream
857
+ ) = invoice. as_tlv_stream ( ) ;
858
+ invoice_request_tlv_stream. amount = Some ( 2000 ) ;
859
+ invoice_tlv_stream. amount = Some ( 2000 ) ;
860
+
861
+ let tlv_stream =
862
+ ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream) ;
863
+ let mut bytes = Vec :: new ( ) ;
864
+ tlv_stream. write ( & mut bytes) . unwrap ( ) ;
865
+
866
+ let signature = merkle:: sign_message (
867
+ recipient_sign, INVOICE_SIGNATURE_TAG , & bytes, recipient_pubkey ( )
868
+ ) . unwrap ( ) ;
869
+ signature_tlv_stream. signature = Some ( & signature) ;
870
+
871
+ let mut encoded_invoice = bytes;
872
+ signature_tlv_stream. write ( & mut encoded_invoice) . unwrap ( ) ;
873
+
874
+ let invoice = Invoice :: try_from ( encoded_invoice) . unwrap ( ) ;
875
+ assert ! ( !invoice. verify( & expanded_key, & secp_ctx) ) ;
876
+
877
+ // Fails verification with altered metadata
878
+ let (
879
+ mut payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
880
+ mut signature_tlv_stream
881
+ ) = invoice. as_tlv_stream ( ) ;
882
+ let metadata = payer_tlv_stream. metadata . unwrap ( ) . iter ( ) . copied ( ) . rev ( ) . collect ( ) ;
883
+ payer_tlv_stream. metadata = Some ( & metadata) ;
884
+
885
+ let tlv_stream =
886
+ ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream) ;
887
+ let mut bytes = Vec :: new ( ) ;
888
+ tlv_stream. write ( & mut bytes) . unwrap ( ) ;
889
+
890
+ let signature = merkle:: sign_message (
891
+ recipient_sign, INVOICE_SIGNATURE_TAG , & bytes, recipient_pubkey ( )
892
+ ) . unwrap ( ) ;
893
+ signature_tlv_stream. signature = Some ( & signature) ;
894
+
895
+ let mut encoded_invoice = bytes;
896
+ signature_tlv_stream. write ( & mut encoded_invoice) . unwrap ( ) ;
897
+
898
+ let invoice = Invoice :: try_from ( encoded_invoice) . unwrap ( ) ;
899
+ assert ! ( !invoice. verify( & expanded_key, & secp_ctx) ) ;
900
+ }
901
+
902
+ #[ test]
903
+ fn builds_invoice_request_with_derived_payer_id ( ) {
904
+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
905
+ let entropy = FixedEntropy { } ;
906
+ let secp_ctx = Secp256k1 :: new ( ) ;
907
+
908
+ let offer = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
909
+ . amount_msats ( 1000 )
910
+ . build ( ) . unwrap ( ) ;
911
+ let invoice_request = offer
912
+ . request_invoice_deriving_payer_id ( & expanded_key, & entropy, & secp_ctx)
913
+ . unwrap ( )
914
+ . build_and_sign ( )
915
+ . unwrap ( ) ;
916
+
917
+ let invoice = invoice_request. respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) )
918
+ . unwrap ( )
919
+ . build ( ) . unwrap ( )
920
+ . sign ( recipient_sign) . unwrap ( ) ;
921
+ assert ! ( invoice. verify( & expanded_key, & secp_ctx) ) ;
922
+
923
+ // Fails verification with altered fields
924
+ let (
925
+ payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream,
926
+ mut invoice_tlv_stream, mut signature_tlv_stream
927
+ ) = invoice. as_tlv_stream ( ) ;
928
+ invoice_request_tlv_stream. amount = Some ( 2000 ) ;
929
+ invoice_tlv_stream. amount = Some ( 2000 ) ;
930
+
931
+ let tlv_stream =
932
+ ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream) ;
933
+ let mut bytes = Vec :: new ( ) ;
934
+ tlv_stream. write ( & mut bytes) . unwrap ( ) ;
935
+
936
+ let signature = merkle:: sign_message (
937
+ recipient_sign, INVOICE_SIGNATURE_TAG , & bytes, recipient_pubkey ( )
938
+ ) . unwrap ( ) ;
939
+ signature_tlv_stream. signature = Some ( & signature) ;
940
+
941
+ let mut encoded_invoice = bytes;
942
+ signature_tlv_stream. write ( & mut encoded_invoice) . unwrap ( ) ;
943
+
944
+ let invoice = Invoice :: try_from ( encoded_invoice) . unwrap ( ) ;
945
+ assert ! ( !invoice. verify( & expanded_key, & secp_ctx) ) ;
946
+
947
+ // Fails verification with altered payer id
948
+ let (
949
+ payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream, invoice_tlv_stream,
950
+ mut signature_tlv_stream
951
+ ) = invoice. as_tlv_stream ( ) ;
952
+ let payer_id = pubkey ( 1 ) ;
953
+ invoice_request_tlv_stream. payer_id = Some ( & payer_id) ;
954
+
955
+ let tlv_stream =
956
+ ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream) ;
957
+ let mut bytes = Vec :: new ( ) ;
958
+ tlv_stream. write ( & mut bytes) . unwrap ( ) ;
959
+
960
+ let signature = merkle:: sign_message (
961
+ recipient_sign, INVOICE_SIGNATURE_TAG , & bytes, recipient_pubkey ( )
962
+ ) . unwrap ( ) ;
963
+ signature_tlv_stream. signature = Some ( & signature) ;
964
+
965
+ let mut encoded_invoice = bytes;
966
+ signature_tlv_stream. write ( & mut encoded_invoice) . unwrap ( ) ;
967
+
968
+ let invoice = Invoice :: try_from ( encoded_invoice) . unwrap ( ) ;
969
+ assert ! ( !invoice. verify( & expanded_key, & secp_ctx) ) ;
970
+ }
971
+
803
972
#[ test]
804
973
fn builds_invoice_request_with_chain ( ) {
805
974
let mainnet = ChainHash :: using_genesis_block ( Network :: Bitcoin ) ;
0 commit comments