Skip to content

Commit

Permalink
[#10195] Separate keystore dir per multiacc
Browse files Browse the repository at this point in the history
Having all keys related to specific multiacc in a
separate dir will simplify removing of the multiacc.
Also it will allow adding of the same account to
different multiaccs.
  • Loading branch information
rasom committed Jun 24, 2020
1 parent 41e04b9 commit c5837b6
Show file tree
Hide file tree
Showing 14 changed files with 236 additions and 123 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ private String pathCombine(final String path1, final String path2) {
return file.getAbsolutePath();
}

private String prepareDirAndUpdateConfig(final String jsonConfigString) {
private String prepareDirAndUpdateConfig(final String jsonConfigString, final String keyUID) {

Activity currentActivity = getCurrentActivity();

Expand Down Expand Up @@ -303,7 +303,8 @@ private String prepareDirAndUpdateConfig(final String jsonConfigString) {
}

try {
final String updatedJsonConfigString = this.updateConfig(jsonConfigString, absRootDirPath, "/keystore");
final String multiaccountKeystoreDir = pathCombine("/keystore", keyUID);
final String updatedJsonConfigString = this.updateConfig(jsonConfigString, absRootDirPath, multiaccountKeystoreDir);

prettyPrintConfig(updatedJsonConfigString);

Expand All @@ -317,41 +318,55 @@ private String prepareDirAndUpdateConfig(final String jsonConfigString) {
}

@ReactMethod
public void prepareDirAndUpdateConfig(final String config, final Callback callback) {
public void prepareDirAndUpdateConfig(final String keyUID, final String config, final Callback callback) {
Log.d(TAG, "prepareDirAndUpdateConfig");
String finalConfig = prepareDirAndUpdateConfig(config);
String finalConfig = prepareDirAndUpdateConfig(config, keyUID);
callback.invoke(finalConfig);
}

@ReactMethod
public void saveAccountAndLogin(final String multiaccountData, final String password, final String settings, final String config, final String accountsData) {
Log.d(TAG, "saveAccountAndLogin");
String finalConfig = prepareDirAndUpdateConfig(config);
String result = Statusgo.saveAccountAndLogin(multiaccountData, password, settings, finalConfig, accountsData);
if (result.startsWith("{\"error\":\"\"")) {
Log.d(TAG, "saveAccountAndLogin result: " + result);
Log.d(TAG, "Geth node started");
} else {
Log.e(TAG, "saveAccountAndLogin failed: " + result);
try {
Log.d(TAG, "saveAccountAndLogin");
String finalConfig = prepareDirAndUpdateConfig(config, this.getKeyUID(multiaccountData));
String result = Statusgo.saveAccountAndLogin(multiaccountData, password, settings, finalConfig, accountsData);
if (result.startsWith("{\"error\":\"\"")) {
Log.d(TAG, "saveAccountAndLogin result: " + result);
Log.d(TAG, "Geth node started");
} else {
Log.e(TAG, "saveAccountAndLogin failed: " + result);
}
} catch (JSONException e) {
Log.e(TAG, "JSON conversion failed: " + e.getMessage());
}
}

@ReactMethod
public void saveAccountAndLoginWithKeycard(final String multiaccountData, final String password, final String settings, final String config, final String accountsData, final String chatKey) {
Log.d(TAG, "saveAccountAndLoginWithKeycard");
String finalConfig = prepareDirAndUpdateConfig(config);
String result = Statusgo.saveAccountAndLoginWithKeycard(multiaccountData, password, settings, finalConfig, accountsData, chatKey);
if (result.startsWith("{\"error\":\"\"")) {
Log.d(TAG, "saveAccountAndLoginWithKeycard result: " + result);
Log.d(TAG, "Geth node started");
} else {
Log.e(TAG, "saveAccountAndLoginWithKeycard failed: " + result);
try {
Log.d(TAG, "saveAccountAndLoginWithKeycard");
String finalConfig = prepareDirAndUpdateConfig(config, this.getKeyUID(multiaccountData));
String result = Statusgo.saveAccountAndLoginWithKeycard(multiaccountData, password, settings, finalConfig, accountsData, chatKey);
if (result.startsWith("{\"error\":\"\"")) {
Log.d(TAG, "saveAccountAndLoginWithKeycard result: " + result);
Log.d(TAG, "Geth node started");
} else {
Log.e(TAG, "saveAccountAndLoginWithKeycard failed: " + result);
}
} catch (JSONException e) {
Log.e(TAG, "JSON conversion failed: " + e.getMessage());
}
}

private String getKeyUID(final String json) throws JSONException {
final JSONObject jsonObj = new JSONObject(json);
return jsonObj.getString("key-uid");
}

@ReactMethod
public void login(final String accountData, final String password) {
Log.d(TAG, "login");
this.migrateKeyStoreDir(accountData, password);
String result = Statusgo.login(accountData, password);
if (result.startsWith("{\"error\":\"\"")) {
Log.d(TAG, "Login result: " + result);
Expand Down Expand Up @@ -416,23 +431,25 @@ private void copyDirectory(File sourceLocation, File targetLocation) throws IOEx
}

@ReactMethod
private void initKeystore() {
private void initKeystore(final String keyUID, final Callback callback) {
Log.d(TAG, "initKeystore");

Activity currentActivity = getCurrentActivity();

final String keydir = pathCombine(this.getNoBackupDirectory(), "/keystore");

if (!checkAvailability()) {
Log.e(TAG, "[initKeystore] Activity doesn't exist, cannot init keystore");
System.exit(0);
return;
}

final String commonKeydir = pathCombine(this.getNoBackupDirectory(), "/keystore");
final String keydir = pathCombine(commonKeydir, keyUID);

Runnable r = new Runnable() {
@Override
public void run() {
Statusgo.initKeystore(keydir);
callback.invoke(true);
}
};

Expand Down Expand Up @@ -487,9 +504,27 @@ public void run() {
StatusThreadPoolExecutor.getInstance().execute(r);
}

public void migrateKeyStoreDir(final String accountData, final String password) {
try {
final String commonKeydir = pathCombine(this.getNoBackupDirectory(), "/keystore");
final String keydir = pathCombine(commonKeydir, getKeyUID(accountData));
Log.d(TAG, "before migrateKeyStoreDir " + keydir);

File keydirFile = new File(keydir);
if(!keydirFile.exists() || keydirFile.list().length == 0) {
Log.d(TAG, "migrateKeyStoreDir");
Statusgo.migrateKeyStoreDir(accountData, password, commonKeydir, keydir);
Statusgo.initKeystore(keydir);
}
} catch (JSONException e) {
Log.e(TAG, "JSON conversion failed: " + e.getMessage());
}
}

@ReactMethod
public void loginWithKeycard(final String accountData, final String password, final String chatKey) {
Log.d(TAG, "loginWithKeycard");
this.migrateKeyStoreDir(accountData, password);
String result = Statusgo.loginWithKeycard(accountData, password, chatKey);
if (result.startsWith("{\"error\":\"\"")) {
Log.d(TAG, "LoginWithKeycard result: " + result);
Expand Down
64 changes: 54 additions & 10 deletions modules/react-native-status/ios/RCTStatus/RCTStatus.m
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ - (void)handleSignal:(NSString *)signal
////////////////////////////////////////////////////////////////////
#pragma mark - InitKeystore method
//////////////////////////////////////////////////////////////////// StopNode
RCT_EXPORT_METHOD(initKeystore) {
RCT_EXPORT_METHOD(initKeystore:(NSString *)keyUID
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"initKeystore() method called");
#endif
Expand All @@ -119,12 +120,15 @@ - (void)handleSignal:(NSString *)signal
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
lastObject];

NSURL *keystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
NSURL *commonKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
NSURL *keystoreDir = [commonKeystoreDir URLByAppendingPathComponent:keyUID];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^(void)
{
NSString *res = StatusgoInitKeystore(keystoreDir.path);
NSLog(@"InitKeyStore result %@", res);
callback(@[]);
});
}

Expand Down Expand Up @@ -306,8 +310,19 @@ - (void)handleSignal:(NSString *)signal
callback(@[result]);
}

-(NSString *) getKeyUID:(NSString *)jsonString {
NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:nil];

return [json valueForKey:@"key-uid"];
}

//////////////////////////////////////////////////////////////////// prepareDirAndUpdateConfig
-(NSString *) prepareDirAndUpdateConfig:(NSString *)config {
-(NSString *) prepareDirAndUpdateConfig:(NSString *)config
withKeyUID:(NSString *)keyUID {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
NSURL *rootUrl =[[fileManager
Expand Down Expand Up @@ -354,7 +369,8 @@ -(NSString *) prepareDirAndUpdateConfig:(NSString *)config {
NSURL *absDataDirUrl = [NSURL fileURLWithPath:absDataDir];
NSURL *dataDirUrl = [NSURL fileURLWithPath:relativeDataDir];
NSURL *logUrl = [dataDirUrl URLByAppendingPathComponent:@"geth.log"];
[configJSON setValue:@"/keystore" forKey:@"KeyStoreDir"];
NSString *keystoreDir = [@"/keystore/" stringByAppendingString:keyUID];
[configJSON setValue:keystoreDir forKey:@"KeyStoreDir"];
[configJSON setValue:@"" forKey:@"LogDir"];
[configJSON setValue:logUrl.path forKey:@"LogFile"];

Expand All @@ -380,15 +396,15 @@ -(NSString *) prepareDirAndUpdateConfig:(NSString *)config {


//////////////////////////////////////////////////////////////////// prepareDirAndUpdateConfig
RCT_EXPORT_METHOD(prepareDirAndUpdateConfig:(NSString *)config
RCT_EXPORT_METHOD(prepareDirAndUpdateConfig:(NSString *)keyUID
config:(NSString *)config
callback:(RCTResponseSenderBlock)callback) {

#if DEBUG
NSLog(@"PrepareDirAndUpdateConfig() method called");
#endif
NSString *updatedConfig = [self prepareDirAndUpdateConfig:config];
NSString *updatedConfig = [self prepareDirAndUpdateConfig:config
withKeyUID:keyUID];
callback(@[updatedConfig]);

}

//////////////////////////////////////////////////////////////////// saveAccountAndLogin
Expand All @@ -400,7 +416,9 @@ -(NSString *) prepareDirAndUpdateConfig:(NSString *)config {
#if DEBUG
NSLog(@"SaveAccountAndLogin() method called");
#endif
NSString *finalConfig = [self prepareDirAndUpdateConfig:config];
NSString *keyUID = [self getKeyUID:multiaccountData];
NSString *finalConfig = [self prepareDirAndUpdateConfig:config
withKeyUID:keyUID];
NSString *result = StatusgoSaveAccountAndLogin(multiaccountData, password, settings, finalConfig, accountsData);
NSLog(@"%@", result);
}
Expand All @@ -415,17 +433,41 @@ -(NSString *) prepareDirAndUpdateConfig:(NSString *)config {
#if DEBUG
NSLog(@"SaveAccountAndLoginWithKeycard() method called");
#endif
NSString *finalConfig = [self prepareDirAndUpdateConfig:config];
NSString *keyUID = [self getKeyUID:multiaccountData];
NSString *finalConfig = [self prepareDirAndUpdateConfig:config
withKeyUID:keyUID];
NSString *result = StatusgoSaveAccountAndLoginWithKeycard(multiaccountData, password, settings, finalConfig, accountsData, chatKey);
NSLog(@"%@", result);
}

- (void) migrateKeystore:(NSString *)accountData
password:(NSString *)password {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *rootUrl =[[fileManager
URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]
lastObject];

NSString *keyUID = [self getKeyUID:accountData];
NSURL *oldKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
NSURL *multiaccountKeystoreDir = [oldKeystoreDir URLByAppendingPathComponent:keyUID];

NSArray *keys = [fileManager contentsOfDirectoryAtPath:multiaccountKeystoreDir.path error:nil];
if (keys.count == 0) {
NSString *migrationResult = StatusgoMigrateKeyStoreDir(accountData, password, oldKeystoreDir.path, multiaccountKeystoreDir.path);
NSLog(@"keystore migration result %@", migrationResult);

NSString *initKeystoreResult = StatusgoInitKeystore(multiaccountKeystoreDir.path);
NSLog(@"InitKeyStore result %@", initKeystoreResult);
}
}

//////////////////////////////////////////////////////////////////// login
RCT_EXPORT_METHOD(login:(NSString *)accountData
password:(NSString *)password) {
#if DEBUG
NSLog(@"Login() method called");
#endif
[self migrateKeystore:accountData password:password];
NSString *result = StatusgoLogin(accountData, password);
NSLog(@"%@", result);
}
Expand All @@ -437,6 +479,8 @@ -(NSString *) prepareDirAndUpdateConfig:(NSString *)config {
#if DEBUG
NSLog(@"LoginWithKeycard() method called");
#endif
[self migrateKeystore:accountData password:password];

NSString *result = StatusgoLoginWithKeycard(accountData, password, chatKey);

NSLog(@"%@", result);
Expand Down
3 changes: 2 additions & 1 deletion src/status_im/hardwallet/common.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,8 @@
:hardwallet/get-application-info {:pairing (get-pairing db key-uid)}
:hardwallet/login-with-keycard {:multiaccount-data multiaccount-data
:password encryption-public-key
:chat-key whisper-private-key}}
:chat-key whisper-private-key
:key-uid key-uid}}
(when save-keys?
(keychain/save-hardwallet-keys key-uid encryption-public-key whisper-private-key))
(clear-on-card-connected)
Expand Down
3 changes: 2 additions & 1 deletion src/status_im/hardwallet/real_keycard.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,9 @@
(catch on-failure))))

(defn save-multiaccount-and-login
[{:keys [multiaccount-data password settings node-config accounts-data chat-key]}]
[{:keys [key-uid multiaccount-data password settings node-config accounts-data chat-key]}]
(status/save-multiaccount-and-login-with-keycard
key-uid
(types/clj->json multiaccount-data)
password
(types/clj->json settings)
Expand Down
14 changes: 8 additions & 6 deletions src/status_im/hardwallet/simulated_keycard.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
(log/debug "[simulated kk] generate-and-load-key response" response)
(status/multiaccount-store-derived
id
key-uid
[constants/path-wallet-root
constants/path-eip1581
constants/path-whisper
Expand Down Expand Up @@ -197,9 +198,8 @@
path))

(defn export-key [{:keys [pin on-success on-failure]}]
(let [wallet-root-address (get-in
@re-frame.db/app-db
[:multiaccount :wallet-root-address])
(let [{:keys [key-uid wallet-root-address]}
(get @re-frame.db/app-db :multiaccount)
accounts (get @re-frame.db/app-db :multiaccount/accounts)
hashed-password (ethereum/sha3 pin)
path-num (inc (get-in @re-frame.db/app-db [:multiaccount :latest-derived-path]))
Expand All @@ -220,6 +220,7 @@
(re-frame/dispatch [::new-account-error :account-error (i18n/label :t/account-exists-title)])
(status/multiaccount-store-derived
id
key-uid
[path]
hashed-password
(fn [result]
Expand Down Expand Up @@ -271,16 +272,17 @@
(log/warn "sign-typed-data not implemented" args))

(defn save-multiaccount-and-login
[{:keys [multiaccount-data password settings node-config accounts-data]}]
[{:keys [key-uid multiaccount-data password settings node-config accounts-data]}]
(status/save-account-and-login
key-uid
(types/clj->json multiaccount-data)
password
(types/clj->json settings)
node-config
(types/clj->json accounts-data)))

(defn login [{:keys [multiaccount-data password]}]
(status/login multiaccount-data password))
(defn login [{:keys [key-uid multiaccount-data password]}]
(status/login key-uid multiaccount-data password))

(defn send-transaction-with-signature
[{:keys [transaction on-completed]}]
Expand Down
6 changes: 0 additions & 6 deletions src/status_im/init/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
(fx/merge cofx
{:get-supported-biometric-auth nil
::init-theme nil
::init-keystore nil
::open-multiaccounts #(re-frame/dispatch [::initialize-multiaccounts % {:logout? false}])
:ui/listen-to-window-dimensions-change nil
::network/listen-to-network-info nil
Expand All @@ -72,11 +71,6 @@
(fn [callback]
(status/open-accounts callback)))

(re-frame/reg-fx
::init-keystore
(fn []
(status/init-keystore)))

(re-frame/reg-fx
::init-theme
(fn []
Expand Down
Loading

0 comments on commit c5837b6

Please sign in to comment.