67
67
//! ```
68
68
69
69
use bitcoin:: blockdata:: constants:: ChainHash ;
70
+ use bitcoin:: hashes:: { Hash , HashEngine } ;
71
+ use bitcoin:: hashes:: hmac:: { Hmac , HmacEngine } ;
72
+ use bitcoin:: hashes:: sha256:: Hash as Sha256 ;
70
73
use bitcoin:: network:: constants:: Network ;
71
74
use bitcoin:: secp256k1:: PublicKey ;
72
75
use core:: convert:: TryFrom ;
@@ -75,8 +78,10 @@ use core::str::FromStr;
75
78
use core:: time:: Duration ;
76
79
use crate :: io;
77
80
use crate :: ln:: features:: OfferFeatures ;
81
+ use crate :: ln:: inbound_payment:: ExpandedKey ;
78
82
use crate :: ln:: msgs:: MAX_VALUE_MSAT ;
79
83
use crate :: offers:: invoice_request:: InvoiceRequestBuilder ;
84
+ use crate :: offers:: merkle:: TlvStream ;
80
85
use crate :: offers:: parse:: { Bech32Encode , ParseError , ParsedMessage , SemanticError } ;
81
86
use crate :: onion_message:: BlindedPath ;
82
87
use crate :: util:: ser:: { HighZeroBytesDroppedBigSize , WithoutLength , Writeable , Writer } ;
@@ -94,6 +99,7 @@ use std::time::SystemTime;
94
99
/// [module-level documentation]: self
95
100
pub struct OfferBuilder {
96
101
offer : OfferContents ,
102
+ hmac : Option < HmacEngine < Sha256 > > ,
97
103
}
98
104
99
105
impl OfferBuilder {
@@ -108,7 +114,7 @@ impl OfferBuilder {
108
114
features : OfferFeatures :: empty ( ) , absolute_expiry : None , issuer : None , paths : None ,
109
115
supported_quantity : Quantity :: One , signing_pubkey,
110
116
} ;
111
- OfferBuilder { offer }
117
+ OfferBuilder { offer, hmac : None }
112
118
}
113
119
114
120
/// Adds the chain hash of the given [`Network`] to [`Offer::chains`]. If not called,
@@ -127,11 +133,24 @@ impl OfferBuilder {
127
133
self
128
134
}
129
135
130
- /// Sets the [`Offer::metadata`].
136
+ /// Sets the [`Offer::metadata`] to the given bytes .
131
137
///
132
- /// Successive calls to this method will override the previous setting.
138
+ /// Successive calls to this method will override the previous setting and any previous calls to
139
+ /// [`OfferBuilder::metadata_derived`].
133
140
pub fn metadata ( mut self , metadata : Vec < u8 > ) -> Self {
134
141
self . offer . metadata = Some ( metadata) ;
142
+ self . hmac = None ;
143
+ self
144
+ }
145
+
146
+ /// Sets the [`Offer::metadata`] derived from the given `key` and any fields set prior to
147
+ /// calling [`OfferBuilder::build`].
148
+ ///
149
+ /// Successive calls to this method will override the previous setting and any previous calls to
150
+ /// [`OfferBuilder::metadata`].
151
+ pub fn metadata_derived ( mut self , key : & ExpandedKey ) -> Self {
152
+ self . offer . metadata = None ;
153
+ self . hmac = Some ( key. hmac_for_offer ( ) ) ;
135
154
self
136
155
}
137
156
@@ -204,6 +223,11 @@ impl OfferBuilder {
204
223
}
205
224
}
206
225
226
+ if let Some ( mut hmac) = self . hmac {
227
+ self . offer . write ( & mut hmac) . unwrap ( ) ;
228
+ self . offer . metadata = Some ( Hmac :: from_engine ( hmac) . into_inner ( ) . to_vec ( ) ) ;
229
+ }
230
+
207
231
let mut bytes = Vec :: new ( ) ;
208
232
self . offer . write ( & mut bytes) . unwrap ( ) ;
209
233
@@ -482,6 +506,26 @@ impl OfferContents {
482
506
self . signing_pubkey
483
507
}
484
508
509
+ /// Verifies that the offer metadata was produced from the offer in the TLV stream.
510
+ pub ( super ) fn verify ( & self , tlv_stream : TlvStream < ' _ > , key : & ExpandedKey ) -> bool {
511
+ match & self . metadata {
512
+ Some ( metadata) => {
513
+ let mut hmac = key. hmac_for_offer ( ) ;
514
+
515
+ for record in tlv_stream. range ( OFFER_TYPES ) {
516
+ if record. r#type != OFFER_METADATA_TYPE {
517
+ hmac. input ( record. record_bytes ) ;
518
+ } else {
519
+ // TODO: Assert value bytes == metadata?
520
+ }
521
+ }
522
+
523
+ metadata == & Hmac :: from_engine ( hmac) . into_inner ( )
524
+ } ,
525
+ None => false ,
526
+ }
527
+ }
528
+
485
529
pub ( super ) fn as_tlv_stream ( & self ) -> OfferTlvStreamRef {
486
530
let ( currency, amount) = match & self . amount {
487
531
None => ( None , None ) ,
@@ -565,9 +609,15 @@ impl Quantity {
565
609
}
566
610
}
567
611
568
- tlv_stream ! ( OfferTlvStream , OfferTlvStreamRef , 1 ..80 , {
612
+ /// Valid type range for offer TLV records.
613
+ const OFFER_TYPES : core:: ops:: Range < u64 > = 1 ..80 ;
614
+
615
+ /// TLV record type for [`Offer::metadata`].
616
+ const OFFER_METADATA_TYPE : u64 = 4 ;
617
+
618
+ tlv_stream ! ( OfferTlvStream , OfferTlvStreamRef , OFFER_TYPES , {
569
619
( 2 , chains: ( Vec <ChainHash >, WithoutLength ) ) ,
570
- ( 4 , metadata: ( Vec <u8 >, WithoutLength ) ) ,
620
+ ( OFFER_METADATA_TYPE , metadata: ( Vec <u8 >, WithoutLength ) ) ,
571
621
( 6 , currency: CurrencyCode ) ,
572
622
( 8 , amount: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
573
623
( 10 , description: ( String , WithoutLength ) ) ,
@@ -661,17 +711,40 @@ mod tests {
661
711
662
712
use bitcoin:: blockdata:: constants:: ChainHash ;
663
713
use bitcoin:: network:: constants:: Network ;
664
- use bitcoin:: secp256k1:: { PublicKey , Secp256k1 , SecretKey } ;
665
- use core:: convert:: TryFrom ;
714
+ use bitcoin:: secp256k1:: { KeyPair , Message , PublicKey , Secp256k1 , SecretKey } ;
715
+ use bitcoin:: secp256k1:: schnorr:: Signature ;
716
+ use core:: convert:: { Infallible , TryFrom } ;
666
717
use core:: num:: NonZeroU64 ;
667
718
use core:: time:: Duration ;
719
+ use crate :: chain:: keysinterface:: KeyMaterial ;
668
720
use crate :: ln:: features:: OfferFeatures ;
721
+ use crate :: ln:: inbound_payment:: ExpandedKey ;
669
722
use crate :: ln:: msgs:: { DecodeError , MAX_VALUE_MSAT } ;
670
723
use crate :: offers:: parse:: { ParseError , SemanticError } ;
671
724
use crate :: onion_message:: { BlindedHop , BlindedPath } ;
672
725
use crate :: util:: ser:: { BigSize , Writeable } ;
673
726
use crate :: util:: string:: PrintableString ;
674
727
728
+ fn payer_keys ( ) -> KeyPair {
729
+ let secp_ctx = Secp256k1 :: new ( ) ;
730
+ KeyPair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) )
731
+ }
732
+
733
+ fn payer_sign ( digest : & Message ) -> Result < Signature , Infallible > {
734
+ let secp_ctx = Secp256k1 :: new ( ) ;
735
+ let keys = KeyPair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ) ;
736
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( digest, & keys) )
737
+ }
738
+
739
+ fn payer_pubkey ( ) -> PublicKey {
740
+ payer_keys ( ) . public_key ( )
741
+ }
742
+
743
+ fn recipient_pubkey ( ) -> PublicKey {
744
+ let secp_ctx = Secp256k1 :: new ( ) ;
745
+ KeyPair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 43 ; 32 ] ) . unwrap ( ) ) . public_key ( )
746
+ }
747
+
675
748
fn pubkey ( byte : u8 ) -> PublicKey {
676
749
let secp_ctx = Secp256k1 :: new ( ) ;
677
750
PublicKey :: from_secret_key ( & secp_ctx, & privkey ( byte) )
@@ -784,6 +857,35 @@ mod tests {
784
857
assert_eq ! ( offer. as_tlv_stream( ) . metadata, Some ( & vec![ 43 ; 32 ] ) ) ;
785
858
}
786
859
860
+ #[ test]
861
+ fn builds_offer_with_metadata_derived ( ) {
862
+ let keys = ExpandedKey :: new ( & KeyMaterial ( [ 42 ; 32 ] ) ) ;
863
+ let invoice_request = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
864
+ . metadata_derived ( & keys)
865
+ . amount_msats ( 1000 )
866
+ . build ( ) . unwrap ( )
867
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
868
+ . build ( ) . unwrap ( )
869
+ . sign ( payer_sign) . unwrap ( ) ;
870
+ assert ! ( invoice_request. verify( & keys) ) ;
871
+
872
+ let offer = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
873
+ . metadata_derived ( & keys)
874
+ . amount_msats ( 1000 )
875
+ . build ( ) . unwrap ( ) ;
876
+ let mut tlv_stream = offer. as_tlv_stream ( ) ;
877
+ tlv_stream. amount = Some ( 100 ) ;
878
+
879
+ let mut encoded_offer = Vec :: new ( ) ;
880
+ tlv_stream. write ( & mut encoded_offer) . unwrap ( ) ;
881
+
882
+ let invoice_request = Offer :: try_from ( encoded_offer) . unwrap ( )
883
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
884
+ . build ( ) . unwrap ( )
885
+ . sign ( payer_sign) . unwrap ( ) ;
886
+ assert ! ( !invoice_request. verify( & keys) ) ;
887
+ }
888
+
787
889
#[ test]
788
890
fn builds_offer_with_amount ( ) {
789
891
let bitcoin_amount = Amount :: Bitcoin { amount_msats : 1000 } ;
0 commit comments