55// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
66// accordance with one or both of these licenses.
77
8+ use crate :: io:: utils:: check_namespace_key_validity;
9+ use bitcoin:: bip32:: { ChildNumber , Xpriv } ;
10+ use bitcoin:: secp256k1:: Secp256k1 ;
11+ use bitcoin:: Network ;
812use lightning:: io:: { self , Error , ErrorKind } ;
13+ use lightning:: util:: persist:: KVStore ;
14+ use prost:: Message ;
15+ use rand:: RngCore ;
916#[ cfg( test) ]
1017use std:: panic:: RefUnwindSafe ;
1118use std:: sync:: Arc ;
1219use std:: time:: Duration ;
13-
14- use crate :: io:: utils:: check_namespace_key_validity;
15- use lightning:: util:: persist:: KVStore ;
16- use prost:: Message ;
17- use rand:: RngCore ;
1820use tokio:: runtime:: Runtime ;
1921use vss_client:: client:: VssClient ;
2022use vss_client:: error:: VssError ;
@@ -23,6 +25,7 @@ use vss_client::types::{
2325 DeleteObjectRequest , GetObjectRequest , KeyValue , ListKeyVersionsRequest , PutObjectRequest ,
2426 Storable ,
2527} ;
28+ use vss_client:: util:: key_obfuscator:: KeyObfuscator ;
2629use vss_client:: util:: retry:: {
2730 ExponentialBackoffRetryPolicy , FilteredRetryPolicy , JitteredRetryPolicy ,
2831 MaxAttemptsRetryPolicy , MaxTotalDelayRetryPolicy , RetryPolicy ,
@@ -42,14 +45,23 @@ pub struct VssStore {
4245 store_id : String ,
4346 runtime : Runtime ,
4447 storable_builder : StorableBuilder < RandEntropySource > ,
48+ key_obfuscator : KeyObfuscator ,
4549}
50+ const DATA_ENCRYPTION_KEY_DERIVATION_INDEX : u32 = 1 ;
51+ const OBFUSCATION_KEY_DERIVATION_INDEX : u32 = 2 ;
4652
4753impl VssStore {
4854 pub ( crate ) fn new (
49- base_url : String , store_id : String , data_encryption_key : [ u8 ; 32 ] ,
55+ base_url : String , store_id : String , vss_seed : [ u8 ; 32 ] ,
5056 header_provider : Arc < dyn VssHeaderProvider > ,
51- ) -> Self {
52- let runtime = tokio:: runtime:: Builder :: new_multi_thread ( ) . enable_all ( ) . build ( ) . unwrap ( ) ;
57+ ) -> io:: Result < Self > {
58+ let runtime = tokio:: runtime:: Builder :: new_multi_thread ( ) . enable_all ( ) . build ( ) ?;
59+ let vss_master_xprv = Xpriv :: new_master ( Network :: Bitcoin , & vss_seed) . unwrap ( ) ;
60+ let data_encryption_key =
61+ derive_hardened_key ( & vss_master_xprv, DATA_ENCRYPTION_KEY_DERIVATION_INDEX ) ?;
62+ let obfuscation_master_key =
63+ derive_hardened_key ( & vss_master_xprv, OBFUSCATION_KEY_DERIVATION_INDEX ) ?;
64+ let key_obfuscator = KeyObfuscator :: new ( obfuscation_master_key) ;
5365 let storable_builder = StorableBuilder :: new ( data_encryption_key, RandEntropySource ) ;
5466 let retry_policy = ExponentialBackoffRetryPolicy :: new ( Duration :: from_millis ( 100 ) )
5567 . with_max_attempts ( 3 )
@@ -65,24 +77,28 @@ impl VssStore {
6577 } ) as _ ) ;
6678
6779 let client = VssClient :: new_with_headers ( base_url, retry_policy, header_provider) ;
68- Self { client, store_id, runtime, storable_builder }
80+ Ok ( Self { client, store_id, runtime, storable_builder, key_obfuscator } )
6981 }
7082
7183 fn build_key (
7284 & self , primary_namespace : & str , secondary_namespace : & str , key : & str ,
7385 ) -> io:: Result < String > {
86+ let obfuscated_key = self . key_obfuscator . obfuscate ( key) ;
7487 if primary_namespace. is_empty ( ) {
75- Ok ( key . to_string ( ) )
88+ Ok ( obfuscated_key )
7689 } else {
77- Ok ( format ! ( "{}#{}#{}" , primary_namespace, secondary_namespace, key ) )
90+ Ok ( format ! ( "{}#{}#{}" , primary_namespace, secondary_namespace, obfuscated_key ) )
7891 }
7992 }
8093
8194 fn extract_key ( & self , unified_key : & str ) -> io:: Result < String > {
8295 let mut parts = unified_key. splitn ( 3 , '#' ) ;
8396 let ( _primary_namespace, _secondary_namespace) = ( parts. next ( ) , parts. next ( ) ) ;
8497 match parts. next ( ) {
85- Some ( actual_key) => Ok ( actual_key. to_string ( ) ) ,
98+ Some ( obfuscated_key) => {
99+ let actual_key = self . key_obfuscator . deobfuscate ( obfuscated_key) ?;
100+ Ok ( actual_key)
101+ } ,
86102 None => Err ( Error :: new ( ErrorKind :: InvalidData , "Invalid key format" ) ) ,
87103 }
88104 }
@@ -224,6 +240,20 @@ impl KVStore for VssStore {
224240 }
225241}
226242
243+ fn derive_hardened_key ( vss_seed : & Xpriv , index : u32 ) -> io:: Result < [ u8 ; 32 ] > {
244+ let derived_key_xprv = vss_seed
245+ . derive_priv ( & Secp256k1 :: new ( ) , & [ ChildNumber :: Hardened { index } ] )
246+ . map_err ( |e| {
247+ let msg = format ! (
248+ "Failed to derive an extended private key from VSS seed with index: {}, error: {}" ,
249+ index, e
250+ ) ;
251+ Error :: new ( ErrorKind :: Other , msg)
252+ } ) ?;
253+ let derived_key = derived_key_xprv. private_key . secret_bytes ( ) ;
254+ Ok ( derived_key)
255+ }
256+
227257/// A source for generating entropy/randomness using [`rand`].
228258pub ( crate ) struct RandEntropySource ;
229259
@@ -251,11 +281,11 @@ mod tests {
251281 let vss_base_url = std:: env:: var ( "TEST_VSS_BASE_URL" ) . unwrap ( ) ;
252282 let mut rng = thread_rng ( ) ;
253283 let rand_store_id: String = ( 0 ..7 ) . map ( |_| rng. sample ( Alphanumeric ) as char ) . collect ( ) ;
254- let mut data_encryption_key = [ 0u8 ; 32 ] ;
255- rng. fill_bytes ( & mut data_encryption_key ) ;
284+ let mut vss_seed = [ 0u8 ; 32 ] ;
285+ rng. fill_bytes ( & mut vss_seed ) ;
256286 let header_provider = Arc :: new ( FixedHeaders :: new ( HashMap :: new ( ) ) ) ;
257287 let vss_store =
258- VssStore :: new ( vss_base_url, rand_store_id, data_encryption_key , header_provider) ;
288+ VssStore :: new ( vss_base_url, rand_store_id, vss_seed , header_provider) . unwrap ( ) ;
259289
260290 do_read_write_remove_list_persist ( & vss_store) ;
261291 }
0 commit comments