@@ -30,6 +30,7 @@ use self::storage::default::SQLiteStorageType;
3030use self :: storage:: plugged:: PluggedStorageType ;
3131use self :: wallet:: { Wallet , Keys , Tags } ;
3232use self :: indy_crypto:: utils:: json:: { JsonDecodable , JsonEncodable } ;
33+ use utils:: crypto:: pwhash_argon2i13:: PwhashArgon2i13 ;
3334
3435
3536#[ derive( Serialize , Deserialize , Debug ) ]
@@ -63,22 +64,30 @@ impl<'a> JsonDecodable<'a> for WalletConfig {}
6364#[ derive( Debug ) ]
6465pub struct WalletCredentials {
6566 master_key : [ u8 ; 32 ] ,
67+ rekey : Option < [ u8 ; 32 ] > ,
6668 storage_credentials : String ,
6769}
6870
69- use utils:: crypto:: pwhash_argon2i13:: PwhashArgon2i13 ;
7071
7172impl WalletCredentials {
7273 fn from_json ( json : & str , salt : & [ u8 ; PwhashArgon2i13 :: SALTBYTES ] ) -> Result < WalletCredentials , WalletError > {
7374 if let serde_json:: Value :: Object ( m) = serde_json:: from_str ( json) ? {
74- let master_key = if let Some ( key) = m[ "key" ] . as_str ( ) {
75+ let master_key = if let Some ( key) = m. get ( "key" ) . and_then ( |s| s . as_str ( ) ) {
7576 let mut master_key: [ u8 ; ChaCha20Poly1305IETF :: KEYBYTES ] = [ 0 ; ChaCha20Poly1305IETF :: KEYBYTES ] ;
7677 PwhashArgon2i13 :: derive_key ( & mut master_key, key. as_bytes ( ) , salt) ?;
7778 master_key
7879 } else {
7980 return Err ( WalletError :: InputError ( String :: from ( "Credentials missing 'key' field" ) ) ) ;
8081 } ;
8182
83+ let rekey = if let Some ( key) = m. get ( "rekey" ) . and_then ( |s| s. as_str ( ) ) {
84+ let mut rekey: [ u8 ; ChaCha20Poly1305IETF :: KEYBYTES ] = [ 0 ; ChaCha20Poly1305IETF :: KEYBYTES ] ;
85+ PwhashArgon2i13 :: derive_key ( & mut rekey, key. as_bytes ( ) , salt) ?;
86+ Some ( rekey)
87+ } else {
88+ None
89+ } ;
90+
8291 let storage_credentials = serde_json:: to_string (
8392 & m. get ( "storage_credentials" )
8493 . and_then ( |storage_credentials| storage_credentials. as_object ( ) )
@@ -87,6 +96,7 @@ impl WalletCredentials {
8796
8897 Ok ( WalletCredentials {
8998 master_key,
99+ rekey,
90100 storage_credentials
91101 } )
92102 } else {
@@ -274,8 +284,7 @@ impl WalletService {
274284 let config = serde_json:: from_str :: < WalletConfig > ( & config_json)
275285 . map_err ( |err| CommonError :: InvalidState ( format ! ( "Cannot deserialize Storage Config" ) ) ) ?;
276286
277- let credentials = WalletCredentials :: from_json ( credentials, & config. salt ) ?
278- ;
287+ let credentials = WalletCredentials :: from_json ( credentials, & config. salt ) ?;
279288 let storage = storage_type. open_storage ( name,
280289 Some ( & config_json) ,
281290 & credentials. storage_credentials ) ?;
@@ -288,9 +297,13 @@ impl WalletService {
288297 Ok ( keys_vector) => keys_vector,
289298 Err ( _) => return Err ( WalletError :: AccessFailed ( "Invalid master key provided" . to_string ( ) ) ) ,
290299 } ;
300+
291301 let keys = Keys :: new ( keys_vector) ;
292302
293303 let wallet = Wallet :: new ( name, & descriptor. pool_name , storage, keys) ;
304+ if let Some ( ref rekey) = credentials. rekey {
305+ wallet. rotate_key ( & rekey[ ..] ) ?;
306+ }
294307 let wallet_handle = SequenceUtils :: get_next_id ( ) ;
295308 wallets. insert ( wallet_handle, Box :: new ( wallet) ) ;
296309
@@ -739,6 +752,14 @@ mod tests {
739752 String :: from ( r#"{"key":"my_key"}"# )
740753 }
741754
755+ fn _rekey_credentials ( ) -> String {
756+ String :: from ( r#"{"key":"my_key", "rekey": "my_new_key"}"# )
757+ }
758+
759+ fn _credentials_for_new_key ( ) -> String {
760+ String :: from ( r#"{"key": "my_new_key"}"# )
761+ }
762+
742763 fn _cleanup ( ) {
743764 let mut path = std:: env:: home_dir ( ) . unwrap ( ) ;
744765 path. push ( ".indy_client" ) ;
@@ -889,6 +910,16 @@ mod tests {
889910 wallet_service. create_wallet ( "pool1" , "test_wallet" , None , None , & _credentials ( ) ) . unwrap ( ) ;
890911 wallet_service. open_wallet ( "test_wallet" , None , & _credentials ( ) ) . unwrap ( ) ;
891912 }
913+
914+ #[ test]
915+ fn wallet_service_open_wallet_without_master_key_in_credentials_returns_error ( ) {
916+ _cleanup ( ) ;
917+
918+ let wallet_service = WalletService :: new ( ) ;
919+ wallet_service. create_wallet ( "pool1" , "test_wallet" , None , None , & _credentials ( ) ) . unwrap ( ) ;
920+ let res = wallet_service. open_wallet ( "test_wallet" , None , "{}" ) ;
921+ assert_match ! ( Err ( WalletError :: InputError ( _) ) , res) ;
922+ }
892923 //
893924 // // #[test]
894925 // // fn wallet_service_open_works_for_plugged() {
@@ -1521,4 +1552,39 @@ mod tests {
15211552 let get_pool_name_res = wallet_service. get_pool_name ( 1 ) ;
15221553 assert_match ! ( Err ( WalletError :: InvalidHandle ( _) ) , get_pool_name_res) ;
15231554 }
1555+
1556+ /**
1557+ Key rotation test
1558+ */
1559+ #[ test]
1560+ fn wallet_service_key_rotation ( ) {
1561+ _cleanup ( ) ;
1562+
1563+ let wallet_service = WalletService :: new ( ) ;
1564+ wallet_service. create_wallet ( "pool1" , "test_wallet" , None , None , & _credentials ( ) ) . unwrap ( ) ;
1565+ let wallet_handle = wallet_service. open_wallet ( "test_wallet" , None , & _credentials ( ) ) . unwrap ( ) ;
1566+
1567+ wallet_service. add_record ( wallet_handle, "type" , "key1" , "value1" , "{}" ) . unwrap ( ) ;
1568+ let record = wallet_service. get_record ( wallet_handle, "type" , "key1" , & _fetch_options ( true , true , true ) ) . unwrap ( ) ;
1569+ assert_eq ! ( "type" , record. get_type( ) . unwrap( ) ) ;
1570+ assert_eq ! ( "value1" , record. get_value( ) . unwrap( ) ) ;
1571+
1572+ wallet_service. close_wallet ( wallet_handle) . unwrap ( ) ;
1573+
1574+ let wallet_handle = wallet_service. open_wallet ( "test_wallet" , None , & _rekey_credentials ( ) ) . unwrap ( ) ;
1575+ let record = wallet_service. get_record ( wallet_handle, "type" , "key1" , & _fetch_options ( true , true , true ) ) . unwrap ( ) ;
1576+ assert_eq ! ( "type" , record. get_type( ) . unwrap( ) ) ;
1577+ assert_eq ! ( "value1" , record. get_value( ) . unwrap( ) ) ;
1578+ wallet_service. close_wallet ( wallet_handle) . unwrap ( ) ;
1579+
1580+ // Access failed for old key
1581+ let res = wallet_service. open_wallet ( "test_wallet" , None , & _credentials ( ) ) ;
1582+ assert_match ! ( Err ( WalletError :: AccessFailed ( _) ) , res) ;
1583+
1584+ // Works ok with new key when reopening
1585+ let wallet_handle = wallet_service. open_wallet ( "test_wallet" , None , & _credentials_for_new_key ( ) ) . unwrap ( ) ;
1586+ let record = wallet_service. get_record ( wallet_handle, "type" , "key1" , & _fetch_options ( true , true , true ) ) . unwrap ( ) ;
1587+ assert_eq ! ( "type" , record. get_type( ) . unwrap( ) ) ;
1588+ assert_eq ! ( "value1" , record. get_value( ) . unwrap( ) ) ;
1589+ }
15241590}
0 commit comments