@@ -83,11 +83,12 @@ use crate::ln::features::InvoiceRequestFeatures;
83
83
use crate :: ln:: inbound_payment:: { ExpandedKey , Nonce } ;
84
84
use crate :: ln:: msgs:: { DecodeError , MAX_VALUE_MSAT } ;
85
85
use crate :: offers:: invoice:: { BlindedPayInfo , InvoiceBuilder } ;
86
- use crate :: offers:: invoice_request:: { InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef } ;
87
- use crate :: offers:: offer:: { OfferTlvStream , OfferTlvStreamRef } ;
86
+ use crate :: offers:: invoice_request:: { INVOICE_REQUEST_PAYER_ID_TYPE , INVOICE_REQUEST_TYPES , InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef } ;
87
+ use crate :: offers:: merkle:: TlvStream ;
88
+ use crate :: offers:: offer:: { OFFER_TYPES , OfferTlvStream , OfferTlvStreamRef } ;
88
89
use crate :: offers:: parse:: { Bech32Encode , ParseError , ParsedMessage , SemanticError } ;
89
- use crate :: offers:: payer:: { PayerContents , PayerTlvStream , PayerTlvStreamRef } ;
90
- use crate :: offers:: signer:: { MetadataMaterial , DerivedPubkey } ;
90
+ use crate :: offers:: payer:: { PAYER_METADATA_TYPE , PayerContents , PayerTlvStream , PayerTlvStreamRef } ;
91
+ use crate :: offers:: signer:: { MetadataMaterial , DerivedPubkey , self } ;
91
92
use crate :: onion_message:: BlindedPath ;
92
93
use crate :: util:: ser:: { SeekReadable , WithoutLength , Writeable , Writer } ;
93
94
use crate :: util:: string:: PrintableString ;
@@ -461,6 +462,20 @@ impl RefundContents {
461
462
ChainHash :: using_genesis_block ( Network :: Bitcoin )
462
463
}
463
464
465
+ /// Verifies that the payer metadata was produced from the refund in the TLV stream.
466
+ pub ( super ) fn verify ( & self , tlv_stream : TlvStream < ' _ > , key : & ExpandedKey ) -> bool {
467
+ let offer_records = tlv_stream. clone ( ) . range ( OFFER_TYPES ) ;
468
+ let invreq_records = tlv_stream. range ( INVOICE_REQUEST_TYPES ) . filter ( |record| {
469
+ match record. r#type {
470
+ PAYER_METADATA_TYPE => false , // Should be outside range
471
+ INVOICE_REQUEST_PAYER_ID_TYPE => false ,
472
+ _ => true ,
473
+ }
474
+ } ) ;
475
+ let tlv_stream = offer_records. chain ( invreq_records) ;
476
+ signer:: verify_metadata ( & self . payer . 0 , key, tlv_stream)
477
+ }
478
+
464
479
pub ( super ) fn as_tlv_stream ( & self ) -> RefundTlvStreamRef {
465
480
let payer = PayerTlvStreamRef {
466
481
metadata : Some ( & self . payer . 0 ) ,
@@ -638,12 +653,15 @@ mod tests {
638
653
use bitcoin:: secp256k1:: { KeyPair , Secp256k1 , SecretKey } ;
639
654
use core:: convert:: TryFrom ;
640
655
use core:: time:: Duration ;
656
+ use crate :: chain:: keysinterface:: KeyMaterial ;
641
657
use crate :: ln:: features:: { InvoiceRequestFeatures , OfferFeatures } ;
658
+ use crate :: ln:: inbound_payment:: { ExpandedKey , Nonce } ;
642
659
use crate :: ln:: msgs:: { DecodeError , MAX_VALUE_MSAT } ;
643
660
use crate :: offers:: invoice_request:: InvoiceRequestTlvStreamRef ;
644
661
use crate :: offers:: offer:: OfferTlvStreamRef ;
645
662
use crate :: offers:: parse:: { ParseError , SemanticError } ;
646
663
use crate :: offers:: payer:: PayerTlvStreamRef ;
664
+ use crate :: offers:: signer:: DerivedPubkey ;
647
665
use crate :: offers:: test_utils:: * ;
648
666
use crate :: onion_message:: { BlindedHop , BlindedPath } ;
649
667
use crate :: util:: ser:: { BigSize , Writeable } ;
@@ -724,6 +742,87 @@ mod tests {
724
742
}
725
743
}
726
744
745
+ #[ test]
746
+ fn builds_refund_with_metadata_derived ( ) {
747
+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
748
+ let nonce = Nonce ( [ 42 ; Nonce :: LENGTH ] ) ;
749
+
750
+ let refund = RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
751
+ . metadata_derived ( & expanded_key, nonce) . unwrap ( )
752
+ . build ( ) . unwrap ( ) ;
753
+ assert_eq ! ( refund. metadata( ) [ ..Nonce :: LENGTH ] , nonce. 0 ) ;
754
+ assert_eq ! ( refund. payer_id( ) , payer_pubkey( ) ) ;
755
+
756
+ let invoice = refund
757
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , recipient_pubkey ( ) , now ( ) )
758
+ . unwrap ( )
759
+ . build ( ) . unwrap ( )
760
+ . sign ( recipient_sign) . unwrap ( ) ;
761
+ assert ! ( invoice. verify( & expanded_key) ) ;
762
+
763
+ let mut tlv_stream = refund. as_tlv_stream ( ) ;
764
+ tlv_stream. 2 . amount = Some ( 2000 ) ;
765
+
766
+ let mut encoded_refund = Vec :: new ( ) ;
767
+ tlv_stream. write ( & mut encoded_refund) . unwrap ( ) ;
768
+
769
+ let invoice = Refund :: try_from ( encoded_refund) . unwrap ( )
770
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , recipient_pubkey ( ) , now ( ) )
771
+ . unwrap ( )
772
+ . build ( ) . unwrap ( )
773
+ . sign ( recipient_sign) . unwrap ( ) ;
774
+ assert ! ( !invoice. verify( & expanded_key) ) ;
775
+
776
+ match RefundBuilder :: new ( "foo" . into ( ) , vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( )
777
+ . metadata_derived ( & expanded_key, nonce) . unwrap ( )
778
+ . metadata_derived ( & expanded_key, nonce)
779
+ {
780
+ Ok ( _) => panic ! ( "expected error" ) ,
781
+ Err ( e) => assert_eq ! ( e, SemanticError :: UnexpectedMetadata ) ,
782
+ }
783
+ }
784
+
785
+ #[ test]
786
+ fn builds_refund_with_derived_payer_id ( ) {
787
+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
788
+ let nonce = Nonce ( [ 42 ; Nonce :: LENGTH ] ) ;
789
+ let keys = expanded_key. signing_keypair_for_offer ( nonce) ;
790
+
791
+ let payer_pubkey = DerivedPubkey :: new ( & expanded_key, nonce) ;
792
+ let refund = RefundBuilder :: deriving_payer_id ( "foo" . into ( ) , payer_pubkey, 1000 ) . unwrap ( )
793
+ . build ( ) . unwrap ( ) ;
794
+ assert_eq ! ( refund. metadata( ) [ ..Nonce :: LENGTH ] , nonce. 0 ) ;
795
+ assert_eq ! ( refund. payer_id( ) , keys. public_key( ) ) ;
796
+
797
+ let invoice = refund
798
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , recipient_pubkey ( ) , now ( ) )
799
+ . unwrap ( )
800
+ . build ( ) . unwrap ( )
801
+ . sign ( recipient_sign) . unwrap ( ) ;
802
+ assert ! ( invoice. verify( & expanded_key) ) ;
803
+
804
+ let mut tlv_stream = refund. as_tlv_stream ( ) ;
805
+ tlv_stream. 2 . amount = Some ( 2000 ) ;
806
+
807
+ let mut encoded_refund = Vec :: new ( ) ;
808
+ tlv_stream. write ( & mut encoded_refund) . unwrap ( ) ;
809
+
810
+ let invoice = Refund :: try_from ( encoded_refund) . unwrap ( )
811
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , recipient_pubkey ( ) , now ( ) )
812
+ . unwrap ( )
813
+ . build ( ) . unwrap ( )
814
+ . sign ( recipient_sign) . unwrap ( ) ;
815
+ assert ! ( !invoice. verify( & expanded_key) ) ;
816
+
817
+ let payer_pubkey = DerivedPubkey :: new ( & expanded_key, nonce) ;
818
+ match RefundBuilder :: deriving_payer_id ( "foo" . into ( ) , payer_pubkey, 1000 ) . unwrap ( )
819
+ . metadata_derived ( & expanded_key, nonce)
820
+ {
821
+ Ok ( _) => panic ! ( "expected error" ) ,
822
+ Err ( e) => assert_eq ! ( e, SemanticError :: UnexpectedMetadata ) ,
823
+ }
824
+ }
825
+
727
826
#[ test]
728
827
fn builds_refund_with_absolute_expiry ( ) {
729
828
let future_expiry = Duration :: from_secs ( u64:: max_value ( ) ) ;
0 commit comments