feat(@desktop/keycard): unlock keycard flow added

Fixes: #7031
This commit is contained in:
Sale Djenic 2022-09-20 16:01:57 +02:00 committed by saledjenic
parent cf7bc0325b
commit a3b71c7d91
49 changed files with 1009 additions and 253 deletions

View File

@ -30,8 +30,11 @@ type
keychainConnectionIds: seq[UUID]
connectionKeycardResponse: UUID
tmpKeycardContainsMetadata: bool
tmpCardMetadata: CardMetadata
tmpPin: string
tmpPinMatch: bool
tmpPuk: string
tmpValidPuk: bool
tmpPassword: string
tmpSelectedKeyPairIsProfile: bool
tmpSelectedKeyPairDto: KeyPairDto
@ -39,7 +42,9 @@ type
tmpSeedPhrase: string
tmpSeedPhraseLength: int
tmpKeyUidWhichIsBeingAuthenticating: string
tmpKeyUidWhichIsBeingUnlocking: string
tmpUsePinFromBiometrics: bool
tmpKeycardUid: string
proc newController*(delegate: io_interface.AccessInterface,
uniqueIdentifier: string,
@ -63,6 +68,7 @@ proc newController*(delegate: io_interface.AccessInterface,
result.keychainService = keychainService
result.tmpKeycardContainsMetadata = false
result.tmpPinMatch = false
result.tmpValidPuk = false
result.tmpSeedPhraseLength = 0
result.tmpSelectedKeyPairIsProfile = false
result.tmpUsePinFromBiometrics = false
@ -140,6 +146,18 @@ proc setPin*(self: Controller, value: string) =
proc getPin*(self: Controller): string =
return self.tmpPin
proc setPuk*(self: Controller, value: string) =
self.tmpPuk = value
proc getPuk*(self: Controller): string =
return self.tmpPuk
proc setPukValid*(self: Controller, value: bool) =
self.tmpValidPuk = value
proc getValidPuk*(self: Controller): bool =
return self.tmpValidPuk
proc setUsePinFromBiometrics*(self: Controller, value: bool) =
self.tmpUsePinFromBiometrics = value
@ -161,6 +179,12 @@ proc getPassword*(self: Controller): string =
proc getKeyUidWhichIsBeingAuthenticating*(self: Controller): string =
self.tmpKeyUidWhichIsBeingAuthenticating
proc getKeyUidWhichIsBeingUnlocking*(self: Controller): string =
self.tmpKeyUidWhichIsBeingUnlocking
proc setKeyUidWhichIsBeingUnlocking*(self: Controller, keyUid: string) =
self.tmpKeyUidWhichIsBeingUnlocking = keyUid
proc setSelectedKeyPairIsProfile*(self: Controller, value: bool) =
self.tmpSelectedKeyPairIsProfile = value
@ -173,9 +197,15 @@ proc setSelectedKeyPairDto*(self: Controller, keyPairDto: KeyPairDto) =
proc getSelectedKeyPairDto*(self: Controller): KeyPairDto =
return self.tmpSelectedKeyPairDto
proc setKeycardUid*(self: Controller, value: string) =
proc setKeycardUidTheSelectedKeypairIsMigratedTo*(self: Controller, value: string) =
self.tmpSelectedKeyPairDto.keycardUid = value
proc setKeycardUid*(self: Controller, value: string) =
self.tmpKeycardUid = value
proc getKeycardUid*(self: Controller): string =
return self.tmpKeycardUid
proc setSelectedKeyPairWalletPaths*(self: Controller, paths: seq[string]) =
self.tmpSelectedKeyPairWalletPaths = paths
@ -197,6 +227,10 @@ proc validSeedPhrase*(self: Controller, seedPhrase: string): bool =
let err = self.accountsService.validateMnemonic(seedPhrase)
return err.len == 0
proc getKeyUidForSeedPhrase*(self: Controller, seedPhrase: string): string =
let acc = self.accountsService.createAccountFromMnemonic(seedPhrase)
return acc.keyUid
proc seedPhraseRefersToSelectedKeyPair*(self: Controller, seedPhrase: string): bool =
let acc = self.accountsService.createAccountFromMnemonic(seedPhrase)
return acc.keyUid == self.tmpSelectedKeyPairDto.keyUid
@ -217,8 +251,16 @@ proc getCurrentKeycardServiceFlow*(self: Controller): keycard_service.KCSFlowTyp
proc getLastReceivedKeycardData*(self: Controller): tuple[flowType: string, flowEvent: KeycardEvent] =
return self.keycardService.getLastReceivedKeycardData()
proc setMetadataFromKeycard*(self: Controller, cardMetadata: CardMetadata) =
self.delegate.setKeyPairStoredOnKeycard(cardMetadata)
proc getMetadataFromKeycard*(self: Controller): CardMetadata =
return self.tmpCardMetadata
proc setMetadataFromKeycard*(self: Controller, cardMetadata: CardMetadata, updateKeyPair = false) =
self.tmpCardMetadata = cardMetadata
if updateKeyPair:
self.delegate.setKeyPairStoredOnKeycard(cardMetadata)
proc runSharedModuleFlow*(self: Controller, flowToRun: FlowType) =
self.delegate.runFlow(flowToRun)
proc cancelCurrentFlow*(self: Controller) =
self.keycardService.cancelCurrentFlow()
@ -230,17 +272,17 @@ proc runGetAppInfoFlow*(self: Controller, factoryReset = false) =
self.cancelCurrentFlow()
self.keycardService.startGetAppInfoFlow(factoryReset)
proc runGetMetadataFlow*(self: Controller) =
proc runGetMetadataFlow*(self: Controller, resolveAddress = false) =
self.cancelCurrentFlow()
self.keycardService.startGetMetadataFlow()
self.keycardService.startGetMetadataFlow(resolveAddress)
proc runStoreMetadataFlow*(self: Controller, cardName: string, pin: string, walletPaths: seq[string]) =
self.cancelCurrentFlow()
self.keycardService.startStoreMetadataFlow(cardName, pin, walletPaths)
proc runLoadAccountFlow*(self: Controller, factoryReset = false) =
proc runLoadAccountFlow*(self: Controller, seedPhraseLength = 0, seedPhrase = "", puk = "", factoryReset = false) =
self.cancelCurrentFlow()
self.keycardService.startLoadAccountFlow(factoryReset)
self.keycardService.startLoadAccountFlow(seedPhraseLength, seedPhrase, puk, factoryReset)
proc runSignFlow*(self: Controller, keyUid = "", bip44Path = "", txHash = "") =
## For signing a transaction we need to provide a key uid of a keypair that an account we want to sign a transaction
@ -260,6 +302,7 @@ proc readyToDisplayPopup*(self: Controller) =
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_DISPLAY_POPUP, data)
proc terminateCurrentFlow*(self: Controller, lastStepInTheCurrentFlow: bool) =
self.cancelCurrentFlow()
let (_, flowEvent) = self.getLastReceivedKeycardData()
var data = SharedKeycarModuleFlowTerminatedArgs(uniqueIdentifier: self.uniqueIdentifier,
lastStepInTheCurrentFlow: lastStepInTheCurrentFlow)
@ -301,6 +344,27 @@ proc getMigratedKeyPairByKeyUid*(self: Controller, keyUid: string): seq[KeyPairD
return
return self.walletAccountService.getMigratedKeyPairByKeyUid(keyUid)
proc setCurrentKeycardStateToLocked*(self: Controller, keycardUid: string) =
if not serviceApplicable(self.walletAccountService):
return
if not self.walletAccountService.setKeycardLocked(keycardUid):
info "updating keycard locked state failed", keycardUid=keycardUid
proc setCurrentKeycardStateToUnlocked*(self: Controller, keycardUid: string) =
if not serviceApplicable(self.walletAccountService):
return
if not self.walletAccountService.setKeycardUnlocked(keycardUid):
info "updating keycard unlocked state failed", keycardUid=keycardUid
proc updateKeycardUid*(self: Controller, keycardUid: string) =
if not serviceApplicable(self.walletAccountService):
return
self.setCurrentKeycardStateToUnlocked(self.tmpKeycardUid)
if self.tmpKeycardUid != keycardUid:
if not self.walletAccountService.updateKeycardUid(self.tmpKeycardUid, keycardUid):
self.tmpKeycardUid = keycardUid
info "update keycard uid failed", oldKeycardUid=self.tmpKeycardUid, newKeycardUid=keycardUid
proc getSigningPhrase*(self: Controller): string =
if not serviceApplicable(self.settingsService):
return
@ -309,6 +373,9 @@ proc getSigningPhrase*(self: Controller): string =
proc enterKeycardPin*(self: Controller, pin: string) =
self.keycardService.enterPin(pin)
proc enterKeycardPuk*(self: Controller, puk: string) =
self.keycardService.enterPuk(puk)
proc storePinToKeycard*(self: Controller, pin: string, puk: string) =
self.keycardService.storePin(pin, puk)

View File

@ -16,11 +16,12 @@ method executeBackCommand*(self: CreatePinState, controller: Controller) =
controller.cancelCurrentFlow()
method executeTertiaryCommand*(self: CreatePinState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
if self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method getNextSecondaryState*(self: CreatePinState, controller: Controller): State =
if self.flowType == FlowType.SetupNewKeycard:
if controller.getPin().len == PINLengthForStatusApp:
return createState(StateType.RepeatPin, self.flowType, self)
return nil
if self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.UnlockKeycard:
if controller.getPin().len == PINLengthForStatusApp:
return createState(StateType.RepeatPin, self.flowType, self)

View File

@ -18,7 +18,7 @@ method executePrimaryCommand*(self: EnterBiometricsPasswordState, controller: Co
controller.tryToStoreDataToKeychain(password)
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
else:
controller.setKeycardData("wrong-pass")
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongPassword, add = true))
method getNextPrimaryState*(self: EnterBiometricsPasswordState, controller: Controller): State =
if self.flowType == FlowType.Authentication:

View File

@ -17,7 +17,7 @@ method executePrimaryCommand*(self: EnterPasswordState, controller: Controller)
if self.success:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
else:
controller.setKeycardData("wrong-pass")
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongPassword, add = true))
method getNextPrimaryState*(self: EnterPasswordState, controller: Controller): State =
if self.flowType == FlowType.Authentication:

