feat(@desktop/keycard): authenticate flow added

- Added `Authenticate` flow
- `Setup a new Keycard with an existing account` updated so it includes `Authenticate` flow
from the point where is needed (when migrating a profile keypair)
- We are missing `Unlock Keycard` flow for this one, will be added once it is developed
This commit is contained in:
Sale Djenic 2022-09-13 12:03:25 +02:00 committed by saledjenic
parent 405171dad7
commit 03bb1e5bf0
47 changed files with 1720 additions and 183 deletions

View File

@ -246,7 +246,7 @@ proc init*(self: Controller) =
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED, data)
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_DISPLAY_POPUP) do(e: Args):
let args = SharedKeycarModuleArgs(e)
let args = SharedKeycarModuleBaseArgs(e)
if args.uniqueIdentifier != UNIQUE_MAIN_MODULE_IDENTIFIER or
self.authenticateUserFlowRequestedBy.len == 0:
return

View File

@ -34,7 +34,7 @@ proc init*(self: Controller) =
self.delegate.onSharedKeycarModuleFlowTerminated(args.lastStepInTheCurrentFlow)
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_DISPLAY_POPUP) do(e: Args):
let args = SharedKeycarModuleArgs(e)
let args = SharedKeycarModuleBaseArgs(e)
if args.uniqueIdentifier != UNIQUE_SETTING_KEYCARD_MODULE_IDENTIFIER:
return
self.delegate.onDisplayKeycardSharedModuleFlow()

View File

@ -33,12 +33,13 @@ type
tmpPin: string
tmpPinMatch: bool
tmpPassword: string
tmpKeyUid: string
tmpSelectedKeyPairIsProfile: bool
tmpSelectedKeyPairName: string
tmpSelectedKeyPairDto: KeyPairDto
tmpSelectedKeyPairWalletPaths: seq[string]
tmpSeedPhrase: string
tmpSeedPhraseLength: int
tmpKeyUidWhichIsBeingAuthenticating: string
tmpUsePinFromBiometrics: bool
proc newController*(delegate: io_interface.AccessInterface,
uniqueIdentifier: string,
@ -64,6 +65,7 @@ proc newController*(delegate: io_interface.AccessInterface,
result.tmpPinMatch = false
result.tmpSeedPhraseLength = 0
result.tmpSelectedKeyPairIsProfile = false
result.tmpUsePinFromBiometrics = false
proc serviceApplicable[T](service: T): bool =
if not service.isNil:
@ -138,6 +140,12 @@ proc setPin*(self: Controller, value: string) =
proc getPin*(self: Controller): string =
return self.tmpPin
proc setUsePinFromBiometrics*(self: Controller, value: bool) =
self.tmpUsePinFromBiometrics = value
proc usePinFromBiometrics*(self: Controller): bool =
return self.tmpUsePinFromBiometrics
proc setPinMatch*(self: Controller, value: bool) =
self.tmpPinMatch = value
@ -150,8 +158,8 @@ proc setPassword*(self: Controller, value: string) =
proc getPassword*(self: Controller): string =
return self.tmpPassword
proc setKeyUid*(self: Controller, value: string) =
self.tmpKeyUid = value
proc getKeyUidWhichIsBeingAuthenticating*(self: Controller): string =
self.tmpKeyUidWhichIsBeingAuthenticating
proc setSelectedKeyPairIsProfile*(self: Controller, value: bool) =
self.tmpSelectedKeyPairIsProfile = value
@ -159,11 +167,14 @@ proc setSelectedKeyPairIsProfile*(self: Controller, value: bool) =
proc getSelectedKeyPairIsProfile*(self: Controller): bool =
return self.tmpSelectedKeyPairIsProfile
proc setSelectedKeyPairName*(self: Controller, value: string) =
self.tmpSelectedKeyPairName = value
proc setSelectedKeyPairDto*(self: Controller, keyPairDto: KeyPairDto) =
self.tmpSelectedKeyPairDto = keyPairDto
proc getSelectedKeyPairName*(self: Controller): string =
return self.tmpSelectedKeyPairName
proc getSelectedKeyPairDto*(self: Controller): KeyPairDto =
return self.tmpSelectedKeyPairDto
proc setKeycardUid*(self: Controller, value: string) =
self.tmpSelectedKeyPairDto.keycardUid = value
proc setSelectedKeyPairWalletPaths*(self: Controller, paths: seq[string]) =
self.tmpSelectedKeyPairWalletPaths = paths
@ -186,16 +197,16 @@ proc validSeedPhrase*(self: Controller, seedPhrase: string): bool =
let err = self.accountsService.validateMnemonic(seedPhrase)
return err.len == 0
proc seedPhraseRefersToLoggedInUser*(self: Controller, seedPhrase: string): bool =
proc seedPhraseRefersToSelectedKeyPair*(self: Controller, seedPhrase: string): bool =
let acc = self.accountsService.createAccountFromMnemonic(seedPhrase)
return acc.keyUid == singletonInstance.userProfile.getAddress()
return acc.keyUid == self.tmpSelectedKeyPairDto.keyUid
proc verifyPassword*(self: Controller, password: string): bool =
return self.accountsService.verifyPassword(password)
proc convertToKeycardAccount*(self: Controller, password: string): bool =
proc convertSelectedKeyPairToKeycardAccount*(self: Controller, password: string): bool =
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NOT_NOW)
return self.accountsService.convertToKeycardAccount(self.tmpKeyUid, password)
return self.accountsService.convertToKeycardAccount(self.tmpSelectedKeyPairDto.keyUid, password)
proc getLoggedInAccount*(self: Controller): AccountDto =
return self.accountsService.getLoggedInAccount()
@ -231,15 +242,39 @@ proc runLoadAccountFlow*(self: Controller, factoryReset = false) =
self.cancelCurrentFlow()
self.keycardService.startLoadAccountFlow(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
## for belongs to. If we're just doing an authentication for a logged in user, then default key uid is always the key
## uid of the logged in user.
self.tmpKeyUidWhichIsBeingAuthenticating = keyUid
if self.tmpKeyUidWhichIsBeingAuthenticating.len == 0:
self.tmpKeyUidWhichIsBeingAuthenticating = singletonInstance.userProfile.getKeyUid()
self.cancelCurrentFlow()
self.keycardService.startSignFlow(bip44Path, txHash)
proc resumeCurrentFlowLater*(self: Controller) =
self.keycardService.resumeCurrentFlowLater()
proc readyToDisplayPopup*(self: Controller) =
self.events.emit(SignalSharedKeycarModuleDisplayPopup, Args())
let data = SharedKeycarModuleBaseArgs(uniqueIdentifier: self.uniqueIdentifier)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_DISPLAY_POPUP, data)
proc terminateCurrentFlow*(self: Controller, lastStepInTheCurrentFlow: bool) =
let data = SharedKeycarModuleFlowTerminatedArgs(lastStepInTheCurrentFlow: lastStepInTheCurrentFlow)
self.events.emit(SignalSharedKeycarModuleFlowTerminated, data)
let (_, flowEvent) = self.getLastReceivedKeycardData()
var data = SharedKeycarModuleFlowTerminatedArgs(uniqueIdentifier: self.uniqueIdentifier,
lastStepInTheCurrentFlow: lastStepInTheCurrentFlow)
if lastStepInTheCurrentFlow:
data.data = self.tmpPassword
data.keyUid = flowEvent.keyUid
data.txR = flowEvent.txSignature.r
data.txS = flowEvent.txSignature.s
data.txV = flowEvent.txSignature.v
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_FLOW_TERMINATED, data)
proc authenticateUser*(self: Controller) =
self.disconnectKeycardReponseSignal()
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: self.uniqueIdentifier)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
if not serviceApplicable(self.walletAccountService):
@ -251,6 +286,26 @@ proc getBalanceForAddress*(self: Controller, address: string): float64 =
return
return self.walletAccountService.fetchBalanceForAddress(address)
proc addMigratedKeyPair*(self: Controller, keyPair: KeyPairDto): bool =
if not serviceApplicable(self.walletAccountService):
return
return self.walletAccountService.addMigratedKeyPair(keyPair)
proc getAllMigratedKeyPairs*(self: Controller): seq[KeyPairDto] =
if not serviceApplicable(self.walletAccountService):
return
return self.walletAccountService.getAllMigratedKeyPairs()
proc getMigratedKeyPairByKeyUid*(self: Controller, keyUid: string): seq[KeyPairDto] =
if not serviceApplicable(self.walletAccountService):
return
return self.walletAccountService.getMigratedKeyPairByKeyUid(keyUid)
proc getSigningPhrase*(self: Controller): string =
if not serviceApplicable(self.settingsService):
return
return self.settingsService.getSigningPhrase()
proc enterKeycardPin*(self: Controller, pin: string) =
self.keycardService.enterPin(pin)
@ -290,3 +345,13 @@ proc loggedInUserUsesBiometricLogin*(self: Controller): bool =
if (value != LS_VALUE_STORE):
return false
return true
proc tryToObtainDataFromKeychain*(self: Controller) =
if(not self.loggedInUserUsesBiometricLogin()):
return
let loggedInAccount = self.getLoggedInAccount()
self.keychainService.tryToObtainData(loggedInAccount.name)
proc tryToStoreDataToKeychain*(self: Controller, password: string) =
let loggedInAccount = self.getLoggedInAccount()
self.keychainService.storeData(loggedInAccount.name, password)

View File

