5
5
// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
6
6
// accordance with one or both of these licenses.
7
7
8
+ use crate :: io:: utils:: check_namespace_key_validity;
9
+ use bitcoin:: hashes:: { sha256, Hash , HashEngine , Hmac , HmacEngine } ;
8
10
use lightning:: io:: { self , Error , ErrorKind } ;
11
+ use lightning:: util:: persist:: KVStore ;
12
+ use prost:: Message ;
13
+ use rand:: RngCore ;
9
14
#[ cfg( test) ]
10
15
use std:: panic:: RefUnwindSafe ;
11
16
use std:: sync:: Arc ;
12
17
use 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 ;
18
18
use tokio:: runtime:: Runtime ;
19
19
use vss_client:: client:: VssClient ;
20
20
use vss_client:: error:: VssError ;
@@ -23,6 +23,7 @@ use vss_client::types::{
23
23
DeleteObjectRequest , GetObjectRequest , KeyValue , ListKeyVersionsRequest , PutObjectRequest ,
24
24
Storable ,
25
25
} ;
26
+ use vss_client:: util:: key_obfuscator:: KeyObfuscator ;
26
27
use vss_client:: util:: retry:: {
27
28
ExponentialBackoffRetryPolicy , FilteredRetryPolicy , JitteredRetryPolicy ,
28
29
MaxAttemptsRetryPolicy , MaxTotalDelayRetryPolicy , RetryPolicy ,
@@ -42,14 +43,18 @@ pub struct VssStore {
42
43
store_id : String ,
43
44
runtime : Runtime ,
44
45
storable_builder : StorableBuilder < RandEntropySource > ,
46
+ key_obfuscator : KeyObfuscator ,
45
47
}
46
48
47
49
impl VssStore {
48
50
pub ( crate ) fn new (
49
- base_url : String , store_id : String , data_encryption_key : [ u8 ; 32 ] ,
51
+ base_url : String , store_id : String , vss_seed : [ u8 ; 32 ] ,
50
52
header_provider : Arc < dyn VssHeaderProvider > ,
51
- ) -> Self {
52
- let runtime = tokio:: runtime:: Builder :: new_multi_thread ( ) . enable_all ( ) . build ( ) . unwrap ( ) ;
53
+ ) -> io:: Result < Self > {
54
+ let runtime = tokio:: runtime:: Builder :: new_multi_thread ( ) . enable_all ( ) . build ( ) ?;
55
+ let ( data_encryption_key, obfuscation_master_key) =
56
+ derive_data_encryption_and_obfuscation_keys ( & vss_seed) ;
57
+ let key_obfuscator = KeyObfuscator :: new ( obfuscation_master_key) ;
53
58
let storable_builder = StorableBuilder :: new ( data_encryption_key, RandEntropySource ) ;
54
59
let retry_policy = ExponentialBackoffRetryPolicy :: new ( Duration :: from_millis ( 10 ) )
55
60
. with_max_attempts ( 10 )
@@ -65,24 +70,28 @@ impl VssStore {
65
70
} ) as _ ) ;
66
71
67
72
let client = VssClient :: new_with_headers ( base_url, retry_policy, header_provider) ;
68
- Self { client, store_id, runtime, storable_builder }
73
+ Ok ( Self { client, store_id, runtime, storable_builder, key_obfuscator } )
69
74
}
70
75
71
76
fn build_key (
72
77
& self , primary_namespace : & str , secondary_namespace : & str , key : & str ,
73
78
) -> io:: Result < String > {
79
+ let obfuscated_key = self . key_obfuscator . obfuscate ( key) ;
74
80
if primary_namespace. is_empty ( ) {
75
- Ok ( key . to_string ( ) )
81
+ Ok ( obfuscated_key )
76
82
} else {
77
- Ok ( format ! ( "{}#{}#{}" , primary_namespace, secondary_namespace, key ) )
83
+ Ok ( format ! ( "{}#{}#{}" , primary_namespace, secondary_namespace, obfuscated_key ) )
78
84
}
79
85
}
80
86
81
87
fn extract_key ( & self , unified_key : & str ) -> io:: Result < String > {
82
88
let mut parts = unified_key. splitn ( 3 , '#' ) ;
83
89
let ( _primary_namespace, _secondary_namespace) = ( parts. next ( ) , parts. next ( ) ) ;
84
90
match parts. next ( ) {
85
- Some ( actual_key) => Ok ( actual_key. to_string ( ) ) ,
91
+ Some ( obfuscated_key) => {
92
+ let actual_key = self . key_obfuscator . deobfuscate ( obfuscated_key) ?;
93
+ Ok ( actual_key)
94
+ } ,
86
95
None => Err ( Error :: new ( ErrorKind :: InvalidData , "Invalid key format" ) ) ,
87
96
}
88
97
}
@@ -224,6 +233,19 @@ impl KVStore for VssStore {
224
233
}
225
234
}
226
235
236
+ fn derive_data_encryption_and_obfuscation_keys ( vss_seed : & [ u8 ; 32 ] ) -> ( [ u8 ; 32 ] , [ u8 ; 32 ] ) {
237
+ let hkdf = |initial_key_material : & [ u8 ] , salt : & [ u8 ] | -> [ u8 ; 32 ] {
238
+ let mut engine = HmacEngine :: < sha256:: Hash > :: new ( salt) ;
239
+ engine. input ( initial_key_material) ;
240
+ Hmac :: from_engine ( engine) . to_byte_array ( )
241
+ } ;
242
+
243
+ let prk = hkdf ( vss_seed, b"pseudo_random_key" ) ;
244
+ let k1 = hkdf ( & prk, b"data_encryption_key" ) ;
245
+ let k2 = hkdf ( & prk, & [ & k1[ ..] , b"obfuscation_key" ] . concat ( ) ) ;
246
+ ( k1, k2)
247
+ }
248
+
227
249
/// A source for generating entropy/randomness using [`rand`].
228
250
pub ( crate ) struct RandEntropySource ;
229
251
@@ -251,11 +273,11 @@ mod tests {
251
273
let vss_base_url = std:: env:: var ( "TEST_VSS_BASE_URL" ) . unwrap ( ) ;
252
274
let mut rng = thread_rng ( ) ;
253
275
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 ) ;
276
+ let mut vss_seed = [ 0u8 ; 32 ] ;
277
+ rng. fill_bytes ( & mut vss_seed ) ;
256
278
let header_provider = Arc :: new ( FixedHeaders :: new ( HashMap :: new ( ) ) ) ;
257
279
let vss_store =
258
- VssStore :: new ( vss_base_url, rand_store_id, data_encryption_key , header_provider) ;
280
+ VssStore :: new ( vss_base_url, rand_store_id, vss_seed , header_provider) . unwrap ( ) ;
259
281
260
282
do_read_write_remove_list_persist ( & vss_store) ;
261
283
}
0 commit comments