@@ -85,11 +85,12 @@ use crate::ln::features::InvoiceRequestFeatures;
8585use  crate :: ln:: inbound_payment:: { ExpandedKey ,  IV_LEN ,  Nonce } ; 
8686use  crate :: ln:: msgs:: { DecodeError ,  MAX_VALUE_MSAT } ; 
8787use  crate :: offers:: invoice:: { BlindedPayInfo ,  InvoiceBuilder } ; 
88- use  crate :: offers:: invoice_request:: { InvoiceRequestTlvStream ,  InvoiceRequestTlvStreamRef } ; 
89- use  crate :: offers:: offer:: { OfferTlvStream ,  OfferTlvStreamRef } ; 
88+ use  crate :: offers:: invoice_request:: { INVOICE_REQUEST_PAYER_ID_TYPE ,  INVOICE_REQUEST_TYPES ,  InvoiceRequestTlvStream ,  InvoiceRequestTlvStreamRef } ; 
89+ use  crate :: offers:: merkle:: TlvStream ; 
90+ use  crate :: offers:: offer:: { OFFER_TYPES ,  OfferTlvStream ,  OfferTlvStreamRef } ; 
9091use  crate :: offers:: parse:: { Bech32Encode ,  ParseError ,  ParsedMessage ,  SemanticError } ; 
91- use  crate :: offers:: payer:: { PayerContents ,  PayerTlvStream ,  PayerTlvStreamRef } ; 
92- use  crate :: offers:: signer:: { Metadata ,  MetadataMaterial } ; 
92+ use  crate :: offers:: payer:: { PAYER_METADATA_TYPE ,   PayerContents ,  PayerTlvStream ,  PayerTlvStreamRef } ; 
93+ use  crate :: offers:: signer:: { Metadata ,  MetadataMaterial ,   self } ; 
9394use  crate :: onion_message:: BlindedPath ; 
9495use  crate :: util:: ser:: { SeekReadable ,  WithoutLength ,  Writeable ,  Writer } ; 
9596use  crate :: util:: string:: PrintableString ; 
@@ -343,7 +344,7 @@ impl Refund {
343344 	/// 
344345 	/// [`payer_id`]: Self::payer_id 
345346 	pub  fn  metadata ( & self )  -> & [ u8 ]  { 
346- 		self . contents . payer . 0 . as_bytes ( ) . map ( |bytes| bytes . as_slice ( ) ) . unwrap_or ( & [ ] ) 
347+ 		self . contents . metadata ( ) 
347348	} 
348349
349350	/// A chain that the refund is valid for. 
@@ -455,6 +456,10 @@ impl RefundContents {
455456		} 
456457	} 
457458
459+ 	fn  metadata ( & self )  -> & [ u8 ]  { 
460+ 		self . payer . 0 . as_bytes ( ) . map ( |bytes| bytes. as_slice ( ) ) . unwrap_or ( & [ ] ) 
461+ 	} 
462+ 
458463	pub ( super )  fn  chain ( & self )  -> ChainHash  { 
459464		self . chain . unwrap_or_else ( || self . implied_chain ( ) ) 
460465	} 
@@ -463,6 +468,22 @@ impl RefundContents {
463468		ChainHash :: using_genesis_block ( Network :: Bitcoin ) 
464469	} 
465470
471+ 	/// Verifies that the payer metadata was produced from the refund in the TLV stream. 
472+  	pub ( super )  fn  verify < T :  secp256k1:: Signing > ( 
473+ 		& self ,  tlv_stream :  TlvStream < ' _ > ,  key :  & ExpandedKey ,  secp_ctx :  & Secp256k1 < T > 
474+ 	)  -> bool  { 
475+ 		let  offer_records = tlv_stream. clone ( ) . range ( OFFER_TYPES ) ; 
476+ 		let  invreq_records = tlv_stream. range ( INVOICE_REQUEST_TYPES ) . filter ( |record| { 
477+ 			match  record. r#type  { 
478+ 				PAYER_METADATA_TYPE  => false ,  // Should be outside range 
479+ 				INVOICE_REQUEST_PAYER_ID_TYPE  => !self . payer . 0 . derives_keys ( ) , 
480+ 				_ => true , 
481+ 			} 
482+ 		} ) ; 
483+ 		let  tlv_stream = offer_records. chain ( invreq_records) ; 
484+ 		signer:: verify_metadata ( self . metadata ( ) ,  key,  IV_BYTES ,  self . payer_id ,  tlv_stream,  secp_ctx) 
485+ 	} 
486+ 
466487	pub ( super )  fn  as_tlv_stream ( & self )  -> RefundTlvStreamRef  { 
467488		let  payer = PayerTlvStreamRef  { 
468489			metadata :  self . payer . 0 . as_bytes ( ) , 
@@ -640,7 +661,9 @@ mod tests {
640661	use  bitcoin:: secp256k1:: { KeyPair ,  Secp256k1 ,  SecretKey } ; 
641662	use  core:: convert:: TryFrom ; 
642663	use  core:: time:: Duration ; 
664+ 	use  crate :: chain:: keysinterface:: KeyMaterial ; 
643665	use  crate :: ln:: features:: { InvoiceRequestFeatures ,  OfferFeatures } ; 
666+ 	use  crate :: ln:: inbound_payment:: ExpandedKey ; 
644667	use  crate :: ln:: msgs:: { DecodeError ,  MAX_VALUE_MSAT } ; 
645668	use  crate :: offers:: invoice_request:: InvoiceRequestTlvStreamRef ; 
646669	use  crate :: offers:: offer:: OfferTlvStreamRef ; 
@@ -726,6 +749,118 @@ mod tests {
726749		} 
727750	} 
728751
752+ 	#[ test]  
753+ 	fn  builds_refund_with_metadata_derived ( )  { 
754+ 		let  desc = "foo" . to_string ( ) ; 
755+ 		let  node_id = payer_pubkey ( ) ; 
756+ 		let  expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ;  32 ] ) ) ; 
757+ 		let  entropy = FixedEntropy  { } ; 
758+ 		let  secp_ctx = Secp256k1 :: new ( ) ; 
759+ 
760+ 		let  refund = RefundBuilder 
761+ 			:: deriving_payer_id ( desc,  node_id,  & expanded_key,  & entropy,  & secp_ctx,  1000 ) 
762+ 			. unwrap ( ) 
763+ 			. build ( ) . unwrap ( ) ; 
764+ 		assert_eq ! ( refund. payer_id( ) ,  node_id) ; 
765+ 
766+ 		// Fails verification with altered fields 
767+ 		let  invoice = refund
768+ 			. respond_with_no_std ( payment_paths ( ) ,  payment_hash ( ) ,  recipient_pubkey ( ) ,  now ( ) ) 
769+ 			. unwrap ( ) 
770+ 			. build ( ) . unwrap ( ) 
771+ 			. sign ( recipient_sign) . unwrap ( ) ; 
772+ 		assert ! ( invoice. verify( & expanded_key,  & secp_ctx) ) ; 
773+ 
774+ 		let  mut  tlv_stream = refund. as_tlv_stream ( ) ; 
775+ 		tlv_stream. 2 . amount  = Some ( 2000 ) ; 
776+ 
777+ 		let  mut  encoded_refund = Vec :: new ( ) ; 
778+ 		tlv_stream. write ( & mut  encoded_refund) . unwrap ( ) ; 
779+ 
780+ 		let  invoice = Refund :: try_from ( encoded_refund) . unwrap ( ) 
781+ 			. respond_with_no_std ( payment_paths ( ) ,  payment_hash ( ) ,  recipient_pubkey ( ) ,  now ( ) ) 
782+ 			. unwrap ( ) 
783+ 			. build ( ) . unwrap ( ) 
784+ 			. sign ( recipient_sign) . unwrap ( ) ; 
785+ 		assert ! ( !invoice. verify( & expanded_key,  & secp_ctx) ) ; 
786+ 
787+ 		// Fails verification with altered metadata 
788+ 		let  mut  tlv_stream = refund. as_tlv_stream ( ) ; 
789+ 		let  metadata = tlv_stream. 0 . metadata . unwrap ( ) . iter ( ) . copied ( ) . rev ( ) . collect ( ) ; 
790+ 		tlv_stream. 0 . metadata  = Some ( & metadata) ; 
791+ 
792+ 		let  mut  encoded_refund = Vec :: new ( ) ; 
793+ 		tlv_stream. write ( & mut  encoded_refund) . unwrap ( ) ; 
794+ 
795+ 		let  invoice = Refund :: try_from ( encoded_refund) . unwrap ( ) 
796+ 			. respond_with_no_std ( payment_paths ( ) ,  payment_hash ( ) ,  recipient_pubkey ( ) ,  now ( ) ) 
797+ 			. unwrap ( ) 
798+ 			. build ( ) . unwrap ( ) 
799+ 			. sign ( recipient_sign) . unwrap ( ) ; 
800+ 		assert ! ( !invoice. verify( & expanded_key,  & secp_ctx) ) ; 
801+ 	} 
802+ 
803+ 	#[ test]  
804+ 	fn  builds_refund_with_derived_payer_id ( )  { 
805+ 		let  desc = "foo" . to_string ( ) ; 
806+ 		let  node_id = payer_pubkey ( ) ; 
807+ 		let  expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ;  32 ] ) ) ; 
808+ 		let  entropy = FixedEntropy  { } ; 
809+ 		let  secp_ctx = Secp256k1 :: new ( ) ; 
810+ 
811+ 		let  blinded_path = BlindedPath  { 
812+ 			introduction_node_id :  pubkey ( 40 ) , 
813+ 			blinding_point :  pubkey ( 41 ) , 
814+ 			blinded_hops :  vec ! [ 
815+ 				BlindedHop  {  blinded_node_id:  pubkey( 43 ) ,  encrypted_payload:  vec![ 0 ;  43 ]  } , 
816+ 				BlindedHop  {  blinded_node_id:  node_id,  encrypted_payload:  vec![ 0 ;  44 ]  } , 
817+ 			] , 
818+ 		} ; 
819+ 
820+ 		let  refund = RefundBuilder 
821+ 			:: deriving_payer_id ( desc,  node_id,  & expanded_key,  & entropy,  & secp_ctx,  1000 ) 
822+ 			. unwrap ( ) 
823+ 			. path ( blinded_path) 
824+ 			. build ( ) . unwrap ( ) ; 
825+ 		assert_ne ! ( refund. payer_id( ) ,  node_id) ; 
826+ 
827+ 		let  invoice = refund
828+ 			. respond_with_no_std ( payment_paths ( ) ,  payment_hash ( ) ,  recipient_pubkey ( ) ,  now ( ) ) 
829+ 			. unwrap ( ) 
830+ 			. build ( ) . unwrap ( ) 
831+ 			. sign ( recipient_sign) . unwrap ( ) ; 
832+ 		assert ! ( invoice. verify( & expanded_key,  & secp_ctx) ) ; 
833+ 
834+ 		// Fails verification with altered fields 
835+ 		let  mut  tlv_stream = refund. as_tlv_stream ( ) ; 
836+ 		tlv_stream. 2 . amount  = Some ( 2000 ) ; 
837+ 
838+ 		let  mut  encoded_refund = Vec :: new ( ) ; 
839+ 		tlv_stream. write ( & mut  encoded_refund) . unwrap ( ) ; 
840+ 
841+ 		let  invoice = Refund :: try_from ( encoded_refund) . unwrap ( ) 
842+ 			. respond_with_no_std ( payment_paths ( ) ,  payment_hash ( ) ,  recipient_pubkey ( ) ,  now ( ) ) 
843+ 			. unwrap ( ) 
844+ 			. build ( ) . unwrap ( ) 
845+ 			. sign ( recipient_sign) . unwrap ( ) ; 
846+ 		assert ! ( !invoice. verify( & expanded_key,  & secp_ctx) ) ; 
847+ 
848+ 		// Fails verification with altered payer_id 
849+ 		let  mut  tlv_stream = refund. as_tlv_stream ( ) ; 
850+ 		let  payer_id = pubkey ( 1 ) ; 
851+ 		tlv_stream. 2 . payer_id  = Some ( & payer_id) ; 
852+ 
853+ 		let  mut  encoded_refund = Vec :: new ( ) ; 
854+ 		tlv_stream. write ( & mut  encoded_refund) . unwrap ( ) ; 
855+ 
856+ 		let  invoice = Refund :: try_from ( encoded_refund) . unwrap ( ) 
857+ 			. respond_with_no_std ( payment_paths ( ) ,  payment_hash ( ) ,  recipient_pubkey ( ) ,  now ( ) ) 
858+ 			. unwrap ( ) 
859+ 			. build ( ) . unwrap ( ) 
860+ 			. sign ( recipient_sign) . unwrap ( ) ; 
861+ 		assert ! ( !invoice. verify( & expanded_key,  & secp_ctx) ) ; 
862+ 	} 
863+ 
729864	#[ test]  
730865	fn  builds_refund_with_absolute_expiry ( )  { 
731866		let  future_expiry = Duration :: from_secs ( u64:: max_value ( ) ) ; 
0 commit comments