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
53
) -> Self {
52
54
let runtime = tokio:: runtime:: Builder :: new_multi_thread ( ) . enable_all ( ) . build ( ) . unwrap ( ) ;
55
+ let ( data_encryption_key, obfuscation_master_key) =
56
+ derive_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 ( 100 ) )
55
60
. with_max_attempts ( 3 )
@@ -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
+ 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
+ /// Derives the data encryption and obfuscation keys from the vss master key.
237
+ fn derive_encryption_and_obfuscation_keys ( vss_seed : & [ u8 ; 32 ] ) -> ( [ u8 ; 32 ] , [ u8 ; 32 ] ) {
238
+ let prk = hkdf ( vss_seed, "pseudo_random_key" . as_bytes ( ) ) ;
239
+ let k1 = hkdf ( & prk, "data_encryption_key" . as_bytes ( ) ) ;
240
+ let k2 = hkdf ( & prk, & [ & k1[ ..] , "obfuscation_master_key" . as_bytes ( ) ] . concat ( ) ) ;
241
+ ( k1, k2)
242
+ }
243
+ fn hkdf ( initial_key_material : & [ u8 ] , salt : & [ u8 ] ) -> [ u8 ; 32 ] {
244
+ let mut engine = HmacEngine :: < sha256:: Hash > :: new ( salt) ;
245
+ engine. input ( initial_key_material) ;
246
+ Hmac :: from_engine ( engine) . to_byte_array ( )
247
+ }
248
+
227
249
/// A source for generating entropy/randomness using [`rand`].
228
250
pub ( crate ) struct RandEntropySource ;
229
251
@@ -251,11 +273,10 @@ 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
- let vss_store =
258
- VssStore :: new ( vss_base_url, rand_store_id, data_encryption_key, header_provider) ;
279
+ let vss_store = VssStore :: new ( vss_base_url, rand_store_id, vss_seed, header_provider) ;
259
280
260
281
do_read_write_remove_list_persist ( & vss_store) ;
261
282
}
0 commit comments