@@ -78,8 +78,9 @@ use crate::ln::features::OfferFeatures;
78
78
use crate :: ln:: inbound_payment:: { ExpandedKey , Nonce } ;
79
79
use crate :: ln:: msgs:: MAX_VALUE_MSAT ;
80
80
use crate :: offers:: invoice_request:: InvoiceRequestBuilder ;
81
+ use crate :: offers:: merkle:: TlvStream ;
81
82
use crate :: offers:: parse:: { Bech32Encode , ParseError , ParsedMessage , SemanticError } ;
82
- use crate :: offers:: signer:: { MetadataMaterial , DerivedPubkey } ;
83
+ use crate :: offers:: signer:: { MetadataMaterial , DerivedPubkey , self } ;
83
84
use crate :: onion_message:: BlindedPath ;
84
85
use crate :: util:: ser:: { HighZeroBytesDroppedBigSize , WithoutLength , Writeable , Writer } ;
85
86
use crate :: util:: string:: PrintableString ;
@@ -542,6 +543,24 @@ impl OfferContents {
542
543
self . signing_pubkey
543
544
}
544
545
546
+ /// Verifies that the offer metadata was produced from the offer in the TLV stream.
547
+ pub ( super ) fn verify ( & self , tlv_stream : TlvStream < ' _ > , key : & ExpandedKey ) -> bool {
548
+ match & self . metadata {
549
+ Some ( metadata) => {
550
+ let tlv_stream = tlv_stream. range ( OFFER_TYPES ) . filter ( |record| {
551
+ match record. r#type {
552
+ // TODO: Assert value bytes == metadata?
553
+ OFFER_METADATA_TYPE => false ,
554
+ OFFER_NODE_ID_TYPE => false ,
555
+ _ => true ,
556
+ }
557
+ } ) ;
558
+ signer:: verify_metadata ( metadata, key, tlv_stream)
559
+ } ,
560
+ None => false ,
561
+ }
562
+ }
563
+
545
564
pub ( super ) fn as_tlv_stream ( & self ) -> OfferTlvStreamRef {
546
565
let ( currency, amount) = match & self . amount {
547
566
None => ( None , None ) ,
@@ -629,9 +648,18 @@ impl Quantity {
629
648
}
630
649
}
631
650
632
- tlv_stream ! ( OfferTlvStream , OfferTlvStreamRef , 1 ..80 , {
651
+ /// Valid type range for offer TLV records.
652
+ const OFFER_TYPES : core:: ops:: Range < u64 > = 1 ..80 ;
653
+
654
+ /// TLV record type for [`Offer::metadata`].
655
+ const OFFER_METADATA_TYPE : u64 = 4 ;
656
+
657
+ /// TLV record type for [`Offer::signing_pubkey`].
658
+ const OFFER_NODE_ID_TYPE : u64 = 22 ;
659
+
660
+ tlv_stream ! ( OfferTlvStream , OfferTlvStreamRef , OFFER_TYPES , {
633
661
( 2 , chains: ( Vec <ChainHash >, WithoutLength ) ) ,
634
- ( 4 , metadata: ( Vec <u8 >, WithoutLength ) ) ,
662
+ ( OFFER_METADATA_TYPE , metadata: ( Vec <u8 >, WithoutLength ) ) ,
635
663
( 6 , currency: CurrencyCode ) ,
636
664
( 8 , amount: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
637
665
( 10 , description: ( String , WithoutLength ) ) ,
@@ -640,7 +668,7 @@ tlv_stream!(OfferTlvStream, OfferTlvStreamRef, 1..80, {
640
668
( 16 , paths: ( Vec <BlindedPath >, WithoutLength ) ) ,
641
669
( 18 , issuer: ( String , WithoutLength ) ) ,
642
670
( 20 , quantity_max: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
643
- ( 22 , node_id: PublicKey ) ,
671
+ ( OFFER_NODE_ID_TYPE , node_id: PublicKey ) ,
644
672
} ) ;
645
673
646
674
impl Bech32Encode for Offer {
@@ -728,9 +756,12 @@ mod tests {
728
756
use core:: convert:: TryFrom ;
729
757
use core:: num:: NonZeroU64 ;
730
758
use core:: time:: Duration ;
759
+ use crate :: chain:: keysinterface:: KeyMaterial ;
731
760
use crate :: ln:: features:: OfferFeatures ;
761
+ use crate :: ln:: inbound_payment:: { ExpandedKey , Nonce } ;
732
762
use crate :: ln:: msgs:: { DecodeError , MAX_VALUE_MSAT } ;
733
763
use crate :: offers:: parse:: { ParseError , SemanticError } ;
764
+ use crate :: offers:: signer:: DerivedPubkey ;
734
765
use crate :: offers:: test_utils:: * ;
735
766
use crate :: onion_message:: { BlindedHop , BlindedPath } ;
736
767
use crate :: util:: ser:: { BigSize , Writeable } ;
@@ -839,6 +870,82 @@ mod tests {
839
870
assert_eq ! ( offer. as_tlv_stream( ) . metadata, Some ( & vec![ 43 ; 32 ] ) ) ;
840
871
}
841
872
873
+ #[ test]
874
+ fn builds_offer_with_metadata_derived ( ) {
875
+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
876
+ let nonce = Nonce ( [ 42 ; Nonce :: LENGTH ] ) ;
877
+
878
+ let offer = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
879
+ . amount_msats ( 1000 )
880
+ . metadata_derived ( & expanded_key, nonce) . unwrap ( )
881
+ . build ( ) . unwrap ( ) ;
882
+ assert_eq ! ( offer. metadata( ) . unwrap( ) [ ..Nonce :: LENGTH ] , nonce. 0 ) ;
883
+ assert_eq ! ( offer. signing_pubkey( ) , recipient_pubkey( ) ) ;
884
+
885
+ let invoice_request = offer. request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
886
+ . build ( ) . unwrap ( )
887
+ . sign ( payer_sign) . unwrap ( ) ;
888
+ assert ! ( invoice_request. verify( & expanded_key) ) ;
889
+
890
+ let mut tlv_stream = offer. as_tlv_stream ( ) ;
891
+ tlv_stream. amount = Some ( 100 ) ;
892
+
893
+ let mut encoded_offer = Vec :: new ( ) ;
894
+ tlv_stream. write ( & mut encoded_offer) . unwrap ( ) ;
895
+
896
+ let invoice_request = Offer :: try_from ( encoded_offer) . unwrap ( )
897
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
898
+ . build ( ) . unwrap ( )
899
+ . sign ( payer_sign) . unwrap ( ) ;
900
+ assert ! ( !invoice_request. verify( & expanded_key) ) ;
901
+
902
+ match OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
903
+ . metadata_derived ( & expanded_key, nonce) . unwrap ( )
904
+ . metadata_derived ( & expanded_key, nonce)
905
+ {
906
+ Ok ( _) => panic ! ( "expected error" ) ,
907
+ Err ( e) => assert_eq ! ( e, SemanticError :: UnexpectedMetadata ) ,
908
+ }
909
+ }
910
+
911
+ #[ test]
912
+ fn builds_offer_with_derived_signing_pubkey ( ) {
913
+ let expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
914
+ let nonce = Nonce ( [ 42 ; Nonce :: LENGTH ] ) ;
915
+
916
+ let recipient_pubkey = DerivedPubkey :: new ( & expanded_key, nonce) ;
917
+ let offer = OfferBuilder :: deriving_signing_pubkey ( "foo" . into ( ) , recipient_pubkey)
918
+ . amount_msats ( 1000 )
919
+ . build ( ) . unwrap ( ) ;
920
+ assert_eq ! ( offer. metadata( ) . unwrap( ) [ ..Nonce :: LENGTH ] , nonce. 0 ) ;
921
+ assert_eq ! ( offer. signing_pubkey( ) , expanded_key. signing_pubkey_for_offer( nonce) ) ;
922
+
923
+ let invoice_request = offer. request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
924
+ . build ( ) . unwrap ( )
925
+ . sign ( payer_sign) . unwrap ( ) ;
926
+ assert ! ( invoice_request. verify( & expanded_key) ) ;
927
+
928
+ let mut tlv_stream = offer. as_tlv_stream ( ) ;
929
+ tlv_stream. amount = Some ( 100 ) ;
930
+
931
+ let mut encoded_offer = Vec :: new ( ) ;
932
+ tlv_stream. write ( & mut encoded_offer) . unwrap ( ) ;
933
+
934
+ let invoice_request = Offer :: try_from ( encoded_offer) . unwrap ( )
935
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
936
+ . build ( ) . unwrap ( )
937
+ . sign ( payer_sign) . unwrap ( ) ;
938
+ assert ! ( !invoice_request. verify( & expanded_key) ) ;
939
+
940
+ let recipient_pubkey = DerivedPubkey :: new ( & expanded_key, nonce) ;
941
+ match OfferBuilder :: deriving_signing_pubkey ( "foo" . into ( ) , recipient_pubkey)
942
+ . metadata_derived ( & expanded_key, nonce)
943
+ {
944
+ Ok ( _) => panic ! ( "expected error" ) ,
945
+ Err ( e) => assert_eq ! ( e, SemanticError :: UnexpectedMetadata ) ,
946
+ }
947
+ }
948
+
842
949
#[ test]
843
950
fn builds_offer_with_amount ( ) {
844
951
let bitcoin_amount = Amount :: Bitcoin { amount_msats : 1000 } ;
0 commit comments