View File

@ -52,7 +52,7 @@ method resolveKeycardNextState*(self: EnterPinState, keycardFlowType: string, ke
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setMetadataFromKeycard(keycardEvent.cardMetadata)
controller.setMetadataFromKeycard(keycardEvent.cardMetadata, updateKeyPair = true)
return createState(StateType.PinVerified, self.flowType, nil)
if self.flowType == FlowType.SetupNewKeycard:
if keycardFlowType == ResponseTypeValueEnterPIN and
@ -61,16 +61,19 @@ method resolveKeycardNextState*(self: EnterPinState, keycardFlowType: string, ke
controller.setKeycardData($keycardEvent.pinRetries)
if keycardEvent.pinRetries > 0:
return createState(StateType.WrongPin, self.flowType, nil)
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.UseUnlockLabelForLockedState, add = true))
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueEnterPIN and
keycardEvent.error.len == 0:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.UseUnlockLabelForLockedState, add = true))
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueEnterPUK and
keycardEvent.error.len == 0:
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.UseUnlockLabelForLockedState, add = true))
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setMetadataFromKeycard(keycardEvent.cardMetadata)
controller.setMetadataFromKeycard(keycardEvent.cardMetadata, updateKeyPair = true)
return createState(StateType.PinVerified, self.flowType, nil)
if self.flowType == FlowType.Authentication:
if keycardFlowType == ResponseTypeValueEnterPIN and

View File

@ -0,0 +1,36 @@
type
EnterPukState* = ref object of State
proc newEnterPukState*(flowType: FlowType, backState: State): EnterPukState =
result = EnterPukState()
result.setup(flowType, StateType.EnterPuk, backState)
proc delete*(self: EnterPukState) =
self.State.delete
method executePrimaryCommand*(self: EnterPukState, controller: Controller) =
if self.flowType == FlowType.UnlockKeycard:
if controller.getPuk().len == PUKLengthForStatusApp:
controller.enterKeycardPuk(controller.getPuk())
method executeTertiaryCommand*(self: EnterPukState, controller: Controller) =
if self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: EnterPukState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
let state = ensureReaderAndCardPresence(self, keycardFlowType, keycardEvent, controller)
if not state.isNil:
return state
if self.flowType == FlowType.UnlockKeycard:
if keycardFlowType == ResponseTypeValueEnterNewPIN and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorUnblocking:
return createState(StateType.CreatePin, self.flowType, self.getBackState)
if keycardFlowType == ResponseTypeValueEnterPUK and
keycardEvent.error.len > 0 and
keycardEvent.error == RequestParamPUK:
controller.setKeycardData($keycardEvent.pukRetries)
if keycardEvent.pukRetries > 0:
return createState(StateType.WrongPuk, self.flowType, self.getBackState)
return createState(StateType.MaxPukRetriesReached, self.flowType, nil)

View File

@ -17,16 +17,25 @@ method executePrimaryCommand*(self: EnterSeedPhraseState, controller: Controller
if self.verifiedSeedPhrase:
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
else:
controller.setKeycardData(getPredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
method executeTertiaryCommand*(self: EnterSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
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.getKeyUidWhichIsBeingUnlocking()
if self.verifiedSeedPhrase:
controller.runGetMetadataFlow()
else:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
method getNextPrimaryState*(self: EnterSeedPhraseState, controller: Controller): State =
if self.flowType == FlowType.SetupNewKeycard:
if not self.verifiedSeedPhrase:
return createState(StateType.WrongSeedPhrase, self.flowType, nil)
if self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.UnlockKeycard:
if not self.verifiedSeedPhrase:
return createState(StateType.WrongSeedPhrase, self.flowType, nil)
method executeTertiaryCommand*(self: EnterSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: EnterSeedPhraseState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
@ -37,3 +46,15 @@ method resolveKeycardNextState*(self: EnterSeedPhraseState, keycardFlowType: str
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.keyUid.len > 0:
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(), 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)

View File

@ -12,7 +12,7 @@ method executePrimaryCommand*(self: FactoryResetConfirmationDisplayMetadataState
if self.flowType == FlowType.FactoryReset:
controller.runGetAppInfoFlow(factoryReset = true)
elif self.flowType == FlowType.SetupNewKeycard:
controller.setKeycardData(getPredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.HideKeyPair, add = true))
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.HideKeyPair, add = true))
controller.runGetAppInfoFlow(factoryReset = true)
method executeTertiaryCommand*(self: FactoryResetConfirmationDisplayMetadataState, controller: Controller) =

View File

@ -12,7 +12,7 @@ method executePrimaryCommand*(self: FactoryResetConfirmationState, controller: C
if self.flowType == FlowType.FactoryReset:
controller.runGetAppInfoFlow(factoryReset = true)
elif self.flowType == FlowType.SetupNewKeycard:
controller.setKeycardData(getPredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.HideKeyPair, add = true))
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.HideKeyPair, add = true))
controller.runGetAppInfoFlow(factoryReset = true)
method executeTertiaryCommand*(self: FactoryResetConfirmationState, controller: Controller) =

View File

@ -13,15 +13,11 @@ method executeBackCommand*(self: InsertKeycardState, controller: Controller) =
if not self.getBackState.isNil and self.getBackState.stateType == StateType.SelectExistingKeyPair:
controller.cancelCurrentFlow()
method executePrimaryCommand*(self: InsertKeycardState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method executeTertiaryCommand*(self: InsertKeycardState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.Authentication:
self.flowType == FlowType.Authentication or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: InsertKeycardState, keycardFlowType: string, keycardEvent: KeycardEvent,
@ -32,10 +28,10 @@ method resolveKeycardNextState*(self: InsertKeycardState, keycardFlowType: strin
if keycardFlowType == ResponseTypeValueInsertCard and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorConnection:
controller.setKeycardData(getPredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WronglyInsertedCard, add = true))
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WronglyInsertedCard, add = true))
return nil
if keycardFlowType == ResponseTypeValueCardInserted:
controller.setKeycardData(getPredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WronglyInsertedCard, add = false))
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WronglyInsertedCard, add = false))
if self.flowType == FlowType.SetupNewKeycard:
return createState(StateType.KeycardInserted, self.flowType, self.getBackState)
return createState(StateType.KeycardInserted, self.flowType, nil)

View File

