fix(@desktop/keycard): keycard may be factory reseted during unlock flow in some scenarios (shared module part)

- Unexpected wiping out the data during the unlock flow handled (shared module part)

Fixes: #9183
This commit is contained in:
Sale Djenic 2023-01-23 18:07:56 +01:00 committed by saledjenic
parent b44fc7a842
commit 52597472ed
12 changed files with 119 additions and 83 deletions

View File

@ -515,6 +515,7 @@ proc buildAndRegisterUserProfile(self: AppController) =
let oldUid = self.changedKeycardUids[0].oldKcUid
let newUid = self.changedKeycardUids[^1].newKcUid
discard self.walletAccountService.updateKeycardUid(oldUid, newUid)
discard self.walletAccountService.setKeycardUnlocked(loggedInAccount.keyUid, newUid)
proc storeDefaultKeyPairForNewKeycardUser*(self: AppController) =
self.storeDefaultKeyPair = true

View File

@ -97,6 +97,10 @@ method getKeycardSharedModule*(self: Module): QVariant =
return self.keycardSharedModule.getModuleAsVariant()
proc createSharedKeycardModule(self: Module) =
if self.isSharedKeycardModuleFlowRunning():
info "keycard shared module is still running"
self.view.emitSharedModuleBusy()
return
self.keycardSharedModule = keycard_shared_module.newModule[Module](self, UNIQUE_SETTING_KEYCARD_MODULE_IDENTIFIER,
self.events, self.keycardService, self.settingsService, self.privacyService, self.accountsService,
self.walletAccountService, self.keychainService)

View File

@ -44,6 +44,10 @@ QtObject:
QtProperty[QVariant] keycardSharedModule:
read = getKeycardSharedModule
proc sharedModuleBusy*(self: View) {.signal.}
proc emitSharedModuleBusy*(self: View) =
self.sharedModuleBusy()
proc displayKeycardSharedModuleFlow*(self: View) {.signal.}
proc emitDisplayKeycardSharedModuleFlow*(self: View) =
self.displayKeycardSharedModuleFlow()

View File