@ -0,0 +1,22 @@
type
BiometricsPasswordFailedState* = ref object of State
proc newBiometricsPasswordFailedState*(flowType: FlowType, backState: State): BiometricsPasswordFailedState =
result = BiometricsPasswordFailedState()
result.setup(flowType, StateType.BiometricsPasswordFailed, backState)
proc delete*(self: BiometricsPasswordFailedState) =
self.State.delete
method executePrimaryCommand*(self: BiometricsPasswordFailedState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.tryToObtainDataFromKeychain()
method getNextSecondaryState*(self: BiometricsPasswordFailedState, controller: Controller): State =
if self.flowType == FlowType.Authentication:
return createState(StateType.EnterPassword, self.flowType, nil)
method executeTertiaryCommand*(self: BiometricsPasswordFailedState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setPassword("")
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -0,0 +1,31 @@
type
BiometricsPinFailedState* = ref object of State
proc newBiometricsPinFailedState*(flowType: FlowType, backState: State): BiometricsPinFailedState =
result = BiometricsPinFailedState()
result.setup(flowType, StateType.BiometricsPinFailed, backState)
proc delete*(self: BiometricsPinFailedState) =
self.State.delete
method executePrimaryCommand*(self: BiometricsPinFailedState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.tryToObtainDataFromKeychain()
method executeSecondaryCommand*(self: BiometricsPinFailedState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setUsePinFromBiometrics(true)
method getNextSecondaryState*(self: BiometricsPinFailedState, controller: Controller): State =
if self.flowType == FlowType.Authentication:
return createState(StateType.EnterPin, self.flowType, nil)
method executeTertiaryCommand*(self: BiometricsPinFailedState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setPassword("")
controller.setPin("")
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: BiometricsPinFailedState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
return ensureReaderAndCardPresenceAndResolveNextState(self, keycardFlowType, keycardEvent, controller)

View File

@ -0,0 +1,31 @@
type
BiometricsPinInvalidState* = ref object of State
proc newBiometricsPinInvalidState*(flowType: FlowType, backState: State): BiometricsPinInvalidState =
result = BiometricsPinInvalidState()
result.setup(flowType, StateType.BiometricsPinInvalid, backState)
proc delete*(self: BiometricsPinInvalidState) =
self.State.delete
method executePrimaryCommand*(self: BiometricsPinInvalidState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.tryToObtainDataFromKeychain()
method executeSecondaryCommand*(self: BiometricsPinInvalidState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setUsePinFromBiometrics(true)
method getNextSecondaryState*(self: BiometricsPinInvalidState, controller: Controller): State =
if self.flowType == FlowType.Authentication:
return createState(StateType.EnterPin, self.flowType, nil)
method executeTertiaryCommand*(self: BiometricsPinInvalidState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setPassword("")
controller.setPin("")
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: BiometricsPinInvalidState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
return ensureReaderAndCardPresenceAndResolveNextState(self, keycardFlowType, keycardEvent, controller)

View File

@ -0,0 +1,31 @@
type
BiometricsReadyToSignState* = ref object of State
proc newBiometricsReadyToSignState*(flowType: FlowType, backState: State): BiometricsReadyToSignState =
result = BiometricsReadyToSignState()
result.setup(flowType, StateType.BiometricsReadyToSign, backState)
proc delete*(self: BiometricsReadyToSignState) =
self.State.delete
method executePrimaryCommand*(self: BiometricsReadyToSignState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.tryToObtainDataFromKeychain()
method executeSecondaryCommand*(self: BiometricsReadyToSignState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setUsePinFromBiometrics(true)
method getNextSecondaryState*(self: BiometricsReadyToSignState, controller: Controller): State =
if self.flowType == FlowType.Authentication:
return createState(StateType.EnterPin, self.flowType, nil)
method executeTertiaryCommand*(self: BiometricsReadyToSignState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setPassword("")
controller.setPin("")
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: BiometricsReadyToSignState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
return ensureReaderAndCardPresenceAndResolveNextState(self, keycardFlowType, keycardEvent, controller)

View File

@ -0,0 +1,31 @@
type
EnterBiometricsPasswordState* = ref object of State
success: bool
proc newEnterBiometricsPasswordState*(flowType: FlowType, backState: State): EnterBiometricsPasswordState =
result = EnterBiometricsPasswordState()
result.setup(flowType, StateType.EnterBiometricsPassword, backState)
result.success = false
proc delete*(self: EnterBiometricsPasswordState) =
self.State.delete
method executePrimaryCommand*(self: EnterBiometricsPasswordState, controller: Controller) =
if self.flowType == FlowType.Authentication:
let password = controller.getPassword()
self.success = controller.verifyPassword(password)
if self.success:
controller.tryToStoreDataToKeychain(password)
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
else:
controller.setKeycardData("wrong-pass")
method getNextPrimaryState*(self: EnterBiometricsPasswordState, controller: Controller): State =
if self.flowType == FlowType.Authentication:
if not self.success:
return createState(StateType.WrongBiometricsPassword, self.flowType, nil)
method executeTertiaryCommand*(self: EnterBiometricsPasswordState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setPassword("")
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -0,0 +1,34 @@
type
EnterPasswordState* = ref object of State
success: bool
proc newEnterPasswordState*(flowType: FlowType, backState: State): EnterPasswordState =
result = EnterPasswordState()
result.setup(flowType, StateType.EnterPassword, backState)
result.success = false
proc delete*(self: EnterPasswordState) =
self.State.delete
method executePrimaryCommand*(self: EnterPasswordState, controller: Controller) =
if self.flowType == FlowType.Authentication:
let password = controller.getPassword()
self.success = controller.verifyPassword(password)
if self.success:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
else:
controller.setKeycardData("wrong-pass")
method getNextPrimaryState*(self: EnterPasswordState, controller: Controller): State =
if self.flowType == FlowType.Authentication:
if not self.success:
return createState(StateType.WrongPassword, self.flowType, nil)
method executeSecondaryCommand*(self: EnterPasswordState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.tryToObtainDataFromKeychain()
method executeTertiaryCommand*(self: EnterPasswordState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setPassword("")
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -12,18 +12,25 @@ method getNextPrimaryState*(self: EnterPinState, controller: Controller): State
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard:
return createState(StateType.FactoryResetConfirmation, self.flowType, self)
return nil
if self.flowType == FlowType.Authentication:
if controller.getPin().len == PINLengthForStatusApp:
controller.enterKeycardPin(controller.getPin())
method executeSecondaryCommand*(self: EnterPinState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
if self.flowType == FlowType.Authentication:
controller.setUsePinFromBiometrics(false)
controller.tryToObtainDataFromKeychain()
method executeTertiaryCommand*(self: EnterPinState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.FactoryReset:
if controller.getPin().len == PINLengthForStatusApp:
controller.enterKeycardPin(controller.getPin())
if self.flowType == FlowType.Authentication:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: EnterPinState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
@ -33,7 +40,7 @@ method resolveKeycardNextState*(self: EnterPinState, keycardFlowType: string, ke
if self.flowType == FlowType.FactoryReset:
if keycardFlowType == ResponseTypeValueEnterPIN and
keycardEvent.error.len > 0 and
keycardEvent.error == RequestParamPIN:
keycardEvent.error == ErrorPIN:
controller.setKeycardData($keycardEvent.pinRetries)
if keycardEvent.pinRetries > 0:
return createState(StateType.WrongPin, self.flowType, nil)
@ -51,7 +58,7 @@ method resolveKeycardNextState*(self: EnterPinState, keycardFlowType: string, ke
if self.flowType == FlowType.SetupNewKeycard:
if keycardFlowType == ResponseTypeValueEnterPIN and
keycardEvent.error.len > 0 and
keycardEvent.error == RequestParamPIN:
keycardEvent.error == ErrorPIN:
controller.setKeycardData($keycardEvent.pinRetries)
if keycardEvent.pinRetries > 0:
return createState(StateType.WrongPin, self.flowType, nil)
@ -66,3 +73,24 @@ method resolveKeycardNextState*(self: EnterPinState, keycardFlowType: string, ke
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setMetadataFromKeycard(keycardEvent.cardMetadata)
return createState(StateType.PinVerified, self.flowType, nil)
if self.flowType == FlowType.Authentication:
if keycardFlowType == ResponseTypeValueEnterPIN and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorPIN:
controller.setKeycardData($keycardEvent.pinRetries)
if keycardEvent.pinRetries > 0:
if controller.loggedInUserUsesBiometricLogin() and not controller.usePinFromBiometrics():
return createState(StateType.WrongKeychainPin, self.flowType, nil)
return createState(StateType.WrongPin, self.flowType, nil)
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueEnterPIN and
keycardEvent.error.len == 0:
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueEnterPUK and
keycardEvent.error.len == 0:
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
if keycardEvent.error.len == 0:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
return nil

View File

@ -13,9 +13,7 @@ proc delete*(self: EnterSeedPhraseState) =
method executePrimaryCommand*(self: EnterSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:
self.verifiedSeedPhrase = controller.validSeedPhrase(controller.getSeedPhrase()) and
(not controller.getSelectedKeyPairIsProfile() or
controller.getSelectedKeyPairIsProfile() and
controller.seedPhraseRefersToLoggedInUser(controller.getSeedPhrase()))
controller.seedPhraseRefersToSelectedKeyPair(controller.getSeedPhrase())
if self.verifiedSeedPhrase:
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
else:
@ -38,5 +36,4 @@ method resolveKeycardNextState*(self: EnterSeedPhraseState, keycardFlowType: str
if self.flowType == FlowType.SetupNewKeycard:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.keyUid.len > 0:
controller.setKeyUid(keycardEvent.keyUid)
return createState(StateType.MigratingKeyPair, self.flowType, nil)

View File

@ -18,6 +18,10 @@ method executePrimaryCommand*(self: InsertKeycardState, controller: Controller)
self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method executeTertiaryCommand*(self: InsertKeycardState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: InsertKeycardState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
let state = ensureReaderAndCardPresenceAndResolveNextState(self, keycardFlowType, keycardEvent, controller)

View File

@ -12,3 +12,7 @@ method executePrimaryCommand*(self: KeycardEmptyState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method executeTertiaryCommand*(self: KeycardEmptyState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -24,3 +24,7 @@ method executePrimaryCommand*(self: KeycardInsertedState, controller: Controller
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method executeTertiaryCommand*(self: KeycardInsertedState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -0,0 +1,17 @@
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 executeSecondaryCommand*(self: KeycardLockedState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -11,10 +11,17 @@ proc delete*(self: MaxPinRetriesReachedState) =
method getNextPrimaryState*(self: MaxPinRetriesReachedState, controller: Controller): State =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard:
debug "Run Unlock Keycard flow... (not developed yet)"
return createState(StateType.FactoryResetConfirmation, self.flowType, self)
if self.flowType == FlowType.Authentication:
debug "Run Unlock Keycard flow... (not developed yet)"
return nil
method executeSecondaryCommand*(self: MaxPinRetriesReachedState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method executeTertiaryCommand*(self: MaxPinRetriesReachedState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -10,19 +10,37 @@ proc newMigratingKeyPairState*(flowType: FlowType, backState: State): MigratingK
proc delete*(self: MigratingKeyPairState) =
self.State.delete
method executePrimaryCommand*(self: MigratingKeyPairState, controller: Controller) =
method procDoMigration(self: MigratingKeyPairState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:
# Ran authentication popup and get pass from there...
let password = controller.getPassword()
self.migrationSuccess = controller.verifyPassword(password)
controller.setPassword("")
if controller.getSelectedKeyPairIsProfile():
self.migrationSuccess = self.migrationSuccess and controller.convertToKeycardAccount(password)
self.migrationSuccess = controller.verifyPassword(password)
if not self.migrationSuccess:
return
let selectedKeyPairDto = controller.getSelectedKeyPairDto()
self.migrationSuccess = controller.addMigratedKeyPair(selectedKeyPairDto)
if not self.migrationSuccess:
return
controller.runStoreMetadataFlow(controller.getSelectedKeyPairName(), controller.getPin(),
if controller.getSelectedKeyPairIsProfile():
self.migrationSuccess = self.migrationSuccess and controller.convertSelectedKeyPairToKeycardAccount(password)
if not self.migrationSuccess:
return
controller.runStoreMetadataFlow(selectedKeyPairDto.keypairName, controller.getPin(),
controller.getSelectedKeyPairWalletPaths())
method getNextPrimaryState*(self: MigratingKeyPairState, controller: Controller): State =
method executePrimaryCommand*(self: MigratingKeyPairState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:
if controller.getSelectedKeyPairIsProfile():
controller.authenticateUser()
else:
self.procDoMigration(controller)
method executeSecondaryCommand*(self: MigratingKeyPairState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:
self.procDoMigration(controller)
method getNextSecondaryState*(self: MigratingKeyPairState, controller: Controller): State =
if self.flowType == FlowType.SetupNewKeycard:
if not self.migrationSuccess:
return createState(StateType.KeyPairMigrateFailure, self.flowType, nil)

View File

@ -12,3 +12,7 @@ method executePrimaryCommand*(self: NotKeycardState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method executeTertiaryCommand*(self: NotKeycardState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -18,6 +18,10 @@ method executePrimaryCommand*(self: PluginReaderState, controller: Controller) =
self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method executeTertiaryCommand*(self: PluginReaderState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: PluginReaderState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
return ensureReaderAndCardPresenceAndResolveNextState(self, keycardFlowType, keycardEvent, controller)

View File

@ -18,6 +18,10 @@ method executePrimaryCommand*(self: ReadingKeycardState, controller: Controller)
self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method executeTertiaryCommand*(self: ReadingKeycardState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method getNextSecondaryState*(self: ReadingKeycardState, controller: Controller): State =
let (flowType, flowEvent) = controller.getLastReceivedKeycardData()
# this is used in case a keycard is not inserted in the moment when flow is run (we're animating an insertion)

View File

@ -31,4 +31,5 @@ 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)

View File

@ -26,6 +26,5 @@ method resolveKeycardNextState*(self: SeedPhraseEnterWordsState, keycardFlowType
if self.flowType == FlowType.SetupNewKeycard:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.keyUid.len > 0:
controller.setKeyUid(keycardEvent.keyUid)
controller.removeMnemonic()
return createState(StateType.MigratingKeyPair, self.flowType, nil)

View File

@ -16,6 +16,7 @@ type StateType* {.pure.} = enum
PinVerified = "PinVerified"
EnterPin = "EnterPin"
WrongPin = "WrongPin"
WrongKeychainPin = "WrongKeychainPin"
MaxPinRetriesReached = "MaxPinRetriesReached"
FactoryResetConfirmation = "FactoryResetConfirmation"
FactoryResetConfirmationDisplayMetadata = "FactoryResetConfirmationDisplayMetadata"
@ -24,7 +25,9 @@ type StateType* {.pure.} = enum
KeycardMetadataDisplay = "KeycardMetadataDisplay"
KeycardEmpty = "KeycardEmpty"
KeycardNotEmpty = "KeycardNotEmpty"
KeycardLocked = "KeycardLocked"
NotKeycard = "NotKeycard"
WrongKeycard = "WrongKeycard"
RecognizedKeycard = "RecognizedKeycard"
SelectExistingKeyPair = "SelectExistingKeyPair"
EnterSeedPhrase = "EnterSeedPhrase"
@ -34,6 +37,14 @@ type StateType* {.pure.} = enum
KeyPairMigrateSuccess = "KeyPairMigrateSuccess"
KeyPairMigrateFailure = "KeyPairMigrateFailure"
MigratingKeyPair = "MigratingKeyPair"
EnterPassword = "EnterPassword"
WrongPassword = "WrongPassword"
BiometricsPasswordFailed = "BiometricsPasswordFailed"
BiometricsPinFailed = "BiometricsPinFailed"
BiometricsPinInvalid = "BiometricsPinInvalid"
EnterBiometricsPassword = "EnterBiometricsPassword"
WrongBiometricsPassword = "WrongBiometricsPassword"
BiometricsReadyToSign = "BiometricsReadyToSign"
## This is the base class for all state we may have in onboarding/login flow.

View File

@ -21,7 +21,13 @@ proc getPredefinedKeycardData*(currValue: string, value: PredefinedKeycardData,
proc ensureReaderAndCardPresence*(state: State, keycardFlowType: string, keycardEvent: KeycardEvent, controller: Controller): State
proc ensureReaderAndCardPresenceAndResolveNextState*(state: State, keycardFlowType: string, keycardEvent: KeycardEvent, controller: Controller): State
include biometrics_password_failed_state
include biometrics_pin_failed_state
include biometrics_pin_invalid_state
include biometrics_ready_to_sign_state
include create_pin_state
include enter_biometrics_password_state
include enter_password_state
include enter_pin_state
include enter_seed_phrase_state
include factory_reset_confirmation_displayed_metadata_state
@ -35,6 +41,7 @@ include keycard_empty_state
include keycard_inserted_state
include keycard_metadata_display_state
include keycard_not_empty_state
include keycard_locked_state
include max_pin_retries_reached_state
include migrating_key_pair_state
include not_keycard_state
@ -47,7 +54,11 @@ include repeat_pin_state
include seed_phrase_display_state
include seed_phrase_enter_words_state
include select_existing_key_pair_state
include wrong_biometrics_password_state
include wrong_keycard_state
include wrong_password_state
include wrong_pin_state
include wrong_keychain_pin_state
include wrong_seed_phrase_state
proc getPredefinedKeycardData*(currValue: string, value: PredefinedKeycardData, add: bool): string =
@ -67,8 +78,20 @@ proc getPredefinedKeycardData*(currValue: string, value: PredefinedKeycardData,
return if add: $(value.int) else: ""
proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: State): State =
if stateToBeCreated == StateType.BiometricsPasswordFailed:
return newBiometricsPasswordFailedState(flowType, backState)
if stateToBeCreated == StateType.BiometricsPinFailed:
return newBiometricsPinFailedState(flowType, backState)
if stateToBeCreated == StateType.BiometricsPinInvalid:
return newBiometricsPinInvalidState(flowType, backState)
if stateToBeCreated == StateType.BiometricsReadyToSign:
return newBiometricsReadyToSignState(flowType, backState)
if stateToBeCreated == StateType.CreatePin:
return newCreatePinState(flowType, backState)
if stateToBeCreated == StateType.EnterBiometricsPassword:
return newEnterBiometricsPasswordState(flowType, backState)
if stateToBeCreated == StateType.EnterPassword:
return newEnterPasswordState(flowType, backState)
if stateToBeCreated == StateType.EnterPin:
return newEnterPinState(flowType, backState)
if stateToBeCreated == StateType.EnterSeedPhrase:
@ -95,6 +118,8 @@ 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.MaxPinRetriesReached:
return newMaxPinRetriesReachedState(flowType, backState)
if stateToBeCreated == StateType.MigratingKeyPair:
@ -119,32 +144,41 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St
return newSeedPhraseEnterWordsState(flowType, backState)
if stateToBeCreated == StateType.SelectExistingKeyPair:
return newSelectExistingKeyPairState(flowType, backState)
if stateToBeCreated == StateType.WrongBiometricsPassword:
return newWrongBiometricsPasswordState(flowType, backState)
if stateToBeCreated == StateType.WrongKeycard:
return newWrongKeycardState(flowType, backState)
if stateToBeCreated == StateType.WrongPassword:
return newWrongPasswordState(flowType, backState)
if stateToBeCreated == StateType.WrongPin:
return newWrongPinState(flowType, backState)
if stateToBeCreated == StateType.WrongKeychainPin:
return newWrongKeychainPinState(flowType, backState)
if stateToBeCreated == StateType.WrongSeedPhrase:
return newWrongSeedPhraseState(flowType, backState)
error "No implementation available for state ", state=stateToBeCreated
proc ensureReaderAndCardPresence*(state: State, keycardFlowType: string, keycardEvent: KeycardEvent, controller: Controller): State =
## Handling factory reset flow
if state.flowType == FlowType.FactoryReset:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorConnection:
controller.resumeCurrentFlowLater()
if state.stateType == StateType.PluginReader:
return nil
return createState(StateType.PluginReader, state.flowType, nil)
if keycardFlowType == ResponseTypeValueInsertCard and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorConnection:
if state.stateType == StateType.InsertKeycard:
return nil
return createState(StateType.InsertKeycard, state.flowType, state)
if keycardFlowType == ResponseTypeValueCardInserted:
controller.setKeycardData(getPredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WronglyInsertedCard, add = false))
return createState(StateType.KeycardInserted, state.flowType, nil)
## Handling factory reset or authentication flow
if state.flowType == FlowType.FactoryReset or
state.flowType == FlowType.Authentication:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorConnection:
controller.resumeCurrentFlowLater()
if state.stateType == StateType.PluginReader:
return nil
return createState(StateType.PluginReader, state.flowType, nil)
if keycardFlowType == ResponseTypeValueInsertCard and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorConnection:
if state.stateType == StateType.InsertKeycard:
return nil
return createState(StateType.InsertKeycard, state.flowType, nil)
if keycardFlowType == ResponseTypeValueCardInserted:
controller.setKeycardData(getPredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WronglyInsertedCard, add = false))
return createState(StateType.KeycardInserted, state.flowType, nil)
## Handling setup new keycard flow
if state.flowType == FlowType.SetupNewKeycard:
@ -206,6 +240,8 @@ proc ensureReaderAndCardPresenceAndResolveNextState*(state: State, keycardFlowTy
return createState(StateType.NotKeycard, state.flowType, nil)
if keycardEvent.error == ErrorHasKeys:
return createState(StateType.KeycardNotEmpty, state.flowType, nil)
if keycardEvent.error == ErrorFreePairingSlots:
return createState(StateType.KeycardLocked, state.flowType, nil)
if keycardFlowType == ResponseTypeValueEnterPIN:
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.GetMetadata:
return createState(StateType.EnterPin, state.flowType, nil)
@ -229,3 +265,34 @@ proc ensureReaderAndCardPresenceAndResolveNextState*(state: State, keycardFlowTy
if state.stateType == StateType.SelectExistingKeyPair:
return createState(StateType.RecognizedKeycard, state.flowType, state)
return createState(StateType.RecognizedKeycard, state.flowType, state.getBackState)
## Handling authentiaction flow
if state.flowType == FlowType.Authentication:
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 keycardFlowType == ResponseTypeValueEnterPIN:
if keycardEvent.keyUid == controller.getKeyUidWhichIsBeingAuthenticating():
if controller.loggedInUserUsesBiometricLogin():
if keycardEvent.error.len > 0 and
keycardEvent.error == ErrorPIN:
controller.setKeycardData($keycardEvent.pinRetries)
if keycardEvent.pinRetries > 0:
if not controller.usePinFromBiometrics():
return createState(StateType.WrongKeychainPin, state.flowType, nil)
return createState(StateType.WrongPin, state.flowType, nil)
return createState(StateType.MaxPinRetriesReached, state.flowType, nil)
return createState(StateType.BiometricsReadyToSign, state.flowType, nil)
return createState(StateType.EnterPin, state.flowType, nil)
return createState(StateType.WrongKeycard, state.flowType, nil)
if keycardFlowType == ResponseTypeValueEnterPUK and
keycardEvent.error.len == 0:
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
return createState(StateType.MaxPinRetriesReached, state.flowType, nil)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
if keycardEvent.error.len == 0:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
return nil

View File

@ -0,0 +1,27 @@
type
WrongBiometricsPasswordState* = ref object of State
success: bool
proc newWrongBiometricsPasswordState*(flowType: FlowType, backState: State): WrongBiometricsPasswordState =
result = WrongBiometricsPasswordState()
result.setup(flowType, StateType.WrongBiometricsPassword, backState)
result.success = false
proc delete*(self: WrongBiometricsPasswordState) =
self.State.delete
method executePrimaryCommand*(self: WrongBiometricsPasswordState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setKeycardData("")
let password = controller.getPassword()
self.success = controller.verifyPassword(password)
if self.success:
controller.tryToStoreDataToKeychain(password)
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
else:
controller.setKeycardData("wrong-pass")
method executeTertiaryCommand*(self: WrongBiometricsPasswordState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setPassword("")
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -0,0 +1,13 @@
type
WrongKeycardState* = ref object of State
proc newWrongKeycardState*(flowType: FlowType, backState: State): WrongKeycardState =
result = WrongKeycardState()
result.setup(flowType, StateType.WrongKeycard, backState)
proc delete*(self: WrongKeycardState) =
self.State.delete
method executeTertiaryCommand*(self: WrongKeycardState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -0,0 +1,42 @@
type
WrongKeychainPinState* = ref object of State
proc newWrongKeychainPinState*(flowType: FlowType, backState: State): WrongKeychainPinState =
result = WrongKeychainPinState()
result.setup(flowType, StateType.WrongKeychainPin, backState)
proc delete*(self: WrongKeychainPinState) =
self.State.delete
method getNextPrimaryState*(self: WrongKeychainPinState, controller: Controller): State =
if self.flowType == FlowType.Authentication:
if controller.getPin().len == PINLengthForStatusApp:
controller.enterKeycardPin(controller.getPin())
return nil
method executeTertiaryCommand*(self: WrongKeychainPinState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: WrongKeychainPinState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
let state = ensureReaderAndCardPresence(self, keycardFlowType, keycardEvent, controller)
if not state.isNil:
return state
if self.flowType == FlowType.Authentication:
if keycardFlowType == ResponseTypeValueEnterPIN and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorPIN:
controller.setKeycardData($keycardEvent.pinRetries)
if keycardEvent.pinRetries > 0:
return self
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueEnterPUK and
keycardEvent.error.len == 0:
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
if keycardEvent.error.len == 0:
controller.tryToStoreDataToKeychain(controller.getPin())
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
return nil

View File

@ -0,0 +1,30 @@
type
WrongPasswordState* = ref object of State
success: bool
proc newWrongPasswordState*(flowType: FlowType, backState: State): WrongPasswordState =
result = WrongPasswordState()
result.setup(flowType, StateType.WrongPassword, backState)
result.success = false
proc delete*(self: WrongPasswordState) =
self.State.delete
method executePrimaryCommand*(self: WrongPasswordState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setKeycardData("")
let password = controller.getPassword()
self.success = controller.verifyPassword(password)
if self.success:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
else:
controller.setKeycardData("wrong-pass")
method executeSecondaryCommand*(self: WrongPasswordState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.tryToObtainDataFromKeychain()
method executeTertiaryCommand*(self: WrongPasswordState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.setPassword("")
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -12,18 +12,26 @@ method getNextPrimaryState*(self: WrongPinState, 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:
if controller.getPin().len == PINLengthForStatusApp:
controller.enterKeycardPin(controller.getPin())
return nil
method executeSecondaryCommand*(self: WrongPinState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
if self.flowType == FlowType.Authentication:
controller.setUsePinFromBiometrics(false)
controller.tryToObtainDataFromKeychain()
method executeTertiaryCommand*(self: WrongPinState, controller: Controller) =
if self.flowType == FlowType.FactoryReset or
self.flowType == FlowType.SetupNewKeycard:
if controller.getPin().len == PINLengthForStatusApp:
controller.enterKeycardPin(controller.getPin())
if self.flowType == FlowType.Authentication:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method resolveKeycardNextState*(self: WrongPinState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
@ -33,7 +41,7 @@ method resolveKeycardNextState*(self: WrongPinState, keycardFlowType: string, ke
if self.flowType == FlowType.FactoryReset:
if keycardFlowType == ResponseTypeValueEnterPIN and
keycardEvent.error.len > 0 and
keycardEvent.error == RequestParamPIN:
keycardEvent.error == ErrorPIN:
controller.setKeycardData($keycardEvent.pinRetries)
if keycardEvent.pinRetries > 0:
return self
@ -48,7 +56,7 @@ method resolveKeycardNextState*(self: WrongPinState, keycardFlowType: string, ke
if self.flowType == FlowType.SetupNewKeycard:
if keycardFlowType == ResponseTypeValueEnterPIN and
keycardEvent.error.len > 0 and
keycardEvent.error == RequestParamPIN:
keycardEvent.error == ErrorPIN:
controller.setKeycardData($keycardEvent.pinRetries)
if keycardEvent.pinRetries > 0:
return self
@ -60,3 +68,19 @@ method resolveKeycardNextState*(self: WrongPinState, keycardFlowType: string, ke
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setMetadataFromKeycard(keycardEvent.cardMetadata)
return createState(StateType.PinVerified, self.flowType, nil)
if self.flowType == FlowType.Authentication:
if keycardFlowType == ResponseTypeValueEnterPIN and
keycardEvent.error.len > 0 and
keycardEvent.error == ErrorPIN:
controller.setKeycardData($keycardEvent.pinRetries)
if keycardEvent.pinRetries > 0:
return self
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueEnterPUK and
keycardEvent.error.len == 0:
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
return createState(StateType.MaxPinRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
if keycardEvent.error.len == 0:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
return nil

View File

@ -17,7 +17,7 @@ method executePrimaryCommand*(self: WrongSeedPhraseState, controller: Controller
controller.setKeycardData(getPredefinedKeycardData(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.seedPhraseRefersToLoggedInUser(controller.getSeedPhrase())
controller.seedPhraseRefersToSelectedKeyPair(controller.getSeedPhrase())
if self.verifiedSeedPhrase:
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
else:
@ -35,6 +35,5 @@ method resolveKeycardNextState*(self: WrongSeedPhraseState, keycardFlowType: str
if self.flowType == FlowType.SetupNewKeycard:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.keyUid.len > 0:
controller.setKeyUid(keycardEvent.keyUid)
controller.setKeycardData(getPredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
return createState(StateType.MigratingKeyPair, self.flowType, nil)

View File

@ -66,7 +66,7 @@ method onTertiaryActionClicked*(self: AccessInterface) {.base.} =
method onKeycardResponse*(self: AccessInterface, keycardFlowType: string, keycardEvent: KeycardEvent) {.base.} =
raise newException(ValueError, "No implementation available")
method runFlow*(self: AccessInterface, flowToRun: FlowType) {.base.} =
method runFlow*(self: AccessInterface, flowToRun: FlowType, keyUid = "", bip44Path = "", txHash = "") {.base.} =
raise newException(ValueError, "No implementation available")
method setPin*(self: AccessInterface, value: string) {.base.} =
@ -105,6 +105,17 @@ method migratingProfileKeyPair*(self: AccessInterface): bool {.base.} =
method isProfileKeyPairMigrated*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method getSigningPhrase*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} =
raise newException(ValueError, "No implementation available")
method keychainObtainedDataFailure*(self: AccessInterface, errorDescription: string, errorType: string) {.base.} =
raise newException(ValueError, "No implementation available")
method keychainObtainedDataSuccess*(self: AccessInterface, data: string) {.base.} =
raise newException(ValueError, "No implementation available")
type
DelegateInterface* = concept c

View File

@ -20,6 +20,8 @@ type
type
KeyPairItem* = ref object of RootObj
pubKey: string
keyUid: string
locked: bool
name: string
image: string
icon: string
@ -28,15 +30,18 @@ type
accounts: seq[WalletAccountDetails]
proc initKeyPairItem*(
pubKey: string,
name: string,
image: string,
icon: string,
pairType: KeyPairType,
derivedFrom: string
pubKey = "",
keyUid = "",
locked = false,
name = "",
image = "",
icon = "",
pairType = KeyPairType.Unknown,
derivedFrom = ""
): KeyPairItem =
result = KeyPairItem()
result.pubKey = pubKey
result.keyUid = keyUid
result.name = name
result.image = image
result.icon = icon
@ -56,18 +61,38 @@ proc `$`*(self: KeyPairItem): string =
proc pubKey*(self: KeyPairItem): string {.inline.} =
self.pubKey
proc setPubKey*(self: KeyPairItem, value: string) {.inline.} =
self.pubKey = value
proc keyUid*(self: KeyPairItem): string {.inline.} =
self.keyUid
proc setKeyUid*(self: KeyPairItem, value: string) {.inline.} =
self.keyUid = value
proc locked*(self: KeyPairItem): bool {.inline.} =
self.locked
proc setLocked*(self: KeyPairItem, value: bool) {.inline.} =
self.locked = value
proc name*(self: KeyPairItem): string {.inline.} =
self.name
proc setName*(self: KeyPairItem, value: string) {.inline.} =
self.name = value
proc image*(self: KeyPairItem): string {.inline.} =
self.image
proc setImage*(self: KeyPairItem, value: string) {.inline.} =
self.image = value
proc icon*(self: KeyPairItem): string {.inline.} =
self.icon
proc setIcon*(self: KeyPairItem, value: string) {.inline.} =
self.icon = value
proc pairType*(self: KeyPairItem): KeyPairType {.inline.} =
self.pairType
proc setPairType*(self: KeyPairItem, value: KeyPairType) {.inline.} =
self.pairType = value
proc derivedFrom*(self: KeyPairItem): string {.inline.} =
self.derivedFrom

View File

@ -4,6 +4,7 @@ import key_pair_item
type
ModelRole {.pure.} = enum
PubKey = UserRole + 1
Locked
Name
Image
Icon
@ -52,6 +53,7 @@ QtObject:
method roleNames(self: KeyPairModel): Table[int, string] =
{
ModelRole.PubKey.int: "pubKey",
ModelRole.Locked.int: "locked",
ModelRole.Name.int: "name",
ModelRole.Image.int: "image",
ModelRole.Icon.int: "icon",
@ -70,6 +72,8 @@ QtObject:
case enumRole:
of ModelRole.PubKey:
result = newQVariant(item.pubKey)
of ModelRole.Locked:
result = newQVariant(item.locked)
of ModelRole.Name:
result = newQVariant(item.name)
of ModelRole.Image:

View File

@ -13,10 +13,25 @@ QtObject:
result.QObject.setup
proc keyPairSelectedItemChanged*(self: KeyPairSelectedItem) {.signal.}
proc keyPairSelectedItemLockedChanged*(self: KeyPairSelectedItem) {.signal.}
proc keyPairSelectedItemIconChanged*(self: KeyPairSelectedItem) {.signal.}
proc setItem*(self: KeyPairSelectedItem, item: KeyPairItem) =
self.item = item
self.keyPairSelectedItemChanged()
self.keyPairSelectedItemLockedChanged()
self.keyPairSelectedItemIconChanged()
proc updateLockedState*(self: KeyPairSelectedItem, locked: bool) =
if self.item.isNil:
return
self.item.setLocked(locked)
if locked:
self.item.setIcon("lock")
else:
self.item.setIcon("keycard")
self.keyPairSelectedItemLockedChanged()
self.keyPairSelectedItemIconChanged()
proc getPubKey*(self: KeyPairSelectedItem): string {.slot.} =
if(self.item.isNil):
@ -26,6 +41,14 @@ QtObject:
read = getPubKey
notify = keyPairSelectedItemChanged
proc getLocked*(self: KeyPairSelectedItem): bool {.slot.} =
if(self.item.isNil):
return false
return self.item.locked()
QtProperty[bool] locked:
read = getLocked
notify = keyPairSelectedItemLockedChanged
proc getName*(self: KeyPairSelectedItem): string {.slot.} =
if(self.item.isNil):
return ""
@ -48,7 +71,7 @@ QtObject:
return self.item.icon()
QtProperty[string] icon:
read = getIcon
notify = keyPairSelectedItemChanged
notify = keyPairSelectedItemIconChanged
proc getPairType*(self: KeyPairSelectedItem): int {.slot.} =
if(self.item.isNil):

View File

@ -48,6 +48,10 @@ proc newModule*[T](delegate: T,
result.initialized = false
result.authenticationPopupIsAlreadyRunning = false
## Forward declaration
proc updateKeyPairItemIfDataAreKnown[T](self: Module[T], address: string, item: var KeyPairItem): bool
proc generateRandomColor[T](self: Module[T]): string
method delete*[T](self: Module[T]) =
self.view.delete
self.viewVariant.delete
@ -104,6 +108,14 @@ method migratingProfileKeyPair*[T](self: Module[T]): bool =
method isProfileKeyPairMigrated*[T](self: Module[T]): bool =
return self.controller.getLoggedInAccount().keycardPairing.len > 0
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
if currFlowType == FlowType.Authentication:
self.view.setLockedPropForKeyPairForAuthentication(nextStateType == StateType.MaxPinRetriesReached)
method onBackActionClicked*[T](self: Module[T]) =
let currStateObj = self.view.currentStateObj()
if currStateObj.isNil:
@ -112,6 +124,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.view.setCurrentState(backState)
debug "sm_back_action - set state", setCurrFlow=backState.flowType(), newCurrState=backState.stateType()
currStateObj.delete()
@ -126,6 +139,7 @@ method onPrimaryActionClicked*[T](self: Module[T]) =
let nextState = currStateObj.getNextPrimaryState(self.controller)
if nextState.isNil:
return
self.authenticationKeyPairUpdate(nextState.flowType(), nextState.stateType())
self.view.setCurrentState(nextState)
debug "sm_primary_action - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
@ -139,6 +153,7 @@ method onSecondaryActionClicked*[T](self: Module[T]) =
let nextState = currStateObj.getNextSecondaryState(self.controller)
if nextState.isNil:
return
self.authenticationKeyPairUpdate(nextState.flowType(), nextState.stateType())
self.view.setCurrentState(nextState)
debug "sm_secondary_action - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
@ -152,6 +167,7 @@ method onTertiaryActionClicked*[T](self: Module[T]) =
let nextState = currStateObj.getNextTertiaryState(self.controller)
if nextState.isNil:
return
self.authenticationKeyPairUpdate(nextState.flowType(), nextState.stateType())
self.view.setCurrentState(nextState)
debug "sm_tertiary_action - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
@ -164,6 +180,7 @@ method onKeycardResponse*[T](self: Module[T], keycardFlowType: string, keycardEv
let nextState = self.tmpLocalState.resolveKeycardNextState(keycardFlowType, keycardEvent, self.controller)
if nextState.isNil:
return
self.authenticationKeyPairUpdate(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()
@ -172,10 +189,18 @@ method onKeycardResponse*[T](self: Module[T], keycardFlowType: string, keycardEv
let nextState = currStateObj.resolveKeycardNextState(keycardFlowType, keycardEvent, self.controller)
if nextState.isNil:
return
self.authenticationKeyPairUpdate(nextState.flowType(), nextState.stateType())
self.view.setCurrentState(nextState)
debug "sm_on_keycard_response - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
proc prepareKeyPairsModel[T](self: Module[T]) =
proc buildKeyPairsList[T](self: Module[T]): seq[KeyPairItem] =
let keyPairMigrated = proc(keyUid: string): bool =
result = false
let migratedKeyPairs = self.controller.getAllMigratedKeyPairs()
for kp in migratedKeyPairs:
if kp.keyUid == keyUid:
return true
let findItemByDerivedFromAddress = proc(items: seq[KeyPairItem], address: string): KeyPairItem =
if address.len == 0:
return nil
@ -193,7 +218,7 @@ proc prepareKeyPairsModel[T](self: Module[T]) =
let accounts = self.controller.getWalletAccounts()
var items: seq[KeyPairItem]
for a in accounts:
if a.isChat or a.walletType == WalletTypeWatch:
if a.isChat or a.walletType == WalletTypeWatch or keyPairMigrated(a.keyUid):
continue
var item = findItemByDerivedFromAddress(items, a.derivedfrom)
if a.walletType == WalletTypeDefaultStatusAccount or a.walletType == WalletTypeGenerated:
@ -201,6 +226,8 @@ proc prepareKeyPairsModel[T](self: Module[T]) =
continue
if item.isNil:
item = initKeyPairItem(pubKey = a.publicKey,
keyUid = a.keyUid,
locked = false,
name = singletonInstance.userProfile.getName(),
image = singletonInstance.userProfile.getIcon(),
icon = "",
@ -216,6 +243,8 @@ proc prepareKeyPairsModel[T](self: Module[T]) =
let diffImports = countOfKeyPairsForType(items, KeyPairType.SeedImport)
if item.isNil:
item = initKeyPairItem(pubKey = a.publicKey,
keyUid = a.keyUid,
locked = false,
name = "Seed Phrase " & $(diffImports + 1), # string created here should be transalted, but so far it's like it is
image = "",
icon = "key_pair_seed_phrase",
@ -228,43 +257,86 @@ proc prepareKeyPairsModel[T](self: Module[T]) =
let diffImports = countOfKeyPairsForType(items, KeyPairType.PrivateKeyImport)
if item.isNil:
item = initKeyPairItem(pubKey = a.publicKey,
keyUid = a.keyUid,
locked = false,
name = "Key " & $(diffImports + 1), # string created here should be transalted, but so far it's like it is
image = "",
icon = "key_pair_private_key",
pairType = KeyPairType.SeedImport,
pairType = KeyPairType.PrivateKeyImport,
derivedFrom = a.derivedfrom)
items.add(item)
item.addAccount(a.name, a.path, a.address, a.emoji, a.color, icon = "", balance = 0.0)
continue
if items.len == 0:
debug "sm_there is no any key pair for the logged in user that is not already migrated to a keycard"
self.view.createKeyPairModel(items)
return items
method runFlow*[T](self: Module[T], flowToRun: FlowType) =
proc prepareKeyPairItemForAuthentication[T](self: Module[T], keyUid: string) =
var item = initKeyPairItem()
self.view.createKeyPairForAuthentication()
let items = self.buildKeyPairsList()
for it in items:
if it.keyUid == keyUid:
item = it
break
if item.name.len == 0:
error "sm_cannot find keypair among known keypairs for the given keyUid", keyUid=keyUid
item.setPubKey("")
item.setImage("")
item.setIcon("keycard")
item.setPairType(KeyPairType.Unknown)
self.view.setKeyPairForAuthentication(item)
method runFlow*[T](self: Module[T], flowToRun: FlowType, keyUid = "", bip44Path = "", txHash = "") =
## In case of `Authentication` if we're signing a transaction we need to provide a key uid of a keypair that an account
## we want to sign a transaction for belongs to. If we're just doing an authentication for a logged in user, then
## default key uid is always the key uid of the logged in user.
if flowToRun == FlowType.General:
self.controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
error "sm_cannot run an general flow"
return
if not self.initialized:
self.initialized = true
self.controller.init()
if flowToRun == FlowType.FactoryReset:
self.prepareKeyPairsModel()
let items = self.buildKeyPairsList()
self.view.createKeyPairModel(items)
self.tmpLocalState = newReadingKeycardState(flowToRun, nil)
self.controller.runGetMetadataFlow()
return
if flowToRun == FlowType.SetupNewKeycard:
self.prepareKeyPairsModel()
let items = self.buildKeyPairsList()
self.view.createKeyPairModel(items)
self.view.setCurrentState(newSelectExistingKeyPairState(flowToRun, nil))
self.controller.readyToDisplayPopup()
return
if flowToRun == FlowType.Authentication:
self.controller.connectKeychainSignals()
if keyUid.len > 0:
self.prepareKeyPairItemForAuthentication(keyUid)
self.tmpLocalState = newReadingKeycardState(flowToRun, nil)
self.controller.runSignFlow(keyUid, bip44Path, txHash)
return
if self.controller.loggedInUserUsesBiometricLogin():
self.controller.tryToObtainDataFromKeychain()
return
self.view.setCurrentState(newEnterPasswordState(flowToRun, nil))
self.authenticationPopupIsAlreadyRunning = true
self.controller.readyToDisplayPopup()
return
method setSelectedKeyPair*[T](self: Module[T], item: KeyPairItem) =
var paths: seq[string]
var keyPairDto = KeyPairDto(keycardUid: "", # will be set during migration
keypairName: item.name,
keycardLocked: item.locked,
keyUid: item.keyUid)
for a in item.accountsAsArr():
paths.add(a.path)
keyPairDto.accountsAddresses.add(a.address)
self.controller.setSelectedKeyPairIsProfile(item.pairType == KeyPairType.Profile)
self.controller.setSelectedKeyPairName(item.name)
self.controller.setSelectedKeyPairWalletPaths(paths)
self.controller.setSelectedKeyPairDto(keyPairDto)
proc generateRandomColor[T](self: Module[T]): string =
let r = rand(0 .. 255)
@ -286,6 +358,8 @@ proc updateKeyPairItemIfDataAreKnown[T](self: Module[T], address: string, item:
method setKeyPairStoredOnKeycard*[T](self: Module[T], cardMetadata: CardMetadata) =
var item = initKeyPairItem(pubKey = "",
keyUid = "",
locked = false,
name = cardMetadata.name,
image = "",
icon = "keycard",
@ -301,3 +375,44 @@ method setKeyPairStoredOnKeycard*[T](self: Module[T], cardMetadata: CardMetadata
self.view.setKeyPairStoredOnKeycardIsKnown(knownKeyPair)
self.view.setKeyPairStoredOnKeycard(item)
method onUserAuthenticated*[T](self: Module[T], password: string) =
let currStateObj = self.view.currentStateObj()
if not currStateObj.isNil and currStateObj.flowType() == FlowType.SetupNewKeycard:
self.controller.setPassword(password)
self.onSecondaryActionClicked()
method keychainObtainedDataFailure*[T](self: Module[T], errorDescription: string, errorType: string) =
let currStateObj = self.view.currentStateObj()
if currStateObj.isNil or
currStateObj.stateType() == StateType.EnterPassword or
currStateObj.stateType() == StateType.WrongPassword or
currStateObj.stateType() == StateType.BiometricsPasswordFailed:
self.view.setCurrentState(newBiometricsPasswordFailedState(FlowType.Authentication, nil))
if not self.authenticationPopupIsAlreadyRunning:
self.authenticationPopupIsAlreadyRunning = true
self.controller.readyToDisplayPopup()
return
if not currStateObj.isNil:
self.view.setCurrentState(newBiometricsPinFailedState(FlowType.Authentication, nil))
method keychainObtainedDataSuccess*[T](self: Module[T], data: string) =
let currStateObj = self.view.currentStateObj()
if currStateObj.isNil or
currStateObj.stateType() == StateType.EnterPassword or
currStateObj.stateType() == StateType.WrongPassword or
currStateObj.stateType() == StateType.BiometricsPasswordFailed:
if self.controller.verifyPassword(data):
self.controller.setPassword(data)
self.controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
else:
self.view.setCurrentState(newEnterBiometricsPasswordState(FlowType.Authentication, nil))
if not self.authenticationPopupIsAlreadyRunning:
self.authenticationPopupIsAlreadyRunning = true
self.controller.readyToDisplayPopup()
return
if not currStateObj.isNil:
if data.len == PINLengthForStatusApp:
self.controller.enterKeycardPin(data)
else:
self.view.setCurrentState(newBiometricsPinInvalidState(FlowType.Authentication, nil))

View File

@ -16,6 +16,8 @@ QtObject:
keyPairStoredOnKeycardIsKnown: bool
keyPairStoredOnKeycard: KeyPairSelectedItem
keyPairStoredOnKeycardVariant: QVariant
keyPairForAuthentication: KeyPairSelectedItem
keyPairForAuthenticationVariant: QVariant
keycardData: string # used to temporary store the data coming from keycard, depends on current state different data may be stored
proc delete*(self: View) =
@ -33,6 +35,10 @@ QtObject:
self.keyPairStoredOnKeycard.delete
if not self.keyPairStoredOnKeycardVariant.isNil:
self.keyPairStoredOnKeycardVariant.delete
if not self.keyPairForAuthentication.isNil:
self.keyPairForAuthentication.delete
if not self.keyPairForAuthenticationVariant.isNil:
self.keyPairForAuthenticationVariant.delete
self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface): View =
@ -58,7 +64,7 @@ QtObject:
read = getCurrentState
proc keycardDataChanged*(self: View) {.signal.}
proc setKeycardData*(self: View, value: string) =
proc setKeycardData*(self: View, value: string) {.slot.} =
if self.keycardData == value:
return
self.keycardData = value
@ -67,6 +73,7 @@ QtObject:
return self.keycardData
QtProperty[string] keycardData:
read = getKeycardData
write = setKeycardData
notify = keycardDataChanged
proc onBackActionClicked*(self: View) {.slot.} =
@ -86,6 +93,8 @@ QtObject:
proc keyPairModelChanged(self: View) {.signal.}
proc getKeyPairModel(self: View): QVariant {.slot.} =
if self.keyPairModelVariant.isNil:
return newQVariant()
return self.keyPairModelVariant
QtProperty[QVariant] keyPairModel:
read = getKeyPairModel
@ -108,6 +117,8 @@ QtObject:
self.keyPairModelChanged()
proc getSelectedKeyPairItem*(self: View): QVariant {.slot.} =
if self.selectedKeyPairItemVariant.isNil:
return newQVariant()
return self.selectedKeyPairItemVariant
QtProperty[QVariant] selectedKeyPairItem:
read = getSelectedKeyPairItem
@ -124,12 +135,33 @@ QtObject:
self.keyPairStoredOnKeycardIsKnown = value
proc getKeyPairStoredOnKeycard*(self: View): QVariant {.slot.} =
if self.keyPairStoredOnKeycardVariant.isNil:
return newQVariant()
return self.keyPairStoredOnKeycardVariant
QtProperty[QVariant] keyPairStoredOnKeycard:
read = getKeyPairStoredOnKeycard
proc setKeyPairStoredOnKeycard*(self: View, item: KeyPairItem) =
self.keyPairStoredOnKeycard.setItem(item)
proc createKeyPairForAuthentication*(self: View) =
if self.keyPairForAuthentication.isNil:
self.keyPairForAuthentication = newKeyPairSelectedItem()
if self.keyPairForAuthenticationVariant.isNil:
self.keyPairForAuthenticationVariant = newQVariant(self.keyPairForAuthentication)
proc getKeyPairForAuthentication*(self: View): QVariant {.slot.} =
if self.keyPairForAuthenticationVariant.isNil:
return newQVariant()
return self.keyPairForAuthenticationVariant
QtProperty[QVariant] keyPairForAuthentication:
read = getKeyPairForAuthentication
proc setKeyPairForAuthentication*(self: View, item: KeyPairItem) =
self.keyPairForAuthentication.setItem(item)
proc setLockedPropForKeyPairForAuthentication*(self: View, locked: bool) =
if self.keyPairForAuthentication.isNil:
return
self.keyPairForAuthentication.updateLockedState(locked)
proc setPin*(self: View, value: string) {.slot.} =
self.delegate.setPin(value)
@ -159,3 +191,6 @@ QtObject:
proc isProfileKeyPairMigrated*(self: View): bool {.slot.} =
return self.delegate.isProfileKeyPairMigrated()
proc getSigningPhrase*(self: View): string {.slot.} =
return self.delegate.getSigningPhrase()

View File

@ -125,7 +125,7 @@ proc init*(self: Controller) =
self.connectionIds.add(handlerId)
handlerId = self.events.onWithUUID(SIGNAL_SHARED_KEYCARD_MODULE_DISPLAY_POPUP) do(e: Args):
let args = SharedKeycarModuleArgs(e)
let args = SharedKeycarModuleBaseArgs(e)
if args.uniqueIdentifier != UNIQUE_STARTUP_MODULE_IDENTIFIER:
return
self.delegate.onDisplayKeycardSharedModuleFlow()

View File

@ -15,9 +15,6 @@ import shared.status 1.0
import shared.popups.keycard 1.0
import "../stores"
import "../controls"
import "../panels"
import "../popups"
SettingsContentBase {
id: root

View File

@ -22,6 +22,7 @@ import shared 1.0
import shared.controls 1.0
import shared.panels 1.0
import shared.popups 1.0
import shared.popups.keycard 1.0
import shared.status 1.0
import StatusQ.Core.Theme 0.1
@ -50,7 +51,16 @@ Item {
Connections {
target: rootStore.mainModuleInst
onDisplayUserProfile: Global.openProfilePopup(publicKey)
onDisplayKeycardSharedModuleFlow: {
keycardPopup.active = true
}
onDestroyKeycardSharedModuleFlow: {
keycardPopup.active = false
}
}
Connections {
@ -1177,4 +1187,16 @@ Item {
Global.settingsHasLoaded();
Global.errorSound = errorSound;
}
Loader {
id: keycardPopup
active: false
sourceComponent: KeycardPopup {
sharedKeycardModule: rootStore.mainModuleInst.keycardSharedModule
}
onLoaded: {
keycardPopup.item.open()
}
}
}

View File

@ -44,6 +44,9 @@ StatusModal {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.factoryReset) {
return qsTr("Factory reset a Keycard")
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
return qsTr("Authenticate")
}
return ""
}
@ -65,6 +68,7 @@ StatusModal {
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.createPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.repeatPin ||
@ -86,7 +90,7 @@ StatusModal {
return
}
}
else if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.factoryReset) {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.factoryReset) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.selectExistingKeyPair ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pinVerified ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin ||
@ -101,6 +105,31 @@ StatusModal {
return
}
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
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.enterPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPassword ||
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.enterBiometricsPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongBiometricsPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmpty ||
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.notKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard)
{
root.sharedKeycardModule.currentState.doTertiaryAction()
return
}
}
root.sharedKeycardModule.currentState.doPrimaryAction()
}
@ -120,10 +149,16 @@ 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.notKeycard ||
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.recognizedKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardMetadataDisplay)
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardMetadataDisplay ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPasswordFailed ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinFailed ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinInvalid)
{
return initComponent
}
@ -140,6 +175,7 @@ StatusModal {
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.repeatPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeychainPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pinSet ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pinVerified)
{
@ -158,6 +194,12 @@ StatusModal {
{
return enterSeedPhraseWordsComponent
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterBiometricsPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongBiometricsPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPassword) {
return passwordComponent
}
return undefined
}
@ -167,6 +209,10 @@ StatusModal {
id: initComponent
KeycardInit {
sharedKeycardModule: root.sharedKeycardModule
Component.onCompleted: {
d.primaryButtonEnabled = false
}
}
}
@ -204,6 +250,14 @@ StatusModal {
id: keycardPinComponent
KeycardPin {
sharedKeycardModule: root.sharedKeycardModule
Component.onCompleted: {
d.primaryButtonEnabled = false
}
onPinUpdated: {
d.primaryButtonEnabled = pin.length === Constants.keycard.general.keycardPinLength
}
}
}
@ -252,14 +306,29 @@ StatusModal {
}
}
}
Component {
id: passwordComponent
EnterPassword {
sharedKeycardModule: root.sharedKeycardModule
Component.onCompleted: {
d.primaryButtonEnabled = false
}
onPasswordValid: {
d.primaryButtonEnabled = valid
}
}
}
}
leftButtons: [
StatusBackButton {
id: backButton
visible: root.sharedKeycardModule.currentState.displayBackButton
height: primaryButton.height
width: primaryButton.height
height: Constants.keycard.general.footerButtonsHeight
width: height
onClicked: {
root.sharedKeycardModule.currentState.backAction()
}
@ -267,12 +336,72 @@ StatusModal {
]
rightButtons: [
StatusButton {
id: tertiaryButton
height: Constants.keycard.general.footerButtonsHeight
text: {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
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.keycardEmpty ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
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.notKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPassword ||
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.enterBiometricsPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongBiometricsPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin)
return qsTr("Cancel")
}
return ""
}
visible: {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
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.keycardEmpty ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
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.notKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPassword ||
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.enterBiometricsPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongBiometricsPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin)
return true
}
return false
}
onClicked: {
root.sharedKeycardModule.currentState.doTertiaryAction()
}
},
StatusButton {
id: secondaryButton
height: Constants.keycard.general.footerButtonsHeight
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.enterPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
@ -285,7 +414,7 @@ StatusModal {
return qsTr("Cancel")
}
}
else if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.factoryReset) {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.factoryReset) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pinVerified ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
@ -296,12 +425,36 @@ StatusModal {
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetConfirmation)
return qsTr("Cancel")
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
if (root.sharedKeycardModule.loggedInUserUsesBiometricLogin()) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPassword)
return qsTr("Use biometrics instead")
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPasswordFailed)
return qsTr("Use password instead")
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin)
return qsTr("Use biometrics")
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.biometricsReadyToSign ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.notKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinFailed ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinInvalid ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmpty)
return qsTr("Use PIN")
}
}
return ""
}
visible: {
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.enterPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
@ -314,7 +467,7 @@ StatusModal {
return true
}
}
else if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.factoryReset) {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.factoryReset) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pinVerified ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
@ -326,9 +479,42 @@ StatusModal {
return true
}
}
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
}
}
return false
}
highlighted: focus
enabled: {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
if (root.sharedKeycardModule.loggedInUserUsesBiometricLogin() &&
(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.notKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmpty))
return false
}
return true
}
onClicked: {
root.sharedKeycardModule.currentState.doSecondaryAction()
@ -336,6 +522,7 @@ StatusModal {
},
StatusButton {
id: primaryButton
height: Constants.keycard.general.footerButtonsHeight
text: {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.setupNewKeycard) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pluginReader ||
@ -362,13 +549,14 @@ StatusModal {
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin) {
return qsTr("I dont know the pin")
return qsTr("I dont know the PIN")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetConfirmation ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetConfirmationDisplayMetadata) {
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 ||
@ -377,22 +565,23 @@ StatusModal {
return qsTr("Next")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached) {
return qsTr("Tmp-Next")
return qsTr("Unlock Keycard")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.migratingKeyPair ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keyPairMigrateFailure) {
return qsTr("Done")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keyPairMigrateSuccess &&
root.sharedKeycardModule.migratingProfileKeyPair()) {
return qsTr("Restart app & sign in using your new Keycard")
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keyPairMigrateSuccess) {
if (root.sharedKeycardModule.migratingProfileKeyPair())
return qsTr("Restart app & sign in using your new Keycard")
return qsTr("Done")
}
return qsTr("Cancel")
}
else if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.factoryReset) {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.factoryReset) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin) {
return qsTr("I dont know the pin")
return qsTr("I dont know the PIN")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetConfirmation ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetConfirmationDisplayMetadata) {
@ -404,7 +593,7 @@ StatusModal {
return qsTr("Next")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached) {
return qsTr("Tmp-Next")
return qsTr("Unlock Keycard")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.readingKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetSuccess) {
@ -412,6 +601,37 @@ StatusModal {
}
return qsTr("Cancel")
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
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.keycardEmpty ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.notKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsReadyToSign ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin) {
return qsTr("Authenticate")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterBiometricsPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongBiometricsPassword) {
return qsTr("Update password & authenticate")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeychainPin) {
return qsTr("Update PIN & authenticate")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPasswordFailed ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinFailed ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinInvalid) {
return qsTr("Try biometrics again")
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached) {
return qsTr("Unlock Keycard")
}
}
return ""
}
enabled: {
@ -421,7 +641,7 @@ StatusModal {
return false
}
}
else if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.setupNewKeycard) {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.setupNewKeycard) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.selectExistingKeyPair ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetConfirmation ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetConfirmationDisplayMetadata ||
@ -439,28 +659,64 @@ StatusModal {
return false
}
}
else if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.factoryReset) {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.factoryReset) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetConfirmation ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetConfirmationDisplayMetadata) {
return d.primaryButtonEnabled
}
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
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.keycardEmpty ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeychainPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.notKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterBiometricsPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongBiometricsPassword ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin) {
return d.primaryButtonEnabled
}
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached) {
return true
}
}
return true
}
icon.name: {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.setupNewKeycard) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.seedPhraseEnterWords ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterSeedPhrase) {
if (root.sharedKeycardModule.migratingProfileKeyPair()) {
if (root.sharedKeycardModule.loggedInUserUsesBiometricLogin())
return "touch-id"
if (root.sharedKeycardModule.isProfileKeyPairMigrated())
return "keycard"
return "password"
}
}
}
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.biometricsPasswordFailed ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinFailed ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinInvalid ||
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 "touch-id"
}
}
return ""
}
highlighted: focus
onClicked: {
root.sharedKeycardModule.currentState.doPrimaryAction()

View File

@ -23,6 +23,7 @@ StatusListItem {
property string keyPairImage: ""
property string keyPairDerivedFrom: ""
property string keyPairAccounts: ""
property bool keyPairCardLocked: false
signal keyPairSelected()
@ -36,17 +37,18 @@ StatusListItem {
height: root.keyPairIcon? 24 : 40
name: root.keyPairImage? root.keyPairImage : root.keyPairIcon
color: root.keyPairType === Constants.keycard.keyPairType.profile?
Utils.colorForPubkey(d.myPublicKey) : Theme.palette.primaryColor1
letterSize: Math.max(4, this.width / 2.4)
Utils.colorForPubkey(d.myPublicKey) :
root.keyPairCardLocked? Theme.palette.dangerColor1 : Theme.palette.primaryColor1
letterSize: Math.max(4, asset.width / 2.4)
charactersLen: 2
isLetterIdenticon: !root.keyPairIcon && !this.name.toString()
bgColor: Theme.palette.primaryColor3
isLetterIdenticon: !root.keyPairIcon && !asset.name.toString()
bgColor: root.keyPairCardLocked? Theme.palette.dangerColor3 : Theme.palette.primaryColor3
}
ringSettings {
ringSpecModel: root.keyPairType === Constants.keycard.keyPairType.profile?
Utils.getColorHashAsJson(d.myPublicKey) : []
ringPxSize: Math.max(this.icon.width / 24.0)
ringPxSize: Math.max(asset.width / 24.0)
}
tagsModel: ListModel{}

View File

@ -65,7 +65,7 @@ Rectangle {
height: 24
name: root.keyPairIcon
color: Utils.colorForPubkey(root.keyPairPubKey)
letterSize: Math.max(4, this.image.width / 2.4)
letterSize: Math.max(4, asset.width / 2.4)
charactersLen: 2
isLetterIdenticon: false
bgColor: Theme.palette.primaryColor3

View File

@ -0,0 +1,210 @@
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 string kcData: root.sharedKeycardModule.keycardData
signal passwordValid(bool valid)
onKcDataChanged: {
d.updatePasswordValidation()
}
onStateChanged: {
password.focus = true
}
Component.onCompleted: timer.start()
Timer {
id: timer
interval: 1000
onTriggered: {
password.forceActiveFocus(Qt.MouseFocusReason)
}
}
QtObject {
id: d
function updatePasswordValidation() {
root.passwordValid(password.text !== "" && root.kcData === "")
}
}
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
}
StatusBaseText {
id: message
Layout.alignment: Qt.AlignCenter
wrapMode: Text.WordWrap
visible: text != ""
}
StatusPasswordInput {
id: password
Layout.alignment: Qt.AlignHCenter
signingPhrase: root.sharedKeycardModule.getSigningPhrase()
placeholderText: qsTr("Password")
selectByMouse: true
focus: true
onTextChanged: {
root.sharedKeycardModule.keycardData = ""
root.sharedKeycardModule.setPassword(text)
d.updatePasswordValidation()
}
onAccepted: {
if (password.text !== "") {
root.sharedKeycardModule.currentState.doPrimaryAction()
}
}
}
StatusBaseText {
id: info
Layout.alignment: Qt.AlignCenter
wrapMode: Text.WordWrap
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
states: [
State {
name: Constants.keycardSharedState.enterPassword
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPassword
PropertyChanges {
target: image
source: Style.png("keycard/authenticate")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("Enter your password")
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
}
PropertyChanges {
target: message
text: ""
}
PropertyChanges {
target: info
text: ""
}
},
State {
name: Constants.keycardSharedState.wrongPassword
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPassword
PropertyChanges {
target: image
source: Style.png("keycard/authenticate")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("Enter your password")
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
}
PropertyChanges {
target: message
text: ""
}
PropertyChanges {
target: info
text: root.kcData !== ""? qsTr("Password incorrect") : ""
color: Theme.palette.dangerColor1
}
},
State {
name: Constants.keycardSharedState.enterBiometricsPassword
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterBiometricsPassword
PropertyChanges {
target: image
source: Style.png("keycard/biometrics-success")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("Password changed on other device")
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
}
PropertyChanges {
target: message
text: qsTr("Enter your new password to proceed")
color: Theme.palette.baseColor1
}
PropertyChanges {
target: info
text: root.kcData !== ""? qsTr("Password incorrect") : ""
color: Theme.palette.dangerColor1
}
},
State {
name: Constants.keycardSharedState.wrongBiometricsPassword
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongBiometricsPassword
PropertyChanges {
target: image
source: Style.png("keycard/biometrics-success")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("Password changed on other device")
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
}
PropertyChanges {
target: message
text: qsTr("Enter your new password to proceed")
color: Theme.palette.baseColor1
}
PropertyChanges {
target: info
text: root.kcData !== ""? qsTr("Password incorrect") : ""
color: Theme.palette.dangerColor1
}
}
]
}

View File

@ -29,11 +29,9 @@ Item {
onWrongSeedPhraseChanged: {
if (wrongSeedPhrase) {
invalidSeedTxt.text = qsTr("The phrase youve entered does not match this Keycards seed phrase")
invalidSeedTxt.visible = true
}
else {
invalidSeedTxt.text = ""
invalidSeedTxt.visible = false
}
}
@ -50,7 +48,6 @@ Item {
root.sharedKeycardModule.setSeedPhrase(mnemonicString)
} else {
invalidSeedTxt.text = qsTr("Invalid seed phrase")
invalidSeedTxt.visible = true
d.allEntriesValid = false
}
}
@ -65,11 +62,9 @@ Item {
d.allEntriesValid = d.allEntriesValid && d.seedPhrases_en.words.includes(word)
if (d.allEntriesValid) {
invalidSeedTxt.text = ""
invalidSeedTxt.visible = false
}
else {
invalidSeedTxt.text = qsTr("The phrase youve entered is invalid")
invalidSeedTxt.visible = true
}
}
@ -311,7 +306,6 @@ Item {
id: invalidSeedTxt
Layout.alignment: Qt.AlignHCenter
color: Theme.palette.dangerColor1
visible: false
}
}

View File

@ -6,11 +6,6 @@ import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
///////// Remove Later////////////
import StatusQ.Popups 0.1
import StatusQ.Controls 0.1
//////////////////////////////////
import utils 1.0
import "../helpers"
@ -22,7 +17,7 @@ Item {
Component.onCompleted: {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.migratingKeyPair) {
passwordPopup.open()
root.sharedKeycardModule.currentState.doPrimaryAction()
}
}
@ -41,37 +36,6 @@ Item {
}
}
///////// Remove Later////////////
StatusModal {
id: passwordPopup
width: 300
height: 200
anchors.centerIn: parent
header.title: qsTr("Temporary Popup")
closePolicy: Popup.NoAutoClose
contentItem: Item {
StatusInput {
id: password
width: parent.width - Style.current.padding * 2
anchors.centerIn: parent
input.clearable: true
placeholderText: qsTr("Enter password...")
}
}
rightButtons: [
StatusButton {
id: primaryButton
text: qsTr("Next")
onClicked: {
root.sharedKeycardModule.setPassword(password.text)
passwordPopup.close()
root.sharedKeycardModule.currentState.doPrimaryAction()
}
}
]
}
//////////////////////////////////
Component {
id: keyPairComponent
KeyPairItem {
@ -110,6 +74,20 @@ Item {
}
}
Component {
id: keyPairForAuthenticationComponent
KeyPairItem {
keyPairType: root.sharedKeycardModule.keyPairForAuthentication.pairType
keyPairPubKey: root.sharedKeycardModule.keyPairForAuthentication.pubKey
keyPairName: root.sharedKeycardModule.keyPairForAuthentication.name
keyPairIcon: root.sharedKeycardModule.keyPairForAuthentication.icon
keyPairImage: root.sharedKeycardModule.keyPairForAuthentication.image
keyPairDerivedFrom: root.sharedKeycardModule.keyPairForAuthentication.derivedFrom
keyPairAccounts: root.sharedKeycardModule.keyPairForAuthentication.accounts
keyPairCardLocked: root.sharedKeycardModule.keyPairForAuthentication.locked
}
}
ColumnLayout {
anchors.fill: parent
anchors.topMargin: Style.current.xlPadding
@ -187,6 +165,21 @@ Item {
return true
}
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication &&
!!root.sharedKeycardModule.keyPairForAuthentication &&
root.sharedKeycardModule.keyPairForAuthentication.name !== "") {
if(root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardInserted ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.insertKeycard ||
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.biometricsReadyToSign ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinFailed ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinInvalid ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard) {
return true
}
}
return false
}
@ -216,6 +209,21 @@ Item {
return unknownKeyPairCompontnt
}
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardInserted ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.insertKeycard ||
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.biometricsReadyToSign ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinFailed ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinInvalid ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard) {
return keyPairForAuthenticationComponent
}
}
return undefined
}
}
@ -338,12 +346,17 @@ Item {
}
PropertyChanges {
target: image
pattern: "keycard/strong_error/img-%1"
source: ""
pattern: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication?
"" : "keycard/strong_error/img-%1"
source: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication?
Style.png("keycard/plain-error") : ""
startImgIndexForTheFirstLoop: 0
startImgIndexForOtherLoops: 18
endImgIndex: 29
duration: 1300
startImgIndexForOtherLoops: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication?
0 : 18
endImgIndex: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication?
0 : 29
duration: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication?
0 : 1300
loops: -1
}
PropertyChanges {
@ -353,6 +366,28 @@ Item {
color: Theme.palette.dangerColor1
}
},
State {
name: Constants.keycardSharedState.wrongKeycard
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard
PropertyChanges {
target: title
text: qsTr("Wrong Keycard inserted")
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.dangerColor1
}
PropertyChanges {
target: image
source: Style.png("keycard/plain-error")
pattern: ""
}
PropertyChanges {
target: message
text: qsTr("Keycard inserted does not match the Keycard below")
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.dangerColor1
}
},
State {
name: Constants.keycardSharedState.maxPinRetriesReached
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.maxPinRetriesReached
@ -375,7 +410,9 @@ Item {
}
PropertyChanges {
target: message
text: qsTr("Pin entered incorrectly too many times")
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
}
@ -410,18 +447,21 @@ Item {
text: qsTr("Keycard is empty")
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.directColor1
color: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication?
Theme.palette.dangerColor1 : Theme.palette.directColor1
}
PropertyChanges {
target: image
source: Style.png("keycard/card-empty")
source: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication?
Style.png("keycard/plain-error") : 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: Theme.palette.directColor1
color: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication?
Theme.palette.dangerColor1 : Theme.palette.directColor1
}
},
State {
@ -449,6 +489,33 @@ Item {
color: Theme.palette.directColor1
}
},
State {
name: Constants.keycardSharedState.keycardLocked
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardLocked
PropertyChanges {
target: title
text: qsTr("Keycard locked and already stores keys")
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: qsTr("The Keycard you have inserted is locked,\nyou will need to factory reset it before proceeding")
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.directColor1
}
},
State {
name: Constants.keycardSharedState.recognizedKeycard
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.recognizedKeycard
@ -601,6 +668,88 @@ Item {
target: message
text: ""
}
},
State {
name: Constants.keycardSharedState.biometricsReadyToSign
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsReadyToSign
PropertyChanges {
target: title
text: qsTr("Ready to authenticate...")
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.directColor1
}
PropertyChanges {
target: image
source: Style.png("keycard/card-inserted")
pattern: ""
}
PropertyChanges {
target: message
text: ""
}
},
State {
name: Constants.keycardSharedState.biometricsPasswordFailed
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPasswordFailed
PropertyChanges {
target: image
source: Style.png("keycard/biometrics-fail")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("Biometric scan failed")
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.directColor1
}
PropertyChanges {
target: message
text: qsTr("Biometrics incorrect")
color: Theme.palette.dangerColor1
}
},
State {
name: Constants.keycardSharedState.biometricsPinFailed
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinFailed
PropertyChanges {
target: image
source: Style.png("keycard/plain-error")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("Biometric scan failed")
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.directColor1
}
PropertyChanges {
target: message
text: qsTr("Biometrics incorrect")
color: Theme.palette.dangerColor1
}
},
State {
name: Constants.keycardSharedState.biometricsPinInvalid
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.biometricsPinInvalid
PropertyChanges {
target: image
source: Style.png("keycard/plain-error")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("Biometric pin invalid")
font.pixelSize: Constants.keycard.general.fontSize1
font.weight: Font.Bold
color: Theme.palette.dangerColor1
}
PropertyChanges {
target: message
text: ""
}
}
]
}

View File

@ -19,8 +19,11 @@ Item {
property int remainingAttempts: parseInt(root.sharedKeycardModule.keycardData, 10)
signal pinUpdated(string pin)
onRemainingAttemptsChanged: {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeychainPin) {
pinInputField.statesInitialization()
pinInputField.forceFocus()
}
@ -36,14 +39,51 @@ Item {
}
}
Component.onCompleted: timer.start()
Timer {
id: timer
interval: 1000
onTriggered: {
pinInputField.statesInitialization()
pinInputField.forceFocus()
}
}
Component {
id: keyPairComponent
KeyPairItem {
keyPairType: root.sharedKeycardModule.selectedKeyPairItem.pairType
keyPairPubKey: root.sharedKeycardModule.selectedKeyPairItem.pubKey
keyPairName: root.sharedKeycardModule.selectedKeyPairItem.name
keyPairIcon: root.sharedKeycardModule.selectedKeyPairItem.icon
keyPairImage: root.sharedKeycardModule.selectedKeyPairItem.image
keyPairDerivedFrom: root.sharedKeycardModule.selectedKeyPairItem.derivedFrom
keyPairAccounts: root.sharedKeycardModule.selectedKeyPairItem.accounts
}
}
Component {
id: keyPairForAuthenticationComponent
KeyPairItem {
keyPairType: root.sharedKeycardModule.keyPairForAuthentication.pairType
keyPairPubKey: root.sharedKeycardModule.keyPairForAuthentication.pubKey
keyPairName: root.sharedKeycardModule.keyPairForAuthentication.name
keyPairIcon: root.sharedKeycardModule.keyPairForAuthentication.icon
keyPairImage: root.sharedKeycardModule.keyPairForAuthentication.image
keyPairDerivedFrom: root.sharedKeycardModule.keyPairForAuthentication.derivedFrom
keyPairAccounts: root.sharedKeycardModule.keyPairForAuthentication.accounts
keyPairCardLocked: root.sharedKeycardModule.keyPairForAuthentication.locked
}
}
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
clip: true
spacing: Style.current.halfPadding
KeycardImage {
id: image
@ -58,16 +98,26 @@ Item {
font.weight: Font.Bold
}
StatusBaseText {
id: subTitle
Layout.alignment: Qt.AlignCenter
wrapMode: Text.WordWrap
visible: text !== ""
}
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 &&
root.sharedKeycardModule.currentState.stateType !== Constants.keycardSharedState.pinVerified
onPinInputChanged: {
if (root.state !== Constants.keycardSharedState.wrongPin) {
root.pinUpdated(pinInput)
if (root.state !== Constants.keycardSharedState.wrongPin ||
root.state === Constants.keycardSharedState.wrongKeychainPin) {
image.source = Style.png("keycard/enter-pin-%1".arg(pinInput.length))
}
if(pinInput.length == 0) {
@ -75,8 +125,11 @@ Item {
}
if(root.state === Constants.keycardSharedState.createPin ||
root.state === Constants.keycardSharedState.enterPin ||
root.state === Constants.keycardSharedState.wrongPin) {
root.state === Constants.keycardSharedState.wrongPin ||
root.state === Constants.keycardSharedState.wrongKeychainPin) {
root.sharedKeycardModule.setPin(pinInput)
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication)
return
root.sharedKeycardModule.currentState.doTertiaryAction()
}
else if(root.state === Constants.keycardSharedState.repeatPin) {
@ -95,6 +148,7 @@ Item {
StatusBaseText {
id: info
Layout.alignment: Qt.AlignCenter
Layout.fillHeight: info.visble && !message.visible? true : false
wrapMode: Text.WordWrap
visible: text !== ""
}
@ -102,30 +156,49 @@ Item {
StatusBaseText {
id: message
Layout.alignment: Qt.AlignCenter
Layout.fillHeight: message.visible? true : false
wrapMode: Text.WordWrap
visible: text !== ""
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
Loader {
Layout.preferredWidth: parent.width
active: root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.setupNewKeycard &&
(root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.createPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.repeatPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pinSet)
active: {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.setupNewKeycard) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.createPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.repeatPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pinSet) {
return true
}
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeychainPin) {
return true
}
}
sourceComponent: KeyPairItem {
keyPairType: root.sharedKeycardModule.selectedKeyPairItem.pairType
keyPairPubKey: root.sharedKeycardModule.selectedKeyPairItem.pubKey
keyPairName: root.sharedKeycardModule.selectedKeyPairItem.name
keyPairIcon: root.sharedKeycardModule.selectedKeyPairItem.icon
keyPairImage: root.sharedKeycardModule.selectedKeyPairItem.image
keyPairDerivedFrom: root.sharedKeycardModule.selectedKeyPairItem.derivedFrom
keyPairAccounts: root.sharedKeycardModule.selectedKeyPairItem.accounts
return false
}
sourceComponent: {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.setupNewKeycard) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.createPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.repeatPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.pinSet) {
return keyPairComponent
}
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.enterPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongPin ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeychainPin) {
return keyPairForAuthenticationComponent
}
}
return undefined
}
}
}
@ -145,6 +218,10 @@ Item {
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
}
PropertyChanges {
target: subTitle
text: ""
}
PropertyChanges {
target: info
text: ""
@ -168,6 +245,45 @@ Item {
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
}
PropertyChanges {
target: subTitle
text: ""
}
PropertyChanges {
target: info
text: qsTr("PIN incorrect")
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
}
},
State {
name: Constants.keycardSharedState.wrongKeychainPin
when: root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeychainPin
PropertyChanges {
target: image
source: Style.png("keycard/plain-error")
pattern: ""
}
PropertyChanges {
target: title
text: qsTr("Your saved PIN is out of date")
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
}
PropertyChanges {
target: subTitle
text: qsTr("Enter your new PIN to proceed")
font.pixelSize: Constants.keycard.general.fontSize3
color: Theme.palette.baseColor1
}
PropertyChanges {
target: info
text: qsTr("PIN incorrect")
@ -197,6 +313,10 @@ Item {
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
}
PropertyChanges {
target: subTitle
text: ""
}
PropertyChanges {
target: info
text: qsTr("It is very important that you do not lose this PIN")
@ -222,6 +342,10 @@ Item {
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
}
PropertyChanges {
target: subTitle
text: ""
}
PropertyChanges {
target: info
text: qsTr("It is very important that you do not lose this PIN")
@ -252,6 +376,10 @@ Item {
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
}
PropertyChanges {
target: subTitle
text: ""
}
PropertyChanges {
target: info
text: ""
@ -280,6 +408,10 @@ Item {
font.pixelSize: Constants.keycard.general.fontSize1
color: Theme.palette.directColor1
}
PropertyChanges {
target: subTitle
text: ""
}
PropertyChanges {
target: info
text: ""

View File

@ -79,6 +79,7 @@ QtObject {
readonly property string general: "General"
readonly property string factoryReset: "FactoryReset"
readonly property string setupNewKeycard: "SetupNewKeycard"
readonly property string authentication: "Authentication"
}
readonly property QtObject keycardSharedState: QtObject {
@ -93,6 +94,7 @@ QtObject {
readonly property string pinVerified: "PinVerified"
readonly property string enterPin: "EnterPin"
readonly property string wrongPin: "WrongPin"
readonly property string wrongKeychainPin: "WrongKeychainPin"
readonly property string maxPinRetriesReached: "MaxPinRetriesReached"
readonly property string factoryResetConfirmation: "FactoryResetConfirmation"
readonly property string factoryResetConfirmationDisplayMetadata: "FactoryResetConfirmationDisplayMetadata"
@ -101,7 +103,9 @@ QtObject {
readonly property string keycardMetadataDisplay: "KeycardMetadataDisplay"
readonly property string keycardEmpty: "KeycardEmpty"
readonly property string keycardNotEmpty: "KeycardNotEmpty"
readonly property string keycardLocked: "KeycardLocked"
readonly property string notKeycard: "NotKeycard"
readonly property string wrongKeycard: "WrongKeycard"
readonly property string recognizedKeycard: "RecognizedKeycard"
readonly property string selectExistingKeyPair: "SelectExistingKeyPair"
readonly property string enterSeedPhrase: "EnterSeedPhrase"
@ -111,6 +115,14 @@ QtObject {
readonly property string keyPairMigrateSuccess: "KeyPairMigrateSuccess"
readonly property string keyPairMigrateFailure: "KeyPairMigrateFailure"
readonly property string migratingKeyPair: "MigratingKeyPair"
readonly property string enterPassword: "EnterPassword"
readonly property string wrongPassword: "WrongPassword"
readonly property string biometricsPasswordFailed: "BiometricsPasswordFailed"
readonly property string biometricsPinFailed: "BiometricsPinFailed"
readonly property string biometricsPinInvalid: "BiometricsPinInvalid"
readonly property string biometricsReadyToSign: "BiometricsReadyToSign"
readonly property string enterBiometricsPassword: "EnterBiometricsPassword"
readonly property string wrongBiometricsPassword: "WrongBiometricsPassword"
}
readonly property QtObject keychain: QtObject {
@ -372,6 +384,7 @@ QtObject {
readonly property int popupBiggerHeight: 766
readonly property int titleHeight: 44
readonly property int messageHeight: 48
readonly property int footerButtonsHeight: 44
}
readonly property QtObject keyPairType: QtObject {