@ -0,0 +1,17 @@
type
KeycardAlreadyUnlockedState* = ref object of State
proc newKeycardAlreadyUnlockedState*(flowType: FlowType, backState: State): KeycardAlreadyUnlockedState =
result = KeycardAlreadyUnlockedState()
result.setup(flowType, StateType.KeycardAlreadyUnlocked, backState)
proc delete*(self: KeycardAlreadyUnlockedState) =
self.State.delete
method executePrimaryCommand*(self: KeycardAlreadyUnlockedState, controller: Controller) =
if self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
method executeTertiaryCommand*(self: KeycardAlreadyUnlockedState, controller: Controller) =
if self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -11,5 +11,6 @@ proc delete*(self: KeycardEmptyState) =
method executeTertiaryCommand*(self: KeycardEmptyState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.Authentication:
self.flowType == FlowType.Authentication or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -23,5 +23,6 @@ method getNextSecondaryState*(self: KeycardInsertedState, controller: Controller
method executeTertiaryCommand*(self: KeycardInsertedState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.Authentication:
self.flowType == FlowType.Authentication or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -1,17 +0,0 @@
type
KeycardLockedState* = ref object of State
proc newKeycardLockedState*(flowType: FlowType, backState: State): KeycardLockedState =
result = KeycardLockedState()
result.setup(flowType, StateType.KeycardLocked, backState)
proc delete*(self: KeycardLockedState) =
self.State.delete
method getNextPrimaryState*(self: KeycardLockedState, controller: Controller): State =
if self.flowType == FlowType.SetupNewKeycard:
return createState(StateType.FactoryResetConfirmation, self.flowType, self)
method executeTertiaryCommand*(self: KeycardLockedState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -10,8 +10,8 @@ proc delete*(self: KeycardNotEmptyState) =
method executePrimaryCommand*(self: KeycardNotEmptyState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:
controller.setKeycardData(getPredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.HideKeyPair, add = true))
controller.runGetMetadataFlow()
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.HideKeyPair, add = true))
controller.runGetMetadataFlow(resolveAddress = true)
method executeTertiaryCommand*(self: KeycardNotEmptyState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:

View File

@ -0,0 +1,24 @@
type
MaxPairingSlotsReachedState* = ref object of State
proc newMaxPairingSlotsReachedState*(flowType: FlowType, backState: State): MaxPairingSlotsReachedState =
result = MaxPairingSlotsReachedState()
result.setup(flowType, StateType.MaxPairingSlotsReached, backState)
proc delete*(self: MaxPairingSlotsReachedState) =
self.State.delete
method getNextPrimaryState*(self: MaxPairingSlotsReachedState, controller: Controller): State =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard:
return createState(StateType.FactoryResetConfirmation, self.flowType, self)
if self.flowType == FlowType.Authentication:
debug "Run Unlock Keycard flow... (not developed yet)"
return nil
method executeTertiaryCommand*(self: MaxPairingSlotsReachedState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.Authentication or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -19,6 +19,8 @@ method getNextPrimaryState*(self: MaxPinRetriesReachedState, controller: Control
method executeTertiaryCommand*(self: MaxPinRetriesReachedState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.Authentication:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
if self.flowType == FlowType.SetupNewKeycard:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.UseUnlockLabelForLockedState, add = false))
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -0,0 +1,25 @@
type
MaxPukRetriesReachedState* = ref object of State
proc newMaxPukRetriesReachedState*(flowType: FlowType, backState: State): MaxPukRetriesReachedState =
result = MaxPukRetriesReachedState()
result.setup(flowType, StateType.MaxPukRetriesReached, backState)
proc delete*(self: MaxPukRetriesReachedState) =
self.State.delete
method getNextPrimaryState*(self: MaxPukRetriesReachedState, controller: Controller): State =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard:
return createState(StateType.FactoryResetConfirmation, self.flowType, self)
if self.flowType == FlowType.Authentication:
debug "Run Unlock Keycard flow... (not developed yet)"
if self.flowType == FlowType.UnlockKeycard:
return createState(StateType.EnterSeedPhrase, self.flowType, self)
method executeTertiaryCommand*(self: MaxPukRetriesReachedState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.Authentication or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -11,5 +11,6 @@ proc delete*(self: NotKeycardState) =
method executeTertiaryCommand*(self: NotKeycardState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.Authentication:
self.flowType == FlowType.Authentication or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -14,7 +14,13 @@ method getNextPrimaryState*(self: PinSetState, controller: Controller): State =
return createState(StateType.EnterSeedPhrase, self.flowType, nil)
else:
return createState(StateType.SeedPhraseDisplay, self.flowType, nil)
return nil
if self.flowType == FlowType.UnlockKeycard:
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.GetMetadata:
if controller.getValidPuk():
return createState(StateType.UnlockKeycardSuccess, self.flowType, nil)
return createState(StateType.WrongPuk, self.flowType, self.getBackState)
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.StoreMetadata:
return createState(StateType.UnlockKeycardSuccess, self.flowType, nil)
method executeTertiaryCommand*(self: PinSetState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:

View File

@ -16,7 +16,8 @@ method executeBackCommand*(self: PluginReaderState, controller: Controller) =
method executeTertiaryCommand*(self: PluginReaderState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.Authentication:
self.flowType == FlowType.Authentication or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: PluginReaderState, keycardFlowType: string, keycardEvent: KeycardEvent,

View File

@ -16,7 +16,8 @@ method executeBackCommand*(self: ReadingKeycardState, controller: Controller) =
method executeTertiaryCommand*(self: ReadingKeycardState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.Authentication:
self.flowType == FlowType.Authentication or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method getNextSecondaryState*(self: ReadingKeycardState, controller: Controller): State =

View File

@ -13,11 +13,6 @@ method executeBackCommand*(self: RecognizedKeycardState, controller: Controller)
if not self.getBackState.isNil and self.getBackState.stateType == StateType.SelectExistingKeyPair:
controller.cancelCurrentFlow()
method executeTertiaryCommand*(self: RecognizedKeycardState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method getNextSecondaryState*(self: RecognizedKeycardState, controller: Controller): State =
if self.flowType == FlowType.FactoryReset:
if controller.containsMetadata():
@ -25,4 +20,12 @@ method getNextSecondaryState*(self: RecognizedKeycardState, controller: Controll
else:
return createState(StateType.FactoryResetConfirmation, self.flowType, nil)
if self.flowType == FlowType.SetupNewKeycard:
return createState(StateType.CreatePin, self.flowType, self.getBackState)
return createState(StateType.CreatePin, self.flowType, self.getBackState)
if self.flowType == FlowType.UnlockKeycard:
return createState(StateType.UnlockKeycardOptions, self.flowType, nil)
method executeTertiaryCommand*(self: RecognizedKeycardState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -15,12 +15,14 @@ method executeBackCommand*(self: RepeatPinState, controller: Controller) =
method executeSecondaryCommand*(self: RepeatPinState, controller: Controller) =
if not controller.getPinMatch():
return
if self.flowType == FlowType.SetupNewKeycard:
controller.storePinToKeycard(controller.getPin(), controller.generateRandomPUK())
if self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.UnlockKeycard:
controller.storePinToKeycard(controller.getPin(), controller.generateRandomPUK())
method executeTertiaryCommand*(self: RepeatPinState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
if self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: RepeatPinState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
@ -31,5 +33,33 @@ method resolveKeycardNextState*(self: RepeatPinState, keycardFlowType: string, k
if keycardFlowType == ResponseTypeValueEnterMnemonic and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorLoadingKeys:
controller.setKeycardUid(keycardEvent.instanceUID)
return createState(StateType.PinSet, self.flowType, nil)
controller.setKeycardUidTheSelectedKeypairIsMigratedTo(keycardEvent.instanceUID)
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.setKeycardData($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.instanceUID)
return createState(StateType.PinSet, self.flowType, nil)
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount:
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
if controller.getKeyUidWhichIsBeingUnlocking() != 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:
controller.updateKeycardUid(keycardEvent.instanceUID)
return createState(StateType.PinSet, self.flowType, nil)

View File

@ -16,8 +16,12 @@ type StateType* {.pure.} = enum
PinVerified = "PinVerified"
EnterPin = "EnterPin"
WrongPin = "WrongPin"
EnterPuk = "EnterPuk"
WrongPuk = "WrongPuk"
WrongKeychainPin = "WrongKeychainPin"
MaxPinRetriesReached = "MaxPinRetriesReached"
MaxPukRetriesReached = "MaxPukRetriesReached"
MaxPairingSlotsReached = "MaxPairingSlotsReached"
FactoryResetConfirmation = "FactoryResetConfirmation"
FactoryResetConfirmationDisplayMetadata = "FactoryResetConfirmationDisplayMetadata"
FactoryResetSuccess = "FactoryResetSuccess"
@ -26,6 +30,9 @@ type StateType* {.pure.} = enum
KeycardEmpty = "KeycardEmpty"
KeycardNotEmpty = "KeycardNotEmpty"
KeycardLocked = "KeycardLocked"
KeycardAlreadyUnlocked = "KeycardAlreadyUnlocked"
UnlockKeycardOptions = "UnlockKeycardOptions"
UnlockKeycardSuccess = "UnlockKeycardSuccess"
NotKeycard = "NotKeycard"
WrongKeycard = "WrongKeycard"
RecognizedKeycard = "RecognizedKeycard"

View File

@ -1,4 +1,4 @@
import parseutils, chronicles
import parseutils, sequtils, sugar, chronicles
import ../../../../../app_service/service/keycard/constants
import ../controller
from ../../../../../app_service/service/keycard/service import KCSFlowType
@ -14,10 +14,14 @@ type PredefinedKeycardData* {.pure.} = enum
WronglyInsertedCard = 1
HideKeyPair = 2
WrongSeedPhrase = 4
WrongPassword = 8
OfferPukForUnlock = 16
UseUnlockLabelForLockedState = 32
# Forward declaration
proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: State): State
proc getPredefinedKeycardData*(currValue: string, value: PredefinedKeycardData, add: bool): string
proc extractPredefinedKeycardDataToNumber*(currValue: string): int
proc updatePredefinedKeycardData*(currValue: string, value: PredefinedKeycardData, add: bool): string
proc ensureReaderAndCardPresence*(state: State, keycardFlowType: string, keycardEvent: KeycardEvent, controller: Controller): State
proc ensureReaderAndCardPresenceAndResolveNextState*(state: State, keycardFlowType: string, keycardEvent: KeycardEvent, controller: Controller): State
@ -29,6 +33,7 @@ include create_pin_state
include enter_biometrics_password_state
include enter_password_state
include enter_pin_state
include enter_puk_state
include enter_seed_phrase_state
include factory_reset_confirmation_displayed_metadata_state
include factory_reset_confirmation_state
@ -41,8 +46,10 @@ include keycard_empty_state
include keycard_inserted_state
include keycard_metadata_display_state
include keycard_not_empty_state
include keycard_locked_state
include keycard_already_unlocked_state
include max_pin_retries_reached_state
include max_puk_retries_reached_state
include max_pairing_slots_reached_state
include migrating_key_pair_state
include not_keycard_state
include pin_set_state
@ -54,14 +61,26 @@ include repeat_pin_state
include seed_phrase_display_state
include seed_phrase_enter_words_state
include select_existing_key_pair_state
include unlock_keycard_options_state
include unlock_keycard_success_state
include wrong_biometrics_password_state
include wrong_keycard_state
include wrong_password_state
include wrong_pin_state
include wrong_puk_state
include wrong_keychain_pin_state
include wrong_seed_phrase_state
proc getPredefinedKeycardData*(currValue: string, value: PredefinedKeycardData, add: bool): string =
proc extractPredefinedKeycardDataToNumber*(currValue: string): int =
var currNum: int
try:
if parseInt(currValue, currNum) == 0:
return 0
return currNum
except:
return 0
proc updatePredefinedKeycardData*(currValue: string, value: PredefinedKeycardData, add: bool): string =
var currNum: int
try:
if add:
@ -94,6 +113,8 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St
return newEnterPasswordState(flowType, backState)
if stateToBeCreated == StateType.EnterPin:
return newEnterPinState(flowType, backState)
if stateToBeCreated == StateType.EnterPuk:
return newEnterPukState(flowType, backState)
if stateToBeCreated == StateType.EnterSeedPhrase:
return newEnterSeedPhraseState(flowType, backState)
if stateToBeCreated == StateType.FactoryResetConfirmationDisplayMetadata:
@ -118,10 +139,18 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St
return newKeycardMetadataDisplayState(flowType, backState)
if stateToBeCreated == StateType.KeycardNotEmpty:
return newKeycardNotEmptyState(flowType, backState)
if stateToBeCreated == StateType.KeycardLocked:
return newKeycardLockedState(flowType, backState)
if stateToBeCreated == StateType.KeycardAlreadyUnlocked:
return newKeycardAlreadyUnlockedState(flowType, backState)
if stateToBeCreated == StateType.UnlockKeycardOptions:
return newUnlockKeycardOptionsState(flowType, backState)
if stateToBeCreated == StateType.UnlockKeycardSuccess:
return newUnlockKeycardSuccessState(flowType, backState)
if stateToBeCreated == StateType.MaxPinRetriesReached:
return newMaxPinRetriesReachedState(flowType, backState)
if stateToBeCreated == StateType.MaxPukRetriesReached:
return newMaxPukRetriesReachedState(flowType, backState)
if stateToBeCreated == StateType.MaxPairingSlotsReached:
return newMaxPairingSlotsReachedState(flowType, backState)
if stateToBeCreated == StateType.MigratingKeyPair:
return newMigratingKeyPairState(flowType, backState)
if stateToBeCreated == StateType.NotKeycard:
@ -152,6 +181,8 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St
return newWrongPasswordState(flowType, backState)
if stateToBeCreated == StateType.WrongPin:
return newWrongPinState(flowType, backState)
if stateToBeCreated == StateType.WrongPuk:
return newWrongPukState(flowType, backState)
if stateToBeCreated == StateType.WrongKeychainPin:
return newWrongKeychainPinState(flowType, backState)
if stateToBeCreated == StateType.WrongSeedPhrase:
@ -160,9 +191,10 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St
error "No implementation available for state ", state=stateToBeCreated
proc ensureReaderAndCardPresence*(state: State, keycardFlowType: string, keycardEvent: KeycardEvent, controller: Controller): State =
## Handling factory reset or authentication flow
## Handling factory reset or authentication or unlock keycard flow
if state.flowType == FlowType.FactoryReset or
state.flowType == FlowType.Authentication:
state.flowType == FlowType.Authentication or
state.flowType == FlowType.UnlockKeycard:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorConnection:
@ -177,7 +209,7 @@ proc ensureReaderAndCardPresence*(state: State, keycardFlowType: string, keycard
return nil
return createState(StateType.InsertKeycard, state.flowType, nil)
if keycardFlowType == ResponseTypeValueCardInserted:
controller.setKeycardData(getPredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WronglyInsertedCard, add = false))
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WronglyInsertedCard, add = false))
return createState(StateType.KeycardInserted, state.flowType, nil)
## Handling setup new keycard flow
@ -198,7 +230,7 @@ proc ensureReaderAndCardPresence*(state: State, keycardFlowType: string, keycard
return createState(StateType.InsertKeycard, state.flowType, state)
return createState(StateType.InsertKeycard, state.flowType, state.getBackState)
if keycardFlowType == ResponseTypeValueCardInserted:
controller.setKeycardData(getPredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WronglyInsertedCard, add = false))
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WronglyInsertedCard, add = false))
if state.stateType == StateType.SelectExistingKeyPair:
return createState(StateType.InsertKeycard, state.flowType, state)
return createState(StateType.KeycardInserted, state.flowType, state.getBackState)
@ -214,11 +246,15 @@ proc ensureReaderAndCardPresenceAndResolveNextState*(state: State, keycardFlowTy
if keycardFlowType == ResponseTypeValueEnterPUK and
keycardEvent.error.len == 0:
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
return createState(StateType.MaxPinRetriesReached, state.flowType, nil)
return createState(StateType.FactoryResetConfirmation, state.flowType, nil)
if keycardFlowType == ResponseTypeValueSwapCard and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorNotAKeycard:
keycardEvent.error.len > 0:
if keycardEvent.error == ErrorNotAKeycard:
return createState(StateType.NotKeycard, state.flowType, nil)
if keycardEvent.error == ErrorFreePairingSlots:
return createState(StateType.FactoryResetConfirmation, state.flowType, nil)
if keycardEvent.error == ErrorPUKRetries:
return createState(StateType.FactoryResetConfirmation, state.flowType, nil)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
if keycardEvent.error.len > 0:
if keycardEvent.error == ErrorOk:
@ -241,7 +277,9 @@ proc ensureReaderAndCardPresenceAndResolveNextState*(state: State, keycardFlowTy
if keycardEvent.error == ErrorHasKeys:
return createState(StateType.KeycardNotEmpty, state.flowType, nil)
if keycardEvent.error == ErrorFreePairingSlots:
return createState(StateType.KeycardLocked, state.flowType, nil)
return createState(StateType.MaxPairingSlotsReached, state.flowType, nil)
if keycardEvent.error == ErrorPUKRetries:
return createState(StateType.MaxPukRetriesReached, state.flowType, nil)
if keycardFlowType == ResponseTypeValueEnterPIN:
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.GetMetadata:
return createState(StateType.EnterPin, state.flowType, nil)
@ -274,6 +312,10 @@ proc ensureReaderAndCardPresenceAndResolveNextState*(state: State, keycardFlowTy
return createState(StateType.NotKeycard, state.flowType, nil)
if keycardEvent.error == ErrorNoKeys:
return createState(StateType.KeycardEmpty, state.flowType, nil)
if keycardEvent.error == ErrorFreePairingSlots:
return createState(StateType.MaxPairingSlotsReached, state.flowType, nil)
if keycardEvent.error == ErrorPUKRetries:
return createState(StateType.MaxPukRetriesReached, state.flowType, nil)
if keycardFlowType == ResponseTypeValueEnterPIN:
if keycardEvent.keyUid == controller.getKeyUidWhichIsBeingAuthenticating():
if controller.loggedInUserUsesBiometricLogin():
@ -295,4 +337,30 @@ proc ensureReaderAndCardPresenceAndResolveNextState*(state: State, keycardFlowTy
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
if keycardEvent.error.len == 0:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
return nil
return nil
## Handling unlock keycard flow
if state.flowType == FlowType.UnlockKeycard:
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.GetMetadata:
controller.setKeyUidWhichIsBeingUnlocking(keycardEvent.keyUid)
if keycardFlowType == ResponseTypeValueEnterPIN and
keycardEvent.error.len == 0:
return createState(StateType.KeycardAlreadyUnlocked, state.flowType, nil)
if keycardFlowType == ResponseTypeValueSwapCard and
keycardEvent.error.len > 0:
if keycardEvent.error == ErrorNotAKeycard:
return createState(StateType.NotKeycard, state.flowType, nil)
if keycardEvent.error == ErrorNoKeys:
return createState(StateType.KeycardEmpty, state.flowType, nil)
if keycardEvent.error == ErrorFreePairingSlots:
return createState(StateType.RecognizedKeycard, state.flowType, nil)
if keycardEvent.error == ErrorPUKRetries:
return createState(StateType.RecognizedKeycard, state.flowType, nil)
if keycardFlowType == ResponseTypeValueEnterPUK and
keycardEvent.error.len == 0:
controller.setKeycardUid(keycardEvent.instanceUID)
return createState(StateType.RecognizedKeycard, state.flowType, nil)
if keycardFlowType == ResponseTypeValueKeycardFlowResult or
keycardEvent.error.len > 0:
if keycardEvent.error == ErrorNoKeys:
return createState(StateType.KeycardEmpty, state.flowType, nil)

View File

@ -0,0 +1,21 @@
type
UnlockKeycardOptionsState* = ref object of State
proc newUnlockKeycardOptionsState*(flowType: FlowType, backState: State): UnlockKeycardOptionsState =
result = UnlockKeycardOptionsState()
result.setup(flowType, StateType.UnlockKeycardOptions, backState)
proc delete*(self: UnlockKeycardOptionsState) =
self.State.delete
method getNextPrimaryState*(self: UnlockKeycardOptionsState, controller: Controller): State =
if self.flowType == FlowType.UnlockKeycard:
return createState(StateType.EnterSeedPhrase, self.flowType, self)
method getNextSecondaryState*(self: UnlockKeycardOptionsState, controller: Controller): State =
if self.flowType == FlowType.UnlockKeycard:
return createState(StateType.EnterPuk, self.flowType, self)
method executeTertiaryCommand*(self: UnlockKeycardOptionsState, controller: Controller) =
if self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -0,0 +1,17 @@
type
UnlockKeycardSuccessState* = ref object of State
proc newUnlockKeycardSuccessState*(flowType: FlowType, backState: State): UnlockKeycardSuccessState =
result = UnlockKeycardSuccessState()
result.setup(flowType, StateType.UnlockKeycardSuccess, backState)
proc delete*(self: UnlockKeycardSuccessState) =
self.State.delete
method executePrimaryCommand*(self: UnlockKeycardSuccessState, controller: Controller) =
if self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
method executeTertiaryCommand*(self: UnlockKeycardSuccessState, controller: Controller) =
if self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)

View File

@ -12,14 +12,14 @@ proc delete*(self: WrongBiometricsPasswordState) =
method executePrimaryCommand*(self: WrongBiometricsPasswordState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setKeycardData("")
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongPassword, add = false))
let password = controller.getPassword()
self.success = controller.verifyPassword(password)
if self.success:
controller.tryToStoreDataToKeychain(password)
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
else:
controller.setKeycardData("wrong-pass")
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongPassword, add = true))
method executeTertiaryCommand*(self: WrongBiometricsPasswordState, controller: Controller) =
if self.flowType == FlowType.Authentication:

View File

@ -12,13 +12,13 @@ proc delete*(self: WrongPasswordState) =
method executePrimaryCommand*(self: WrongPasswordState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setKeycardData("")
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongPassword, add = false))
let password = controller.getPassword()
self.success = controller.verifyPassword(password)
if self.success:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
else:
controller.setKeycardData("wrong-pass")
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongPassword, add = true))
method executeSecondaryCommand*(self: WrongPasswordState, controller: Controller) =
if self.flowType == FlowType.Authentication:

View File

@ -50,7 +50,7 @@ method resolveKeycardNextState*(self: WrongPinState, keycardFlowType: string, ke
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setMetadataFromKeycard(keycardEvent.cardMetadata)
controller.setMetadataFromKeycard(keycardEvent.cardMetadata, updateKeyPair = true)
return createState(StateType.PinVerified, self.flowType, nil)
if self.flowType == FlowType.SetupNewKeycard:
if keycardFlowType == ResponseTypeValueEnterPIN and
@ -59,13 +59,15 @@ method resolveKeycardNextState*(self: WrongPinState, keycardFlowType: string, ke
controller.setKeycardData($keycardEvent.pinRetries)
if keycardEvent.pinRetries > 0:
return self
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.UseUnlockLabelForLockedState, add = true))
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueEnterPUK and
keycardEvent.error.len == 0:
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.UseUnlockLabelForLockedState, add = true))
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setMetadataFromKeycard(keycardEvent.cardMetadata)
controller.setMetadataFromKeycard(keycardEvent.cardMetadata, updateKeyPair = true)
return createState(StateType.PinVerified, self.flowType, nil)
if self.flowType == FlowType.Authentication:
if keycardFlowType == ResponseTypeValueEnterPIN and

View File

@ -0,0 +1,42 @@
type
WrongPukState* = ref object of State
proc newWrongPukState*(flowType: FlowType, backState: State): WrongPukState =
result = WrongPukState()
result.setup(flowType, StateType.WrongPuk, backState)
proc delete*(self: WrongPukState) =
self.State.delete
method executePrimaryCommand*(self: WrongPukState, controller: Controller) =
if self.flowType == FlowType.UnlockKeycard:
if controller.getPuk().len == PUKLengthForStatusApp:
controller.enterKeycardPuk(controller.getPuk())
method executeTertiaryCommand*(self: WrongPukState, controller: Controller) =
if self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: WrongPukState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
let state = ensureReaderAndCardPresence(self, keycardFlowType, keycardEvent, controller)
if not state.isNil:
return state
if self.flowType == FlowType.UnlockKeycard:
if keycardFlowType == ResponseTypeValueEnterPUK and
keycardEvent.error.len > 0 and
keycardEvent.error == RequestParamPUK:
controller.setKeycardData($keycardEvent.pukRetries)
controller.setPukValid(false)
if keycardEvent.pukRetries > 0:
return nil
return createState(StateType.MaxPukRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueSwapCard and
keycardEvent.error.len > 0 and
keycardEvent.error == RequestParamPUKRetries:
return createState(StateType.MaxPukRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
if keycardEvent.error.len == 0:
controller.setPukValid(true)
controller.updateKeycardUid(keycardEvent.instanceUID)
return createState(StateType.UnlockKeycardSuccess, self.flowType, nil)

View File

@ -14,18 +14,28 @@ proc delete*(self: WrongSeedPhraseState) =
method executePrimaryCommand*(self: WrongSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:
controller.setKeycardData(getPredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
sleep(500) # just to shortly remove text on the UI side
self.verifiedSeedPhrase = controller.validSeedPhrase(controller.getSeedPhrase()) and
controller.seedPhraseRefersToSelectedKeyPair(controller.getSeedPhrase())
if self.verifiedSeedPhrase:
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
else:
controller.setKeycardData(getPredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
if self.flowType == FlowType.UnlockKeycard:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
sleep(500) # just to shortly remove text on the UI side
self.verifiedSeedPhrase = controller.validSeedPhrase(controller.getSeedPhrase()) and
controller.getKeyUidForSeedPhrase(controller.getSeedPhrase()) == controller.getKeyUidWhichIsBeingUnlocking()
if self.verifiedSeedPhrase:
controller.runGetMetadataFlow()
else:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
method executeTertiaryCommand*(self: WrongSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
if self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: WrongSeedPhraseState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
@ -35,5 +45,17 @@ method resolveKeycardNextState*(self: WrongSeedPhraseState, keycardFlowType: str
if self.flowType == FlowType.SetupNewKeycard:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.keyUid.len > 0:
controller.setKeycardData(getPredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
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(), 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)

View File

@ -35,6 +35,7 @@ type FlowType* {.pure.} = enum
FactoryReset = "FactoryReset"
SetupNewKeycard = "SetupNewKeycard"
Authentication = "Authentication"
UnlockKeycard = "UnlockKeycard"
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
@ -72,6 +73,9 @@ method runFlow*(self: AccessInterface, flowToRun: FlowType, keyUid = "", bip44Pa
method setPin*(self: AccessInterface, value: string) {.base.} =
raise newException(ValueError, "No implementation available")
method setPuk*(self: AccessInterface, value: string) {.base.} =
raise newException(ValueError, "No implementation available")
method setPassword*(self: AccessInterface, value: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -51,6 +51,7 @@ proc initKeyPairItem*(
proc `$`*(self: KeyPairItem): string =
result = fmt"""KeyPairItem[
pubKey: {self.pubkey},
keyUid: {self.keyUid},
name: {self.name},
image: {self.image},
icon: {self.icon},

View File

@ -69,6 +69,9 @@ method setKeycardData*[T](self: Module[T], value: string) =
method setPin*[T](self: Module[T], value: string) =
self.controller.setPin(value)
method setPuk*[T](self: Module[T], value: string) =
self.controller.setPuk(value)
method setPassword*[T](self: Module[T], value: string) =
self.controller.setPassword(value)
@ -111,11 +114,25 @@ method isProfileKeyPairMigrated*[T](self: Module[T]): bool =
method getSigningPhrase*[T](self: Module[T]): string =
return self.controller.getSigningPhrase()
proc authenticationKeyPairUpdate[T](self: Module[T], currFlowType: FlowType, nextStateType: StateType) =
## special check only for authentication flow
proc preStateActivities[T](self: Module[T], currFlowType: FlowType, nextStateType: StateType) =
if nextStateType == StateType.MaxPinRetriesReached or
nextStateType == StateType.MaxPukRetriesReached or
nextStateType == StateType.MaxPairingSlotsReached or
nextStateType == StateType.UnlockKeycardOptions:
## in case the card is locked on another device, we're updating its state in the DB
let (_, flowEvent) = self.controller.getLastReceivedKeycardData()
self.controller.setCurrentKeycardStateToLocked(flowEvent.instanceUID)
if currFlowType == FlowType.Authentication:
self.view.setLockedPropForKeyPairForAuthentication(nextStateType == StateType.MaxPinRetriesReached)
if currFlowType == FlowType.UnlockKeycard:
if nextStateType == StateType.UnlockKeycardOptions:
let (_, flowEvent) = self.controller.getLastReceivedKeycardData()
self.controller.setKeycardData(updatePredefinedKeycardData(self.controller.getKeycardData(), PredefinedKeycardData.OfferPukForUnlock, add = false))
if flowEvent.pinRetries == 0 and flowEvent.pukRetries > 0:
self.controller.setKeycardData(updatePredefinedKeycardData(self.controller.getKeycardData(), PredefinedKeycardData.OfferPukForUnlock, add = true))
method onBackActionClicked*[T](self: Module[T]) =
let currStateObj = self.view.currentStateObj()
if currStateObj.isNil:
@ -124,7 +141,7 @@ method onBackActionClicked*[T](self: Module[T]) =
debug "sm_back_action", currFlow=currStateObj.flowType(), currState=currStateObj.stateType()
currStateObj.executeBackCommand(self.controller)
let backState = currStateObj.getBackState()
self.authenticationKeyPairUpdate(backState.flowType(), backState.stateType())
self.preStateActivities(backState.flowType(), backState.stateType())
self.view.setCurrentState(backState)
debug "sm_back_action - set state", setCurrFlow=backState.flowType(), newCurrState=backState.stateType()
currStateObj.delete()
@ -139,7 +156,7 @@ method onPrimaryActionClicked*[T](self: Module[T]) =
let nextState = currStateObj.getNextPrimaryState(self.controller)
if nextState.isNil:
return
self.authenticationKeyPairUpdate(nextState.flowType(), nextState.stateType())
self.preStateActivities(nextState.flowType(), nextState.stateType())
self.view.setCurrentState(nextState)
debug "sm_primary_action - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
@ -153,7 +170,7 @@ method onSecondaryActionClicked*[T](self: Module[T]) =
let nextState = currStateObj.getNextSecondaryState(self.controller)
if nextState.isNil:
return
self.authenticationKeyPairUpdate(nextState.flowType(), nextState.stateType())
self.preStateActivities(nextState.flowType(), nextState.stateType())
self.view.setCurrentState(nextState)
debug "sm_secondary_action - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
@ -167,33 +184,36 @@ method onTertiaryActionClicked*[T](self: Module[T]) =
let nextState = currStateObj.getNextTertiaryState(self.controller)
if nextState.isNil:
return
self.authenticationKeyPairUpdate(nextState.flowType(), nextState.stateType())
self.preStateActivities(nextState.flowType(), nextState.stateType())
self.view.setCurrentState(nextState)
debug "sm_tertiary_action - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
method onKeycardResponse*[T](self: Module[T], keycardFlowType: string, keycardEvent: KeycardEvent) =
let currStateObj = self.view.currentStateObj()
if currStateObj.isNil:
if self.tmpLocalState.isNil:
error "sm_cannot resolve current state"
return
## Check local state first, in case postponed flow is run
if not self.tmpLocalState.isNil:
let nextState = self.tmpLocalState.resolveKeycardNextState(keycardFlowType, keycardEvent, self.controller)
self.tmpLocalState.delete
self.tmpLocalState = nil
if nextState.isNil:
return
self.authenticationKeyPairUpdate(nextState.flowType(), nextState.stateType())
self.preStateActivities(nextState.flowType(), nextState.stateType())
self.view.setCurrentState(nextState)
self.controller.readyToDisplayPopup()
debug "sm_on_keycard_response - from_local - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
return
## Check regular flows
let currStateObj = self.view.currentStateObj()
if currStateObj.isNil:
return
debug "sm_on_keycard_response", currFlow=currStateObj.flowType(), currState=currStateObj.stateType()
let nextState = currStateObj.resolveKeycardNextState(keycardFlowType, keycardEvent, self.controller)
if nextState.isNil:
return
self.authenticationKeyPairUpdate(nextState.flowType(), nextState.stateType())
self.preStateActivities(nextState.flowType(), nextState.stateType())
self.view.setCurrentState(nextState)
debug "sm_on_keycard_response - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
proc buildKeyPairsList[T](self: Module[T]): seq[KeyPairItem] =
proc buildKeyPairsList[T](self: Module[T], excludeAlreadyMigratedPairs: bool): seq[KeyPairItem] =
let keyPairMigrated = proc(keyUid: string): bool =
result = false
let migratedKeyPairs = self.controller.getAllMigratedKeyPairs()
@ -218,12 +238,10 @@ proc buildKeyPairsList[T](self: Module[T]): seq[KeyPairItem] =
let accounts = self.controller.getWalletAccounts()
var items: seq[KeyPairItem]
for a in accounts:
if a.isChat or a.walletType == WalletTypeWatch or keyPairMigrated(a.keyUid):
if a.isChat or a.walletType == WalletTypeWatch or (excludeAlreadyMigratedPairs and keyPairMigrated(a.keyUid)):
continue
var item = findItemByDerivedFromAddress(items, a.derivedfrom)
if a.walletType == WalletTypeDefaultStatusAccount or a.walletType == WalletTypeGenerated:
if self.isProfileKeyPairMigrated():
continue
if item.isNil:
item = initKeyPairItem(pubKey = a.publicKey,
keyUid = a.keyUid,
@ -274,7 +292,7 @@ proc buildKeyPairsList[T](self: Module[T]): seq[KeyPairItem] =
proc prepareKeyPairItemForAuthentication[T](self: Module[T], keyUid: string) =
var item = initKeyPairItem()
self.view.createKeyPairForAuthentication()
let items = self.buildKeyPairsList()
let items = self.buildKeyPairsList(excludeAlreadyMigratedPairs = false)
for it in items:
if it.keyUid == keyUid:
item = it
@ -299,13 +317,13 @@ method runFlow*[T](self: Module[T], flowToRun: FlowType, keyUid = "", bip44Path
self.initialized = true
self.controller.init()
if flowToRun == FlowType.FactoryReset:
let items = self.buildKeyPairsList()
let items = self.buildKeyPairsList(excludeAlreadyMigratedPairs = false)
self.view.createKeyPairModel(items)
self.tmpLocalState = newReadingKeycardState(flowToRun, nil)
self.controller.runGetMetadataFlow()
self.controller.runGetMetadataFlow(resolveAddress = true)
return
if flowToRun == FlowType.SetupNewKeycard:
let items = self.buildKeyPairsList()
let items = self.buildKeyPairsList(excludeAlreadyMigratedPairs = true)
self.view.createKeyPairModel(items)
self.view.setCurrentState(newSelectExistingKeyPairState(flowToRun, nil))
self.controller.readyToDisplayPopup()
@ -324,6 +342,10 @@ method runFlow*[T](self: Module[T], flowToRun: FlowType, keyUid = "", bip44Path
self.authenticationPopupIsAlreadyRunning = true
self.controller.readyToDisplayPopup()
return
if flowToRun == FlowType.UnlockKeycard:
self.tmpLocalState = newReadingKeycardState(flowToRun, nil)
self.controller.runGetMetadataFlow(resolveAddress = true)
return
method setSelectedKeyPair*[T](self: Module[T], item: KeyPairItem) =
var paths: seq[string]

View File

@ -165,6 +165,9 @@ QtObject:
proc setPin*(self: View, value: string) {.slot.} =
self.delegate.setPin(value)
proc setPuk*(self: View, value: string) {.slot.} =
self.delegate.setPuk(value)
proc setPassword*(self: View, value: string) {.slot.} =
self.delegate.setPassword(value)

View File

@ -369,19 +369,20 @@ proc loginAccountKeycard*(self: Controller) =
if(error.len > 0):
self.delegate.emitAccountLoginError(error)
proc seedPhraseRefersToSelectedKeyPair*(self: Controller, seedPhrase: string): bool =
let selectedAccount = self.getSelectedLoginAccount()
let accFromSP = self.accountsService.createAccountFromMnemonic(seedPhrase)
return selectedAccount.keyUid == accFromSP.keyUid
proc cancelCurrentFlow*(self: Controller) =
self.keycardService.cancelCurrentFlow()
# in most cases we're running another flow after canceling the current one,
# this way we're giving to the keycard some time to cancel the current flow
sleep(200)
proc runLoadAccountFlow*(self: Controller, factoryReset = false) =
proc runLoadAccountFlow*(self: Controller, seedPhraseLength = 0, seedPhrase = "", puk = "", factoryReset = false) =
self.cancelCurrentFlow() # before running into any flow we're making sure that the previous flow is canceled
self.keycardService.startLoadAccountFlow(factoryReset)
proc runLoadAccountFlowWithSeedPhrase*(self: Controller, seedPhraseLength: int, seedPhrase: string, factoryReset = false) =
self.cancelCurrentFlow() # before running into any flow we're making sure that the previous flow is canceled
self.keycardService.startLoadAccountFlowWithSeedPhrase(seedPhraseLength, seedPhrase, factoryReset)
self.keycardService.startLoadAccountFlow(seedPhraseLength, seedPhrase, puk, factoryReset)
proc runLoginFlow*(self: Controller) =
self.cancelCurrentFlow() # before running into any flow we're making sure that the previous flow is canceled
@ -391,9 +392,9 @@ proc startLoginFlowAutomatically*(self: Controller, pin: string) =
self.cancelCurrentFlow() # before running into any flow we're making sure that the previous flow is canceled
self.keycardService.startLoginFlowAutomatically(pin)
proc runRecoverAccountFlow*(self: Controller) =
proc runRecoverAccountFlow*(self: Controller, seedPhraseLength = 0, seedPhrase = "", puk = "", factoryReset = false) =
self.cancelCurrentFlow() # before running into any flow we're making sure that the previous flow is canceled
self.keycardService.startRecoverAccountFlow()
self.keycardService.startRecoverAccountFlow(seedPhraseLength, seedPhrase, puk, factoryReset)
proc resumeCurrentFlow*(self: Controller) =
self.keycardService.resumeCurrentFlow()

View File

@ -10,7 +10,7 @@ proc delete*(self: LoginKeycardEmptyState) =
method executePrimaryCommand*(self: LoginKeycardEmptyState, controller: Controller) =
if self.flowType == FlowType.AppLogin:
controller.runLoadAccountFlow(factoryReset = true)
controller.runLoadAccountFlow(seedPhraseLength = 0, seedPhrase = "", puk = "", factoryReset = true)
method getNextSecondaryState*(self: LoginKeycardEmptyState, controller: Controller): State =
controller.cancelCurrentFlow()

View File

@ -10,7 +10,7 @@ proc delete*(self: LoginNotKeycardState) =
method executePrimaryCommand*(self: LoginNotKeycardState, controller: Controller) =
if self.flowType == FlowType.AppLogin:
controller.runLoadAccountFlow(factoryReset = true)
controller.runLoadAccountFlow(seedPhraseLength = 0, seedPhrase = "", puk = "", factoryReset = true)
method getNextSecondaryState*(self: LoginNotKeycardState, controller: Controller): State =
controller.cancelCurrentFlow()

View File

@ -23,14 +23,17 @@ method getNextPrimaryState*(self: UserProfileEnterSeedPhraseState, controller: C
method executePrimaryCommand*(self: UserProfileEnterSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.AppLogin:
controller.runLoadAccountFlowWithSeedPhrase(controller.getSeedPhraseLength(), controller.getSeedPhrase(), factoryReset = true)
self.successfulImport = controller.validMnemonic(controller.getSeedPhrase()) and
controller.seedPhraseRefersToSelectedKeyPair(controller.getSeedPhrase())
if self.successfulImport:
controller.runLoadAccountFlow(controller.getSeedPhraseLength(), controller.getSeedPhrase(), puk = "", factoryReset = true)
else:
self.successfulImport = controller.importMnemonic()
if self.successfulImport:
if self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
controller.runLoadAccountFlowWithSeedPhrase(controller.getSeedPhraseLength(), controller.getSeedPhrase(), factoryReset = true)
controller.runLoadAccountFlow(controller.getSeedPhraseLength(), controller.getSeedPhrase(), puk = "", factoryReset = true)
elif self.flowType == FlowType.FirstRunOldUserKeycardImport:
controller.runLoadAccountFlowWithSeedPhrase(controller.getSeedPhraseLength(), controller.getSeedPhrase(), factoryReset = true)
controller.runLoadAccountFlow(controller.getSeedPhraseLength(), controller.getSeedPhrase(), puk = "", factoryReset = true)
method resolveKeycardNextState*(self: UserProfileEnterSeedPhraseState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =

View File

@ -17,6 +17,7 @@ const ErrorStoreMeta* = "storing-metadata"
const ErrorNoData* = "no-data"
const ErrorFreePairingSlots* = "free-pairing-slots"
const ErrorPIN* = "pin"
const ErrorPUKRetries* = "puk-retries"
const RequestParamAppInfo* = "application-info"
const RequestParamInstanceUID* = "instance-uid"

View File

@ -60,12 +60,6 @@ QtObject:
lastReceivedKeycardData: tuple[flowType: string, flowEvent: KeycardEvent]
setPayloadForCurrentFlow: JsonNode
#################################################
# Forward declaration section
proc startGetMetadataFlow*(self: Service)
#################################################
proc setup(self: Service) =
self.QObject.setup
@ -164,23 +158,16 @@ QtObject:
)
self.threadpool.start(arg)
proc startLoadAccountFlow*(self: Service, factoryReset: bool) =
var payload = %* { }
if factoryReset:
payload[RequestParamFactoryReset] = %* factoryReset
self.currentFlow = KCSFlowType.LoadAccount
self.startFlow(payload)
proc startLoadAccountFlowWithSeedPhrase*(self: Service, seedPhraseLength: int, seedPhrase: string, factoryReset: bool) =
if seedPhrase.len == 0:
error "empty seed phrase provided"
return
proc startLoadAccountFlow*(self: Service, seedPhraseLength: int, seedPhrase: string, puk: string, factoryReset: bool) =
var payload = %* {
RequestParamOverwrite: true,
RequestParamMnemonicLen: seedPhraseLength,
RequestParamNewPUK: self.generateRandomPUK(),
RequestParamMnemonic: seedPhrase
RequestParamOverwrite: true
}
if seedPhrase.len > 0 and seedPhraseLength > 0:
payload[RequestParamMnemonic] = %* seedPhrase
payload[RequestParamMnemonicLen] = %* seedPhraseLength
payload[RequestParamNewPUK] = %* self.generateRandomPUK()
if puk.len > 0:
payload[RequestParamNewPUK] = %* puk
if factoryReset:
payload[RequestParamFactoryReset] = %* factoryReset
self.currentFlow = KCSFlowType.LoadAccount
@ -198,8 +185,18 @@ QtObject:
self.currentFlow = KCSFlowType.Login
self.startFlow(payload)
proc startRecoverAccountFlow*(self: Service) =
let payload = %* { }
proc startRecoverAccountFlow*(self: Service, seedPhraseLength: int, seedPhrase: string, puk: string, factoryReset: bool) =
var payload = %* {
RequestParamOverwrite: true
}
if seedPhrase.len > 0 and seedPhraseLength > 0:
payload[RequestParamMnemonic] = %* seedPhrase
payload[RequestParamMnemonicLen] = %* seedPhraseLength
payload[RequestParamNewPUK] = %* self.generateRandomPUK()
if puk.len > 0:
payload[RequestParamNewPUK] = %* puk
if factoryReset:
payload[RequestParamFactoryReset] = %* factoryReset
self.currentFlow = KCSFlowType.RecoverAccount
self.startFlow(payload)
@ -210,10 +207,10 @@ QtObject:
self.currentFlow = KCSFlowType.GetAppInfo
self.startFlow(payload)
proc startGetMetadataFlow*(self: Service) =
let payload = %* {
RequestParamResolveAddr: true
}
proc startGetMetadataFlow*(self: Service, resolveAddress: bool) =
var payload = %* { }
if resolveAddress:
payload[RequestParamResolveAddr] = %* resolveAddress
self.currentFlow = KCSFlowType.GetMetadata
self.startFlow(payload)

View File

@ -47,6 +47,9 @@ StatusModal {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
return qsTr("Authenticate")
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.unlockKeycard) {
return qsTr("Unlock Keycard")
}
return ""
}
@ -85,11 +88,15 @@ StatusModal {
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmptyMetadata ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmpty ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardNotEmpty ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardLocked ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardAlreadyUnlocked ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.notKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.unlockKeycardOptions ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.unlockKeycardSuccess ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsReadyToSign ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPukRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPairingSlotsReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.recognizedKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardMetadataDisplay ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPasswordFailed ||
@ -117,6 +124,11 @@ StatusModal {
{
return keycardPinComponent
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPuk ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPuk)
{
return keycardPukComponent
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterSeedPhrase ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongSeedPhrase)
{
@ -197,6 +209,21 @@ StatusModal {
}
}
Component {
id: keycardPukComponent
KeycardPuk {
sharedKeycardModule: root.sharedKeycardModule
Component.onCompleted: {
d.primaryButtonEnabled = false
}
onPukUpdated: {
d.primaryButtonEnabled = puk.length === Constants.keycard.general.keycardPukLength
}
}
}
Component {
id: enterSeedPhraseComponent
EnterSeedPhrase {
@ -275,34 +302,44 @@ StatusModal {
StatusButton {
id: tertiaryButton
height: Constants.keycard.general.footerButtonsHeight
text: qsTr("Cancel")
visible: {
text: {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.setupNewKeycard) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.selectExistingKeyPair ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardNotEmpty ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardLocked ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmptyMetadata ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.notKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPukRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPairingSlotsReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetConfirmation ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetConfirmationDisplayMetadata ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetSuccess ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pinVerified ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardMetadataDisplay) {
return true
return qsTr("Cancel")
}
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.factoryReset) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pinVerified ||
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pluginReader ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.readingKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.insertKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardInserted ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.recognizedKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmpty ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.notKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pinVerified ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPukRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPairingSlotsReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardMetadataDisplay ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmptyMetadata ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetConfirmationDisplayMetadata ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetConfirmation) {
return true
return qsTr("Cancel")
}
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
@ -315,6 +352,8 @@ StatusModal {
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeychainPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsReadyToSign ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPukRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPairingSlotsReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.notKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPassword ||
@ -325,10 +364,34 @@ StatusModal {
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterBiometricsPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongBiometricsPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin)
return true
return qsTr("Cancel")
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.unlockKeycard) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pluginReader ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.readingKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.insertKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardInserted ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.recognizedKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPuk ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPuk ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmpty ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.notKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.unlockKeycardOptions ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterSeedPhrase)
return qsTr("Cancel")
}
return false
return ""
}
visible: text !== ""
enabled: {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.readingKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.migratingKeyPair) {
if (d.disablePopupClose) {
return false
}
}
return true
}
onClicked: {
@ -362,32 +425,21 @@ StatusModal {
return qsTr("Use PIN")
}
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.unlockKeycard) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.unlockKeycardOptions)
return qsTr("Unlock using PUK")
}
return ""
}
visible: {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
if (root.sharedKeycardModule.loggedInUserUsesBiometricLogin()) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pluginReader ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.insertKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardInserted ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.readingKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPasswordFailed ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinFailed ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinInvalid ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsReadyToSign ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.notKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmpty)
return true
visible: text !== ""
enabled: {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.readingKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.migratingKeyPair) {
if (d.disablePopupClose) {
return false
}
}
return false
}
enabled: {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
if (root.sharedKeycardModule.loggedInUserUsesBiometricLogin() &&
(root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pluginReader ||
@ -399,6 +451,10 @@ StatusModal {
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmpty))
return false
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.unlockKeycard) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.unlockKeycardOptions)
return root.sharedKeycardModule.keycardData & Constants.predefinedKeycardData.offerPukForUnlock
}
return true
}
@ -442,7 +498,6 @@ StatusModal {
return qsTr("Factory reset this Keycard")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.selectExistingKeyPair ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardLocked ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmptyMetadata ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetSuccess ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.seedPhraseDisplay ||
@ -450,8 +505,13 @@ StatusModal {
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardMetadataDisplay) {
return qsTr("Next")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached) {
return qsTr("Unlock Keycard")
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPukRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPairingSlotsReached) {
let a = root.sharedKeycardModule.keycardData & Constants.predefinedKeycardData.useUnlockLabelForLockedState
if (root.sharedKeycardModule.keycardData & Constants.predefinedKeycardData.useUnlockLabelForLockedState)
return qsTr("Unlock Keycard")
return qsTr("Next")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.migratingKeyPair ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keyPairMigrateFailure) {
@ -462,7 +522,6 @@ StatusModal {
return qsTr("Restart app & sign in using your new Keycard")
return qsTr("Done")
}
return qsTr("Cancel")
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.factoryReset) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin ||
@ -478,14 +537,14 @@ StatusModal {
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmptyMetadata) {
return qsTr("Next")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPukRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPairingSlotsReached) {
return qsTr("Unlock Keycard")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.readingKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetSuccess) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetSuccess) {
return qsTr("Done")
}
return qsTr("Cancel")
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pluginReader ||
@ -514,12 +573,35 @@ StatusModal {
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinInvalid) {
return qsTr("Try biometrics again")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPukRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPairingSlotsReached) {
return qsTr("Unlock Keycard")
}
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.unlockKeycard) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardAlreadyUnlocked ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.unlockKeycardSuccess)
return qsTr("Done")
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.unlockKeycardOptions)
return qsTr("Unlock using seed phrase")
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.createPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.repeatPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pinSet ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterSeedPhrase ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPuk ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPuk)
return qsTr("Next")
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongSeedPhrase) {
return qsTr("Try entering seed phrase again")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPukRetriesReached) {
return qsTr("Unlock Keycard")
}
}
return ""
}
visible: text !== ""
enabled: {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.readingKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.migratingKeyPair) {
@ -568,9 +650,15 @@ StatusModal {
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin) {
return d.primaryButtonEnabled
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached) {
return true
}
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.unlockKeycard) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterSeedPhrase ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPuk ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPuk)
return d.primaryButtonEnabled
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.createPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.repeatPin)
return false
}
return true
}

View File

@ -42,8 +42,10 @@ Item {
QtObject {
id: d
readonly property bool wrongPassword: root.kcData & Constants.predefinedKeycardData.wrongPassword
function updatePasswordValidation() {
root.passwordValid(password.text !== "" && root.kcData === "")
root.passwordValid(password.text !== "" && !d.wrongPassword)
}
}
@ -152,7 +154,7 @@ Item {
}
PropertyChanges {
target: info
text: root.kcData !== ""? qsTr("Password incorrect") : ""
text: d.wrongPassword? qsTr("Password incorrect") : ""
color: Theme.palette.dangerColor1
}
},
@ -177,7 +179,7 @@ Item {
}
PropertyChanges {
target: info
text: root.kcData !== ""? qsTr("Password incorrect") : ""
text: d.wrongPassword? qsTr("Password incorrect") : ""
color: Theme.palette.dangerColor1
}
},
@ -202,7 +204,7 @@ Item {
}
PropertyChanges {
target: info
text: root.kcData !== ""? qsTr("Password incorrect") : ""
text: d.wrongPassword? qsTr("Password incorrect") : ""
color: Theme.palette.dangerColor1
}
}

View File

@ -173,6 +173,8 @@ Item {
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.readingKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmpty ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPukRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPairingSlotsReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsReadyToSign ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinFailed ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinInvalid ||
@ -215,6 +217,8 @@ Item {
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.readingKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmpty ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPukRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPairingSlotsReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsReadyToSign ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinFailed ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinInvalid ||
@ -366,6 +370,31 @@ Item {
color: Theme.palette.dangerColor1
}
},
State {
name: Constants.keycardSharedState.unlockKeycardOptions
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.unlockKeycardOptions
PropertyChanges {
target: title
text: qsTr("Unlock this Keycard")
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.directColor1
}
PropertyChanges {
target: image
pattern: "keycard/strong_error/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 18
endImgIndex: 29
duration: 1300
loops: -1
}
PropertyChanges {
target: message
text: ""
}
},
State {
name: Constants.keycardSharedState.wrongKeycard
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard
@ -388,35 +417,6 @@ Item {
color: Theme.palette.dangerColor1
}
},
State {
name: Constants.keycardSharedState.maxPinRetriesReached
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached
PropertyChanges {
target: title
text: qsTr("Keycard locked")
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.dangerColor1
}
PropertyChanges {
target: image
pattern: "keycard/strong_error/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 18
endImgIndex: 29
duration: 1300
loops: -1
}
PropertyChanges {
target: message
text: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication?
qsTr("You will need to unlock it before proceeding") :
qsTr("Pin entered incorrectly too many times")
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.dangerColor1
}
},
State {
name: Constants.keycardSharedState.keycardEmptyMetadata
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmptyMetadata
@ -447,21 +447,18 @@ Item {
text: qsTr("Keycard is empty")
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication?
Theme.palette.dangerColor1 : Theme.palette.directColor1
color: Theme.palette.directColor1
}
PropertyChanges {
target: image
source: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication?
Style.png("keycard/plain-error") : Style.png("keycard/card-empty")
source: Style.png("keycard/card-empty")
pattern: ""
}
PropertyChanges {
target: message
text: qsTr("There is no key pair on this Keycard")
font.pixelSize: Constants.keycard.general.fontSize2
color: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication?
Theme.palette.dangerColor1 : Theme.palette.directColor1
color: Theme.palette.directColor1
}
},
State {
@ -490,14 +487,18 @@ Item {
}
},
State {
name: Constants.keycardSharedState.keycardLocked
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardLocked
name: "sharedLockedState"
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPukRetriesReached ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPairingSlotsReached
PropertyChanges {
target: title
text: qsTr("Keycard locked and already stores keys")
text: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.setupNewKeycard?
qsTr("Keycard locked and already stores keys") : qsTr("Keycard locked")
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.directColor1
color: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.setupNewKeycard?
Theme.palette.directColor1 : Theme.palette.dangerColor1
}
PropertyChanges {
target: image
@ -511,10 +512,73 @@ Item {
}
PropertyChanges {
target: message
text: qsTr("The Keycard you have inserted is locked,\nyou will need to factory reset it before proceeding")
text: {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.setupNewKeycard)
return qsTr("The Keycard you have inserted is locked,\nyou will need to factory reset it before proceeding")
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication)
return qsTr("You will need to unlock it before proceeding")
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached)
return qsTr("Pin entered incorrectly too many times")
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPukRetriesReached)
return qsTr("Puk entered incorrectly too many times")
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPairingSlotsReached)
return qsTr("Max pairing slots reached for the entered keycard")
return ""
}
font.pixelSize: Constants.keycard.general.fontSize2
color: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.setupNewKeycard?
Theme.palette.directColor1 : Theme.palette.dangerColor1
}
},
State {
name: Constants.keycardSharedState.keycardAlreadyUnlocked
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardAlreadyUnlocked
PropertyChanges {
target: title
text: qsTr("Your Keycard is already unlocked!")
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.directColor1
}
PropertyChanges {
target: image
pattern: "keycard/success/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 0
endImgIndex: 29
duration: 1300
loops: 1
}
PropertyChanges {
target: message
text: ""
}
},
State {
name: Constants.keycardSharedState.unlockKeycardSuccess
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.unlockKeycardSuccess
PropertyChanges {
target: title
text: qsTr("Unlock successful")
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.directColor1
}
PropertyChanges {
target: image
pattern: "keycard/strong_success/img-%1"
source: ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 0
endImgIndex: 20
duration: 1000
loops: 1
}
PropertyChanges {
target: message
text: ""
}
},
State {
name: Constants.keycardSharedState.recognizedKeycard

View File

@ -83,7 +83,8 @@ Item {
anchors.bottomMargin: Style.current.halfPadding
anchors.leftMargin: Style.current.xlPadding
anchors.rightMargin: Style.current.xlPadding
spacing: Style.current.halfPadding
spacing: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication?
Style.current.halfPadding : Style.current.padding
KeycardImage {
id: image
@ -108,7 +109,6 @@ Item {
StatusPinInput {
id: pinInputField
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: !info.visble && !message.visible? true : false
validator: StatusIntValidator{bottom: 0; top: 999999;}
pinLen: Constants.keycard.general.keycardPinLength
enabled: root.sharedKeycardModule.currentState.stateType !== Constants.keycardSharedState.pinSet &&
@ -148,7 +148,6 @@ Item {
StatusBaseText {
id: info
Layout.alignment: Qt.AlignCenter
Layout.fillHeight: info.visble && !message.visible? true : false
wrapMode: Text.WordWrap
visible: text !== ""
}
@ -156,12 +155,12 @@ Item {
StatusBaseText {
id: message
Layout.alignment: Qt.AlignCenter
Layout.fillHeight: message.visible? true : false
wrapMode: Text.WordWrap
visible: text !== ""
}
Loader {
id: loader
Layout.preferredWidth: parent.width
active: {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.setupNewKeycard) {
@ -201,6 +200,13 @@ Item {
return undefined
}
}
Item {
id: spacer
Layout.fillWidth: true
Layout.fillHeight: true
visible: !loader.active
}
}
states: [

View File

@ -0,0 +1,153 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
import utils 1.0
import "../helpers"
Item {
id: root
property var sharedKeycardModule
property int remainingAttempts: parseInt(root.sharedKeycardModule.keycardData, 10)
signal pukUpdated(string puk)
onRemainingAttemptsChanged: {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPuk) {
pukInputField.statesInitialization()
pukInputField.forceFocus()
}
}
onStateChanged: {
pukInputField.statesInitialization()
pukInputField.forceFocus()
}
ColumnLayout {
anchors.fill: parent
anchors.topMargin: Style.current.xlPadding
anchors.bottomMargin: Style.current.halfPadding
anchors.leftMargin: Style.current.xlPadding
anchors.rightMargin: Style.current.xlPadding
spacing: Style.current.padding
KeycardImage {
id: image
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: Constants.keycard.shared.imageHeight
Layout.preferredWidth: Constants.keycard.shared.imageWidth
}
StatusBaseText {
id: title
Layout.alignment: Qt.AlignCenter
font.weight: Font.Bold
}
StatusPinInput {
id: pukInputField
Layout.alignment: Qt.AlignHCenter
validator: StatusRegularExpressionValidator { regularExpression: /[0-9]+/ }
pinLen: Constants.keycard.general.keycardPukLength
additionalSpacing: Constants.keycard.general.keycardPukAdditionalSpacing
additionalSpacingOnEveryNItems: Constants.keycard.general.keycardPukAdditionalSpacingOnEvery4Items
onPinInputChanged: {
root.pukUpdated(pinInput)
if(pinInput.length == 0) {
return
}
if(root.state === Constants.keycardSharedState.enterPuk ||
root.state === Constants.keycardSharedState.wrongPuk) {
root.sharedKeycardModule.setPuk(pinInput)
root.sharedKeycardModule.currentState.doSecondaryAction()
}
}
}
StatusBaseText {
id: info
Layout.alignment: Qt.AlignCenter
wrapMode: Text.WordWrap
visible: text !== ""
}
StatusBaseText {
id: message
Layout.alignment: Qt.AlignCenter
wrapMode: Text.WordWrap
visible: text !== ""
}
Item {
id: spacer
Layout.fillWidth: true
Layout.fillHeight: true
}
}
states: [
State {
name: Constants.keycardSharedState.enterPuk
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPuk
PropertyChanges {
target: image
source: Style.png("keycard/card-inserted")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("Enter PUK")
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
}
PropertyChanges {
target: info
text: ""
}
PropertyChanges {
target: message
text: ""
}
},
State {
name: Constants.keycardSharedState.wrongPuk
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPuk
PropertyChanges {
target: image
source: Style.png("keycard/plain-error")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("Enter PUK")
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
}
PropertyChanges {
target: info
text: qsTr("The PUK is incorrect, try entering it again")
color: Theme.palette.dangerColor1
font.pixelSize: Constants.keycard.general.fontSize3
}
PropertyChanges {
target: message
text: qsTr("%n attempt(s) remaining", "", root.remainingAttempts)
color: root.remainingAttempts === 1?
Theme.palette.dangerColor1 :
Theme.palette.baseColor1
font.pixelSize: Constants.keycard.general.fontSize3
}
}
]
}

View File

@ -73,6 +73,9 @@ QtObject {
readonly property int wronglyInsertedCard: 1
readonly property int hideKeyPair: 2
readonly property int wrongSeedPhrase: 4
readonly property int wrongPassword: 8
readonly property int offerPukForUnlock: 16
readonly property int useUnlockLabelForLockedState: 32
}
readonly property QtObject keycardSharedFlow: QtObject {
@ -80,6 +83,7 @@ QtObject {
readonly property string factoryReset: "FactoryReset"
readonly property string setupNewKeycard: "SetupNewKeycard"
readonly property string authentication: "Authentication"
readonly property string unlockKeycard: "UnlockKeycard"
}
readonly property QtObject keycardSharedState: QtObject {
@ -94,8 +98,12 @@ QtObject {
readonly property string pinVerified: "PinVerified"
readonly property string enterPin: "EnterPin"
readonly property string wrongPin: "WrongPin"
readonly property string enterPuk: "EnterPuk"
readonly property string wrongPuk: "WrongPuk"
readonly property string wrongKeychainPin: "WrongKeychainPin"
readonly property string maxPinRetriesReached: "MaxPinRetriesReached"
readonly property string maxPukRetriesReached: "MaxPukRetriesReached"
readonly property string maxPairingSlotsReached: "MaxPairingSlotsReached"
readonly property string factoryResetConfirmation: "FactoryResetConfirmation"
readonly property string factoryResetConfirmationDisplayMetadata: "FactoryResetConfirmationDisplayMetadata"
readonly property string factoryResetSuccess: "FactoryResetSuccess"
@ -103,8 +111,10 @@ QtObject {
readonly property string keycardMetadataDisplay: "KeycardMetadataDisplay"
readonly property string keycardEmpty: "KeycardEmpty"
readonly property string keycardNotEmpty: "KeycardNotEmpty"
readonly property string keycardLocked: "KeycardLocked"
readonly property string keycardAlreadyUnlocked: "KeycardAlreadyUnlocked"
readonly property string notKeycard: "NotKeycard"
readonly property string unlockKeycardOptions: "UnlockKeycardOptions"
readonly property string unlockKeycardSuccess: "UnlockKeycardSuccess"
readonly property string wrongKeycard: "WrongKeycard"
readonly property string recognizedKeycard: "RecognizedKeycard"
readonly property string selectExistingKeyPair: "SelectExistingKeyPair"
@ -367,6 +377,9 @@ QtObject {
readonly property QtObject general: QtObject {
readonly property int footerWrapperHeight: 125
readonly property int keycardPinLength: 6
readonly property int keycardPukLength: 12
readonly property int keycardPukAdditionalSpacingOnEvery4Items: 4
readonly property int keycardPukAdditionalSpacing: 32
readonly property int fontSize1: 22
readonly property int fontSize2: 15
readonly property int fontSize3: 12