@ -64,6 +64,7 @@ type
tmpKeycardCopyDestinationKeycardUid: string
tmpKeycardSyncingInProgress: bool
tmpFlowData: SharedKeycarModuleFlowTerminatedArgs
tmpUnlockUsingSeedPhrase: bool # true - sp, false - puk
proc newController*(delegate: io_interface.AccessInterface,
uniqueIdentifier: string,
@ -281,6 +282,12 @@ proc keycardSyncingInProgress*(self: Controller): bool =
proc setKeycardSyncingInProgress*(self: Controller, value: bool) =
self.tmpKeycardSyncingInProgress = value
proc unlockUsingSeedPhrase*(self: Controller): bool =
return self.tmpUnlockUsingSeedPhrase
proc setUnlockUsingSeedPhrase*(self: Controller, value: bool) =
self.tmpUnlockUsingSeedPhrase = value
proc setSelectedKeyPair*(self: Controller, isProfile: bool, paths: seq[string], keyPairDto: KeyPairDto) =
if paths.len != keyPairDto.accountsAddresses.len:
error "selected keypair has different number of paths and addresses"

View File

@ -10,6 +10,7 @@ proc delete*(self: EnterPukState) =
method executePrePrimaryStateCommand*(self: EnterPukState, controller: Controller) =
if self.flowType == FlowType.UnlockKeycard:
controller.setUnlockUsingSeedPhrase(false)
if controller.getPuk().len == PUKLengthForStatusApp:
controller.enterKeycardPuk(controller.getPuk())

View File

@ -15,18 +15,19 @@ proc delete*(self: EnterSeedPhraseState) =
self.State.delete
method executePrePrimaryStateCommand*(self: EnterSeedPhraseState, controller: Controller) =
let sp = controller.getSeedPhrase()
if self.flowType == FlowType.SetupNewKeycard:
self.verifiedSeedPhrase = controller.validSeedPhrase(controller.getSeedPhrase()) and
controller.getKeyUidForSeedPhrase(controller.getSeedPhrase()) == controller.getSelectedKeyPairDto().keyUid
let keyUid = controller.getKeyUidForSeedPhrase(sp)
self.verifiedSeedPhrase = controller.validSeedPhrase(sp) and keyUid == controller.getSelectedKeyPairDto().keyUid
if self.verifiedSeedPhrase:
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), sp)
else:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
if self.flowType == FlowType.SetupNewKeycardOldSeedPhrase:
self.verifiedSeedPhrase = controller.validSeedPhrase(controller.getSeedPhrase())
self.verifiedSeedPhrase = controller.validSeedPhrase(sp)
if self.verifiedSeedPhrase:
## should always be true, since it's not possible to do primary command otherwise (button is disabled on the UI)
let keyUid = controller.getKeyUidForSeedPhrase(controller.getSeedPhrase())
let keyUid = controller.getKeyUidForSeedPhrase(sp)
self.keyPairAlreadyMigrated = controller.getMigratedKeyPairByKeyUid(keyUid).len > 0
if self.keyPairAlreadyMigrated:
controller.prepareKeyPairForProcessing(keyUid)
@ -35,27 +36,25 @@ method executePrePrimaryStateCommand*(self: EnterSeedPhraseState, controller: Co
if self.keyPairAlreadyAdded:
controller.prepareKeyPairForProcessing(keyUid)
return
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), sp)
if self.flowType == FlowType.CreateCopyOfAKeycard:
self.verifiedSeedPhrase = controller.validSeedPhrase(controller.getSeedPhrase()) and
controller.getKeyUidForSeedPhrase(controller.getSeedPhrase()) == controller.getKeyPairForProcessing().getKeyUid()
let keyUid = controller.getKeyUidForSeedPhrase(sp)
self.verifiedSeedPhrase = controller.validSeedPhrase(sp) and keyUid == controller.getKeyPairForProcessing().getKeyUid()
if self.verifiedSeedPhrase:
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), sp)
else:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
if self.flowType == FlowType.UnlockKeycard:
self.verifiedSeedPhrase = controller.validSeedPhrase(controller.getSeedPhrase()) and
controller.getKeyUidForSeedPhrase(controller.getSeedPhrase()) == controller.getKeyPairForProcessing().getKeyUid()
if self.verifiedSeedPhrase:
controller.runGetMetadataFlow()
else:
controller.setUnlockUsingSeedPhrase(true)
let keyUid = controller.getKeyUidForSeedPhrase(sp)
self.verifiedSeedPhrase = controller.validSeedPhrase(sp) and keyUid == controller.getKeyPairForProcessing().getKeyUid()
if not self.verifiedSeedPhrase:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
method getNextPrimaryState*(self: EnterSeedPhraseState, controller: Controller): State =
if self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.UnlockKeycard:
if not self.verifiedSeedPhrase:
return createState(StateType.WrongSeedPhrase, self.flowType, nil)
if self.flowType == FlowType.SetupNewKeycard:
if not self.verifiedSeedPhrase:
return createState(StateType.WrongSeedPhrase, self.flowType, nil)
if self.flowType == FlowType.CreateCopyOfAKeycard:
if not self.verifiedSeedPhrase:
return createState(StateType.WrongSeedPhrase, self.flowType, self.getBackState)
@ -67,6 +66,10 @@ method getNextPrimaryState*(self: EnterSeedPhraseState, controller: Controller):
## Maybe we should differ among these 2 states (keyPairAlreadyMigrated or keyPairAlreadyAdded)
## but we need to check that with designers.
return createState(StateType.SeedPhraseAlreadyInUse, self.flowType, self)
if self.flowType == FlowType.UnlockKeycard:
if self.verifiedSeedPhrase:
return createState(StateType.CreatePin, self.flowType, nil)
return createState(StateType.WrongSeedPhrase, self.flowType, nil)
method executeCancelCommand*(self: EnterSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard or
@ -89,19 +92,6 @@ method resolveKeycardNextState*(self: EnterSeedPhraseState, keycardFlowType: str
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorRequireInit:
return createState(StateType.CreatePin, self.flowType, nil)
if self.flowType == FlowType.UnlockKeycard:
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.GetMetadata:
controller.setMetadataFromKeycard(keycardEvent.cardMetadata)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
if keycardEvent.error.len == 0:
controller.setKeycardUid(keycardEvent.instanceUID)
controller.runLoadAccountFlow(seedPhraseLength = controller.getSeedPhraseLength(), seedPhrase = controller.getSeedPhrase(),
pin = "", puk = "", factoryReset = true)
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount:
if keycardFlowType == ResponseTypeValueEnterNewPIN and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorRequireInit:
return createState(StateType.CreatePin, self.flowType, nil)
if self.flowType == FlowType.CreateCopyOfAKeycard:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.keyUid.len > 0:

View File

@ -29,6 +29,7 @@ method getNextPrimaryState*(self: MaxPinRetriesReachedState, controller: Control
return
return createState(StateType.FactoryResetConfirmation, self.flowType, self)
if self.flowType == FlowType.Authentication:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.DisableSeedPhraseForUnlock, add = true))
controller.runSharedModuleFlow(FlowType.UnlockKeycard)
method executeCancelCommand*(self: MaxPinRetriesReachedState, controller: Controller) =

View File

@ -20,6 +20,9 @@ method executePreSecondaryStateCommand*(self: RepeatPinState, controller: Contro
self.flowType == FlowType.SetupNewKeycardOldSeedPhrase:
controller.storePinToKeycard(controller.getPin(), controller.generateRandomPUK())
if self.flowType == FlowType.UnlockKeycard:
if controller.unlockUsingSeedPhrase():
controller.runGetMetadataFlow()
return
controller.storePinToKeycard(controller.getPin(), "")
method getNextSecondaryState*(self: RepeatPinState, controller: Controller): State =
@ -64,30 +67,39 @@ method resolveKeycardNextState*(self: RepeatPinState, keycardFlowType: string, k
controller.setKeyPairForProcessing(item)
return createState(StateType.PinSet, self.flowType, nil)
if self.flowType == FlowType.UnlockKeycard:
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.GetMetadata:
if keycardFlowType == ResponseTypeValueEnterPUK and
keycardEvent.error.len > 0 and
keycardEvent.error == RequestParamPUK:
controller.setRemainingAttempts(keycardEvent.pukRetries)
controller.setPukValid(false)
if keycardEvent.pukRetries > 0:
return createState(StateType.PinSet, self.flowType, nil)
return createState(StateType.MaxPukRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setPukValid(true)
controller.updateKeycardUid(keycardEvent.keyUid, keycardEvent.instanceUID)
return createState(StateType.PinSet, self.flowType, nil)
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount:
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
if controller.getKeyPairForProcessing().getKeyUid() != keycardEvent.keyUid:
error "load account keyUid and keyUid being unlocked do not match"
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
return
let md = controller.getMetadataFromKeycard()
let paths = md.walletAccounts.map(a => a.path)
controller.runStoreMetadataFlow(cardName = md.name, pin = controller.getPin(), walletPaths = paths)
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.StoreMetadata:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.instanceUID.len > 0:
if not controller.unlockUsingSeedPhrase():
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.GetMetadata:
if keycardFlowType == ResponseTypeValueEnterPUK and
keycardEvent.error.len > 0 and
keycardEvent.error == RequestParamPUK:
controller.setRemainingAttempts(keycardEvent.pukRetries)
controller.setPukValid(false)
if keycardEvent.pukRetries > 0:
return createState(StateType.PinSet, self.flowType, nil)
return createState(StateType.MaxPukRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setPukValid(true)
controller.updateKeycardUid(keycardEvent.keyUid, keycardEvent.instanceUID)
return createState(StateType.PinSet, self.flowType, nil)
return createState(StateType.PinSet, self.flowType, nil)
else:
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.GetMetadata:
controller.setMetadataFromKeycard(keycardEvent.cardMetadata)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
if keycardEvent.error.len == 0:
controller.runLoadAccountFlow(controller.getSeedPhraseLength(), controller.getSeedPhrase(), controller.getPin(), puk = "",
factoryReset = true)
return
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount:
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
if controller.getKeyPairForProcessing().getKeyUid() != keycardEvent.keyUid:
error "load account keyUid and keyUid being unlocked do not match"
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
return
controller.updateKeycardUid(keycardEvent.keyUid, keycardEvent.instanceUID)
let md = controller.getMetadataFromKeycard()
let paths = md.walletAccounts.map(a => a.path)
controller.runStoreMetadataFlow(cardName = md.name, pin = controller.getPin(), walletPaths = paths)
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.StoreMetadata:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.instanceUID.len > 0:
return createState(StateType.PinSet, self.flowType, nil)

View File

@ -13,32 +13,37 @@ proc delete*(self: WrongSeedPhraseState) =
self.State.delete
method executePrePrimaryStateCommand*(self: WrongSeedPhraseState, controller: Controller) =
let sp = controller.getSeedPhrase()
if self.flowType == FlowType.SetupNewKeycard:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
self.verifiedSeedPhrase = controller.validSeedPhrase(controller.getSeedPhrase()) and
controller.getKeyUidForSeedPhrase(controller.getSeedPhrase()) == controller.getSelectedKeyPairDto().keyUid
let keyUid = controller.getKeyUidForSeedPhrase(sp)
self.verifiedSeedPhrase = controller.validSeedPhrase(sp) and keyUid == controller.getSelectedKeyPairDto().keyUid
if self.verifiedSeedPhrase:
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), sp)
if self.flowType == FlowType.CreateCopyOfAKeycard:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
self.verifiedSeedPhrase = controller.validSeedPhrase(controller.getSeedPhrase()) and
controller.getKeyUidForSeedPhrase(controller.getSeedPhrase()) == controller.getKeyPairForProcessing().getKeyUid()
let keyUid = controller.getKeyUidForSeedPhrase(sp)
self.verifiedSeedPhrase = controller.validSeedPhrase(sp) and keyUid == controller.getKeyPairForProcessing().getKeyUid()
if self.verifiedSeedPhrase:
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), sp)
if self.flowType == FlowType.UnlockKeycard:
controller.setUnlockUsingSeedPhrase(true)
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
self.verifiedSeedPhrase = controller.validSeedPhrase(controller.getSeedPhrase()) and
controller.getKeyUidForSeedPhrase(controller.getSeedPhrase()) == controller.getKeyPairForProcessing().getKeyUid()
if self.verifiedSeedPhrase:
controller.runGetMetadataFlow()
let keyUid = controller.getKeyUidForSeedPhrase(sp)
self.verifiedSeedPhrase = controller.validSeedPhrase(sp) and keyUid == controller.getKeyPairForProcessing().getKeyUid()
if not self.verifiedSeedPhrase:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
method getNextPrimaryState*(self: WrongSeedPhraseState, controller: Controller): State =
if self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.CreateCopyOfAKeycard or
self.flowType == FlowType.UnlockKeycard:
self.flowType == FlowType.CreateCopyOfAKeycard:
if not self.verifiedSeedPhrase:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
return self
if self.flowType == FlowType.UnlockKeycard:
if self.verifiedSeedPhrase:
return createState(StateType.CreatePin, self.flowType, nil)
return self
method executeCancelCommand*(self: WrongSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard or
@ -56,19 +61,6 @@ method resolveKeycardNextState*(self: WrongSeedPhraseState, keycardFlowType: str
keycardEvent.keyUid.len > 0:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
return createState(StateType.MigratingKeyPair, self.flowType, nil)
if self.flowType == FlowType.UnlockKeycard:
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.GetMetadata:
controller.setMetadataFromKeycard(keycardEvent.cardMetadata)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
if keycardEvent.error.len == 0:
controller.setKeycardUid(keycardEvent.instanceUID)
controller.runLoadAccountFlow(seedPhraseLength = controller.getSeedPhraseLength(), seedPhrase = controller.getSeedPhrase(),
pin = "", puk = "", factoryReset = true)
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount:
if keycardFlowType == ResponseTypeValueEnterNewPIN and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorRequireInit:
return createState(StateType.CreatePin, self.flowType, nil)
if self.flowType == FlowType.CreateCopyOfAKeycard:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.keyUid.len > 0:

View File

@ -76,6 +76,7 @@ type FlowType* {.pure.} = enum
const FlowsWeShouldNotTryAKeycardSyncFor* = @[
FlowType.General,
FlowType.FactoryReset,
FlowType.UnlockKeycard,
FlowType.SetupNewKeycard,
FlowType.SetupNewKeycardNewSeedPhrase,
FlowType.SetupNewKeycardOldSeedPhrase,

View File

@ -138,7 +138,7 @@ method checkRepeatedKeycardPukWhileTyping*[T](self: Module[T], puk: string): boo
method getCurrentFlowType*[T](self: Module[T]): FlowType =
let currStateObj = self.view.currentStateObj()
if currStateObj.isNil:
error "sm_cannot resolve current state in order to determine mnemonic"
error "sm_cannot resolve current state"
return FlowType.General
return currStateObj.flowType()

View File

@ -1,8 +1,11 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import QtQml.Models 2.14
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups.Dialog 0.1
import utils 1.0
import shared.popups.keycard 1.0
@ -46,6 +49,23 @@ SettingsContentBase {
property string observedKeyUid: ""
}
Component {
id: sharedModuleBusyPopupComponent
StatusDialog {
id: titleContentDialog
title: qsTr("Status Keycard")
StatusBaseText {
anchors.fill: parent
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.directColor1
text: qsTr("The Keycard module is still busy, please try again")
}
standardButtons: Dialog.Ok
}
}
MainView {
Layout.preferredWidth: root.contentWidth
keycardStore: root.keycardStore
@ -74,6 +94,9 @@ SettingsContentBase {
onDestroyKeycardSharedModuleFlow: {
keycardPopup.active = false
}
onSharedModuleBusy: {
Global.openPopup(sharedModuleBusyPopupComponent)
}
}
Loader {