From 02d3398fbcae03f7ba67216153a20f00f391350e Mon Sep 17 00:00:00 2001 From: Sale Djenic Date: Fri, 28 Oct 2022 13:42:09 +0200 Subject: [PATCH] fix(@desktop/keycard): change db password for a keycard users DB password for a Keycard user is now `publicKey` of encryption derivation. kdf iterations for keycard users are set to 256000 so it's the same as we have for regular users. Fixes: #8066 --- .../keycard_popup/controller.nim | 5 +- .../key_pair_migrate_failure_state.nim | 4 + .../shared_modules/keycard_popup/module.nim | 2 +- src/app/modules/startup/controller.nim | 36 ++--- src/app_service/common/account_constants.nim | 4 +- .../accounts/dto/generated_accounts.nim | 7 + src/app_service/service/accounts/service.nim | 126 +++++++++++------- src/backend/accounts.nim | 7 +- vendor/nim-status-go | 2 +- vendor/status-go | 2 +- 10 files changed, 117 insertions(+), 78 deletions(-) diff --git a/src/app/modules/shared_modules/keycard_popup/controller.nim b/src/app/modules/shared_modules/keycard_popup/controller.nim index 29ee13cc89..f0b02c9093 100644 --- a/src/app/modules/shared_modules/keycard_popup/controller.nim +++ b/src/app/modules/shared_modules/keycard_popup/controller.nim @@ -291,8 +291,11 @@ proc verifyPassword*(self: Controller, password: string): bool = proc convertSelectedKeyPairToKeycardAccount*(self: Controller, password: string): bool = if not serviceApplicable(self.accountsService): return + let acc = self.accountsService.createAccountFromMnemonic(self.getSeedPhrase(), includeEncryption = true) singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NOT_NOW) - return self.accountsService.convertToKeycardAccount(self.tmpSelectedKeyPairDto.keyUid, password) + return self.accountsService.convertToKeycardAccount(self.tmpSelectedKeyPairDto.keyUid, + currentPassword = password, + newPassword = acc.derivedAccounts.encryption.publicKey) proc getLoggedInAccount*(self: Controller): AccountDto = if not serviceApplicable(self.accountsService): diff --git a/src/app/modules/shared_modules/keycard_popup/internal/key_pair_migrate_failure_state.nim b/src/app/modules/shared_modules/keycard_popup/internal/key_pair_migrate_failure_state.nim index 7f374899a4..bd186aa2b8 100644 --- a/src/app/modules/shared_modules/keycard_popup/internal/key_pair_migrate_failure_state.nim +++ b/src/app/modules/shared_modules/keycard_popup/internal/key_pair_migrate_failure_state.nim @@ -8,6 +8,10 @@ proc newKeyPairMigrateFailureState*(flowType: FlowType, backState: State): KeyPa proc delete*(self: KeyPairMigrateFailureState) = self.State.delete +method executePrimaryCommand*(self: KeyPairMigrateFailureState, controller: Controller) = + if self.flowType == FlowType.SetupNewKeycard: + controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true) + method executeTertiaryCommand*(self: KeyPairMigrateFailureState, controller: Controller) = if self.flowType == FlowType.SetupNewKeycard: controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true) \ No newline at end of file diff --git a/src/app/modules/shared_modules/keycard_popup/module.nim b/src/app/modules/shared_modules/keycard_popup/module.nim index ba58e7ff69..f3895b6324 100644 --- a/src/app/modules/shared_modules/keycard_popup/module.nim +++ b/src/app/modules/shared_modules/keycard_popup/module.nim @@ -272,7 +272,7 @@ proc buildKeyPairsList[T](self: Module[T], excludeAlreadyMigratedPairs: bool): s locked = false, name = singletonInstance.userProfile.getName(), image = singletonInstance.userProfile.getIcon(), - icon = "wallet", + icon = "", pairType = KeyPairType.Profile, derivedFrom = a.derivedfrom) for ga in accounts: diff --git a/src/app/modules/startup/controller.nim b/src/app/modules/startup/controller.nim index c7b1ef7b5f..b09def2265 100644 --- a/src/app/modules/startup/controller.nim +++ b/src/app/modules/startup/controller.nim @@ -294,36 +294,40 @@ proc importMnemonic*(self: Controller): bool = self.delegate.importAccountError(error) return false -proc setupAccount(self: Controller, accountId: string, storeToKeychain: bool, keycardUsage: bool) = +proc setupKeychain(self: Controller, store: bool) = + if store: + singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_STORE) + self.storePasswordToKeychain() + else: + singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NEVER) + +proc setupAccount(self: Controller, accountId: string, storeToKeychain: bool) = self.delegate.moveToLoadingAppState() - let error = self.accountsService.setupAccount(accountId, self.tmpPassword, self.tmpDisplayName, keycardUsage) + let error = self.accountsService.setupAccount(accountId, self.tmpPassword, self.tmpDisplayName) if error != "": self.delegate.setupAccountError(error) else: - if storeToKeychain: - singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_STORE) - self.storePasswordToKeychain() - else: - singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NEVER) - + self.setupKeychain(storeToKeychain) + proc storeGeneratedAccountAndLogin*(self: Controller, storeToKeychain: bool) = let accounts = self.getGeneratedAccounts() if accounts.len == 0: error "list of generated accounts is empty" return let accountId = accounts[0].id - self.setupAccount(accountId, storeToKeychain, keycardUsage = false) + self.setupAccount(accountId, storeToKeychain) proc storeImportedAccountAndLogin*(self: Controller, storeToKeychain: bool) = let accountId = self.getImportedAccount().id - self.setupAccount(accountId, storeToKeychain, keycardUsage = false) + self.setupAccount(accountId, storeToKeychain) proc storeKeycardAccountAndLogin*(self: Controller, storeToKeychain: bool) = if self.importMnemonic(): - let accountId = self.getImportedAccount().id + self.delegate.moveToLoadingAppState() self.delegate.storeKeyPairForNewKeycardUser() self.storeMetadataForNewKeycardUser() - self.setupAccount(accountId, storeToKeychain, keycardUsage = true) + self.accountsService.setupAccountKeycard(KeycardEvent(), useImportedAcc = true) + self.setupKeychain(storeToKeychain) else: error "an error ocurred while importing mnemonic" @@ -334,12 +338,8 @@ proc setupKeycardAccount*(self: Controller, storeToKeychain: bool) = else: self.delegate.moveToLoadingAppState() self.delegate.storeKeyPairForNewKeycardUser() - self.accountsService.setupAccountKeycard(self.tmpKeycardEvent) - if storeToKeychain: - singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_STORE) - self.storePasswordToKeychain() - else: - singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NEVER) + self.accountsService.setupAccountKeycard(self.tmpKeycardEvent, useImportedAcc = false) + self.setupKeychain(storeToKeychain) proc getOpenedAccounts*(self: Controller): seq[AccountDto] = return self.accountsService.openedAccounts() diff --git a/src/app_service/common/account_constants.nim b/src/app_service/common/account_constants.nim index 29d5b89116..852c82c186 100644 --- a/src/app_service/common/account_constants.nim +++ b/src/app_service/common/account_constants.nim @@ -12,5 +12,5 @@ const PATH_EIP_1581* = "m/43'/60'/1581'" const PATH_DEFAULT_WALLET* = PATH_WALLET_ROOT & "/0" # EIP1581 Chat Key 0, the default whisper key const PATH_WHISPER* = PATH_EIP_1581 & "/0'/0" - - +# EIP1581 Encryption Key +const PATH_ENCRYPTION* = PATH_EIP_1581 & "/1'/0" diff --git a/src/app_service/service/accounts/dto/generated_accounts.nim b/src/app_service/service/accounts/dto/generated_accounts.nim index 298a0de2ce..53ad3a93f9 100644 --- a/src/app_service/service/accounts/dto/generated_accounts.nim +++ b/src/app_service/service/accounts/dto/generated_accounts.nim @@ -7,6 +7,7 @@ import ../../../common/account_constants include ../../../common/[json_utils] type DerivedAccountDetails* = object + privateKey*: string publicKey*: string address*: string derivationPath*: string @@ -16,9 +17,11 @@ type DerivedAccounts* = object walletRoot*: DerivedAccountDetails defaultWallet*: DerivedAccountDetails eip1581*: DerivedAccountDetails + encryption*: DerivedAccountDetails type GeneratedAccountDto* = object id*: string + privateKey*: string publicKey*: string address*: string keyUid*: string @@ -37,6 +40,7 @@ proc toDerivedAccountDetails(jsonObj: JsonNode, derivationPath: string): # handle it a bit different. result = DerivedAccountDetails() result.derivationPath = derivationPath + discard jsonObj.getProp("privateKey", result.privateKey) discard jsonObj.getProp("publicKey", result.publicKey) discard jsonObj.getProp("address", result.address) @@ -51,10 +55,13 @@ proc toDerivedAccounts*(jsonObj: JsonNode): DerivedAccounts = result.defaultWallet = toDerivedAccountDetails(derivedObj, derivationPath) elif(derivationPath == PATH_EIP_1581): result.eip1581 = toDerivedAccountDetails(derivedObj, derivationPath) + elif(derivationPath == PATH_ENCRYPTION): + result.encryption = toDerivedAccountDetails(derivedObj, derivationPath) proc toGeneratedAccountDto*(jsonObj: JsonNode): GeneratedAccountDto = result = GeneratedAccountDto() discard jsonObj.getProp("id", result.id) + discard jsonObj.getProp("privateKey", result.privateKey) discard jsonObj.getProp("publicKey", result.publicKey) discard jsonObj.getProp("address", result.address) discard jsonObj.getProp("keyUid", result.keyUid) diff --git a/src/app_service/service/accounts/service.nim b/src/app_service/service/accounts/service.nim index 751eb9b30f..ca934ac966 100644 --- a/src/app_service/service/accounts/service.nim +++ b/src/app_service/service/accounts/service.nim @@ -21,7 +21,7 @@ export dto_generated_accounts logScope: topics = "accounts-service" -const PATHS = @[PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET] +const PATHS = @[PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET, PATH_ENCRYPTION] const ACCOUNT_ALREADY_EXISTS_ERROR = "account already exists" const output_csv {.booldefine.} = false const KDF_ITERATIONS* {.intdefine.} = 256_000 @@ -336,18 +336,10 @@ proc addKeycardDetails(self: Service, settingsJson: var JsonNode, accountData: v if not accountData.isNil: accountData["keycard-pairing"] = kcDataObj{"key"} -proc setupAccount*(self: Service, accountId, password, displayName: string, keycardUsage: bool): string = +proc setupAccount*(self: Service, accountId, password, displayName: string): string = try: let installationId = $genUUID() var accountDataJson = self.getAccountDataForAccountId(accountId, displayName) - - var usedPassword = password - if password.len == 0: - # this means we're setting up an account using keycard - usedPassword = accountDataJson{"key-uid"}.getStr - - self.setKeyStoreDir(accountDataJson{"key-uid"}.getStr) - let subaccountDataJson = self.getSubaccountDataForAccountId(accountId, displayName) var settingsJson = self.getAccountSettings(accountId, installationId, displayName) let nodeConfigJson = self.getDefaultNodeConfig(installationId) @@ -358,15 +350,16 @@ proc setupAccount*(self: Service, accountId, password, displayName: string, keyc error "error: ", procName="setupAccount", errDesription = description return description - let hashedPassword = hashString(usedPassword) + self.setKeyStoreDir(accountDataJson{"key-uid"}.getStr) + let hashedPassword = hashString(password) discard self.storeAccount(accountId, hashedPassword) discard self.storeDerivedAccounts(accountId, hashedPassword, PATHS) - - if keycardUsage: - self.addKeycardDetails(settingsJson, accountDataJson) + self.loggedInAccount = self.saveAccountAndLogin(hashedPassword, + accountDataJson, + subaccountDataJson, + settingsJson, + nodeConfigJson) - self.loggedInAccount = self.saveAccountAndLogin(hashedPassword, accountDataJson, - subaccountDataJson, settingsJson, nodeConfigJson) self.setLocalAccountSettingsFile() if self.getLoggedInAccount.isValid(): @@ -377,16 +370,40 @@ proc setupAccount*(self: Service, accountId, password, displayName: string, keyc error "error: ", procName="setupAccount", errName = e.name, errDesription = e.msg return e.msg -proc setupAccountKeycard*(self: Service, keycardData: KeycardEvent) = +proc setupAccountKeycard*(self: Service, keycardData: KeycardEvent, useImportedAcc: bool) = try: - let installationId = $genUUID() + var keyUid = keycardData.keyUid + var address = keycardData.masterKey.address + var whisperPrivateKey = keycardData.whisperKey.privateKey + var whisperPublicKey = keycardData.whisperKey.publicKey + var whisperAddress = keycardData.whisperKey.address + var walletPublicKey = keycardData.walletKey.publicKey + var walletAddress = keycardData.walletKey.address + var walletRootAddress = keycardData.walletRootKey.address + var eip1581Address = keycardData.eip1581Key.address + var encryptionPublicKey = keycardData.encryptionKey.publicKey + if useImportedAcc: + keyUid = self.importedAccount.keyUid + address = self.importedAccount.address + whisperPublicKey = self.importedAccount.derivedAccounts.whisper.publicKey + whisperAddress = self.importedAccount.derivedAccounts.whisper.address + walletPublicKey = self.importedAccount.derivedAccounts.defaultWallet.publicKey + walletAddress = self.importedAccount.derivedAccounts.defaultWallet.address + walletRootAddress = self.importedAccount.derivedAccounts.walletRoot.address + eip1581Address = self.importedAccount.derivedAccounts.eip1581.address + encryptionPublicKey = self.importedAccount.derivedAccounts.encryption.publicKey - let alias = generateAliasFromPk(keycardData.whisperKey.publicKey) + whisperPrivateKey = self.importedAccount.derivedAccounts.whisper.privateKey + if whisperPrivateKey.startsWith("0x"): + whisperPrivateKey = whisperPrivateKey[2 .. ^1] + + let installationId = $genUUID() + let alias = generateAliasFromPk(whisperPublicKey) let openedAccounts = self.openedAccounts() var displayName: string for acc in openedAccounts: - if acc.keyUid == keycardData.keyUid: + if acc.keyUid == keyUid: displayName = acc.name break if displayName.len == 0: @@ -395,26 +412,27 @@ proc setupAccountKeycard*(self: Service, keycardData: KeycardEvent) = var accountDataJson = %* { "name": alias, "display-name": displayName, - "address": keycardData.masterKey.address, - "key-uid": keycardData.keyUid + "address": address, + "key-uid": keyUid, + "kdfIterations": KDF_ITERATIONS, } - self.setKeyStoreDir(keycardData.keyUid) + self.setKeyStoreDir(keyUid) let nodeConfigJson = self.getDefaultNodeConfig(installationId) let subaccountDataJson = %* [ { - "public-key": keycardData.walletKey.publicKey, - "address": keycardData.walletKey.address, + "public-key": walletPublicKey, + "address": walletAddress, "color": "#4360df", "wallet": true, "path": PATH_DEFAULT_WALLET, "name": "Status account", - "derived-from": keycardData.masterKey.address, + "derived-from": address, "emoji": self.defaultWalletEmoji, }, { - "public-key": keycardData.whisperKey.publicKey, - "address": keycardData.whisperKey.address, + "public-key": whisperPublicKey, + "address": whisperAddress, "name": alias, "path": PATH_WHISPER, "chat": true, @@ -423,14 +441,14 @@ proc setupAccountKeycard*(self: Service, keycardData: KeycardEvent) = ] var settingsJson = %* { - "key-uid": keycardData.keyUid, - "public-key": keycardData.whisperKey.publicKey, + "key-uid": keyUid, + "public-key": whisperPublicKey, "name": alias, "display-name": "", - "address": keycardData.whisperKey.address, - "eip1581-address": keycardData.eip1581Key.address, - "dapps-address": keycardData.walletKey.address, - "wallet-root-address": keycardData.walletRootKey.address, + "address": whisperAddress, + "eip1581-address": eip1581Address, + "dapps-address": walletAddress, + "wallet-root-address": walletRootAddress, "preview-privacy?": true, "signing-phrase": generateSigningPhrase(3), "log-level": $LogLevel.INFO, @@ -443,7 +461,7 @@ proc setupAccountKeycard*(self: Service, keycardData: KeycardEvent) = "appearance": 0, "installation-id": installationId, "current-user-status": { - "publicKey": keycardData.whisperKey.publicKey, + "publicKey": whisperPublicKey, "statusType": 1, "clock": 0, "text": "" @@ -458,10 +476,8 @@ proc setupAccountKeycard*(self: Service, keycardData: KeycardEvent) = error "error: ", procName="setupAccountKeycard", errDesription = description return - let hashedPassword = hashString(keycardData.keyUid) # using hashed keyUid as password - - self.loggedInAccount = self.saveKeycardAccountAndLogin(keycardData.whisperKey.privateKey, - hashedPassword, + self.loggedInAccount = self.saveKeycardAccountAndLogin(chatKey = whisperPrivateKey, + password = encryptionPublicKey, accountDataJson, subaccountDataJson, settingsJson, @@ -470,15 +486,27 @@ proc setupAccountKeycard*(self: Service, keycardData: KeycardEvent) = except Exception as e: error "error: ", procName="setupAccount", errName = e.name, errDesription = e.msg -proc createAccountFromMnemonic*(self: Service, mnemonic: string): GeneratedAccountDto = +proc createAccountFromMnemonic*(self: Service, mnemonic: string, includeEncryption = false, includeWhisper = false, + includeRoot = false, includeDefaultWallet = false, includeEip1581 = false): GeneratedAccountDto = if mnemonic.len == 0: error "empty mnemonic" return + var paths: seq[string] + if includeEncryption: + paths.add(PATH_ENCRYPTION) + if includeWhisper: + paths.add(PATH_WHISPER) + if includeRoot: + paths.add(PATH_WALLET_ROOT) + if includeDefaultWallet: + paths.add(PATH_DEFAULT_WALLET) + if includeEip1581: + paths.add(PATH_EIP_1581) try: - let response = status_account.createAccountFromMnemonic(mnemonic) + let response = status_account.createAccountFromMnemonicAndDeriveAccountsForPaths(mnemonic, paths) return toGeneratedAccountDto(response.result) except Exception as e: - error "error: ", procName="createAccountFromMnemonic", errName = e.name, errDesription = e.msg + error "error: ", procName="createAccountFromMnemonicAndDeriveAccountsForPaths", errName = e.name, errDesription = e.msg proc importMnemonic*(self: Service, mnemonic: string): string = if mnemonic.len == 0: @@ -609,10 +637,8 @@ proc loginAccountKeycard*(self: Service, keycardData: KeycardEvent): string = var settingsJson: JsonNode self.addKeycardDetails(settingsJson, accountDataJson) - let hashedPassword = hashString(keycardData.keyUid) # using hashed keyUid as password - let response = status_account.loginWithKeycard(keycardData.whisperKey.privateKey, - hashedPassword, + keycardData.encryptionKey.publicKey, accountDataJson) var error = "response doesn't contain \"error\"" @@ -642,7 +668,7 @@ proc verifyAccountPassword*(self: Service, account: string, password: string): b error "error: ", procName="verifyAccountPassword", errName = e.name, errDesription = e.msg -proc convertToKeycardAccount*(self: Service, keyUid: string, password: string): bool = +proc convertToKeycardAccount*(self: Service, keyUid: string, currentPassword: string, newPassword: string): bool = try: var accountDataJson = %* { "name": self.getLoggedInAccount().name, @@ -659,11 +685,9 @@ proc convertToKeycardAccount*(self: Service, keyUid: string, password: string): error "error: ", procName="convertToKeycardAccount", errDesription = description return - let hashedCurrentPassword = hashString(password) - let hashedNewPassword = hashString(keyUid) - - let response = status_account.convertToKeycardAccount(self.keyStoreDir, accountDataJson, settingsJson, hashedCurrentPassword, - hashedNewPassword) + let hashedCurrentPassword = hashString(currentPassword) + let response = status_account.convertToKeycardAccount(self.keyStoreDir, accountDataJson, settingsJson, + hashedCurrentPassword, newPassword) if(response.result.contains("error")): let errMsg = response.result["error"].getStr diff --git a/src/backend/accounts.nim b/src/backend/accounts.nim index 1c8235c0ed..99d75af8ce 100644 --- a/src/backend/accounts.nim +++ b/src/backend/accounts.nim @@ -134,17 +134,18 @@ proc multiAccountImportMnemonic*(mnemonic: string): RpcResponse[JsonNode] {.rais error "error doing rpc request", methodName = "multiAccountImportMnemonic", exception=e.msg raise newException(RpcException, e.msg) -proc createAccountFromMnemonic*(mnemonic: string): RpcResponse[JsonNode] {.raises: [Exception].} = +proc createAccountFromMnemonicAndDeriveAccountsForPaths*(mnemonic: string, paths: seq[string]): RpcResponse[JsonNode] {.raises: [Exception].} = let payload = %* { "mnemonicPhrase": mnemonic, + "paths": paths, "Bip39Passphrase": "" } try: - let response = status_go.createAccountFromMnemonic($payload) + let response = status_go.createAccountFromMnemonicAndDeriveAccountsForPaths($payload) result.result = Json.decode(response, JsonNode) except RpcException as e: - error "error doing rpc request", methodName = "createAccountFromMnemonic", exception=e.msg + error "error doing rpc request", methodName = "createAccountFromMnemonicAndDeriveAccountsForPaths", exception=e.msg raise newException(RpcException, e.msg) proc deriveAccounts*(accountId: string, paths: seq[string]): RpcResponse[JsonNode] {.raises: [Exception].} = diff --git a/vendor/nim-status-go b/vendor/nim-status-go index 00a352da93..7be103a29b 160000 --- a/vendor/nim-status-go +++ b/vendor/nim-status-go @@ -1 +1 @@ -Subproject commit 00a352da93cef3010dd6c213b87a66ce1d9cad01 +Subproject commit 7be103a29b321e5593dda29529bad6368dccc0ba diff --git a/vendor/status-go b/vendor/status-go index 9565342101..caa20e616e 160000 --- a/vendor/status-go +++ b/vendor/status-go @@ -1 +1 @@ -Subproject commit 956534210111815202ba001bbc69a3411ebbb429 +Subproject commit caa20e616e7dc8422faa25fd95a62287ca3103a8