feat(@desktop/keycard): keycard service updated

Keycard library from this commit brings new changes in terms of
signals being sent by the lib in case of reader is not plugged in,
card is not inserted, card is inserted, that means the following
signals are sent only when it's really needed:
`"{\"type\":\"keycard.flow-result\",\"event\":{\"error\":\"connection-error\"}}"`
`"{\"type\":\"keycard.action.insert-card\",\"event\":{\"error\":\"connection-error\"}}"`
`"{\"type\":\"keycard.action.card-inserted\",\"event\":{}}"`
This commit is contained in:
Sale Djenic 2022-08-31 18:47:57 +02:00 committed by saledjenic
parent 59f23c82f7
commit 912cbe3b1d
4 changed files with 135 additions and 68 deletions

View File

@ -1,26 +1,3 @@
const ResponseKeyType* = "type"
const ResponseKeyEvent* = "event"
const ResponseTypeValueKeycardFlowResult* = "keycard.flow-result"
const ResponseTypeValueInsertCard* = "keycard.action.insert-card"
const ResponseTypeValueCardInserted* = "keycard.action.card-inserted"
const ResponseTypeValueSwapCard* = "keycard.action.swap-card"
const ResponseTypeValueEnterPairing* = "keycard.action.enter-pairing"
const ResponseTypeValueEnterPIN* = "keycard.action.enter-pin"
const ResponseTypeValueEnterPUK* = "keycard.action.enter-puk"
const ResponseTypeValueEnterNewPair* = "keycard.action.enter-new-pairing"
const ResponseTypeValueEnterNewPIN* = "keycard.action.enter-new-pin"
const ResponseTypeValueEnterNewPUK* = "keycard.action.enter-new-puk"
const ResponseTypeValueEnterTXHash* = "keycard.action.enter-tx-hash"
const ResponseTypeValueEnterPath* = "keycard.action.enter-bip44-path"
const ResponseTypeValueEnterMnemonic* = "keycard.action.enter-mnemonic"
const ResponseInitialized* = "initialized"
const ResponseInstanceUID* = "instanceUID"
const ResponseVersion* = "version"
const ResponseAvailableSlots* = "availableSlots"
const ResponseKeyUID* = "keyUID"
const ErrorKey* = "error" const ErrorKey* = "error"
const ErrorOK* = "ok" const ErrorOK* = "ok"
const ErrorCancel* = "cancel" const ErrorCancel* = "cancel"
@ -73,6 +50,47 @@ const RequestParamCardMeta* = "card-metadata"
const RequestParamCardName* = "card-name" const RequestParamCardName* = "card-name"
const RequestParamWalletPaths* = "wallet-paths" const RequestParamWalletPaths* = "wallet-paths"
const RequestParamAddress* = "address" const ResponseKeyType* = "type"
const RequestParamPublicKey* = "publicKey" const ResponseKeyEvent* = "event"
const RequestParamPrivateKey* = "privateKey"
const ResponseTypeValueKeycardFlowResult* = "keycard.flow-result"
const ResponseTypeValueInsertCard* = "keycard.action.insert-card"
const ResponseTypeValueCardInserted* = "keycard.action.card-inserted"
const ResponseTypeValueSwapCard* = "keycard.action.swap-card"
const ResponseTypeValueEnterPairing* = "keycard.action.enter-pairing"
const ResponseTypeValueEnterPIN* = "keycard.action.enter-pin"
const ResponseTypeValueEnterPUK* = "keycard.action.enter-puk"
const ResponseTypeValueEnterNewPair* = "keycard.action.enter-new-pairing"
const ResponseTypeValueEnterNewPIN* = "keycard.action.enter-new-pin"
const ResponseTypeValueEnterNewPUK* = "keycard.action.enter-new-puk"
const ResponseTypeValueEnterTXHash* = "keycard.action.enter-tx-hash"
const ResponseTypeValueEnterPath* = "keycard.action.enter-bip44-path"
const ResponseTypeValueEnterMnemonic* = "keycard.action.enter-mnemonic"
const ResponseTypeValueEnterCardName* = "keycard.action.enter-cardname"
const ResponseTypeValueEnterWallets* = "keycard.action.enter-wallets"
const ResponseParamInitialized* = "initialized"
const ResponseParamInstanceUID* = "instanceUID"
const ResponseParamVersion* = "version"
const ResponseParamAvailableSlots* = "availableSlots"
const ResponseParamAppInfoKeyUID* = "keyUID"
const ResponseParamName* = "name"
const ResponseParamWallets* = "wallets"
const ResponseParamPath* = "path"
const ResponseParamAddress* = "address"
const ResponseParamPublicKey* = "publicKey"
const ResponseParamPrivateKey* = "privateKey"
const ResponseParamErrorKey* = ErrorKey
const ResponseParamCardMeta* = RequestParamCardMeta
const ResponseParamFreeSlots* = RequestParamFreeSlots
const ResponseParamPINRetries* = RequestParamPINRetries
const ResponseParamPUKRetries* = RequestParamPUKRetries
const ResponseParamKeyUID* = RequestParamKeyUID
const ResponseParamAppInfo* = RequestParamAppInfo
const ResponseParamEIP1581Key* = RequestParamEIP1581Key
const ResponseParamEncKey* = RequestParamEncKey
const ResponseParamMasterKey* = RequestParamMasterKey
const ResponseParamWalletKey* = RequestParamWalletKey
const ResponseParamWalleRootKey* = RequestParamWalleRootKey
const ResponseParamWhisperKey* = RequestParamWhisperKey
const ResponseParamMnemonicIdxs* = RequestParamMnemonicIdxs

View File

@ -11,6 +11,14 @@ type
availableSlots*: int availableSlots*: int
keyUID*: string keyUID*: string
WalletAccount* = object
path*: string
address*: string
CardMetadata* = object
name*: string
walletAccounts*: seq[WalletAccount]
KeycardEvent* = object KeycardEvent* = object
error*: string error*: string
applicationInfo*: ApplicationInfo applicationInfo*: ApplicationInfo
@ -19,6 +27,7 @@ type
keyUid*: string keyUid*: string
pinRetries*: int pinRetries*: int
pukRetries*: int pukRetries*: int
cardMetadata*: CardMetadata
eip1581Key*: KeyDetails eip1581Key*: KeyDetails
encryptionKey*: KeyDetails encryptionKey*: KeyDetails
masterKey*: KeyDetails masterKey*: KeyDetails
@ -27,49 +36,63 @@ type
whisperKey*: KeyDetails whisperKey*: KeyDetails
proc toKeyDetails(jsonObj: JsonNode): KeyDetails = proc toKeyDetails(jsonObj: JsonNode): KeyDetails =
discard jsonObj.getProp(RequestParamAddress, result.address) discard jsonObj.getProp(ResponseParamAddress, result.address)
discard jsonObj.getProp(RequestParamPrivateKey, result.privateKey) discard jsonObj.getProp(ResponseParamPrivateKey, result.privateKey)
if jsonObj.getProp(RequestParamPublicKey, result.publicKey): if jsonObj.getProp(ResponseParamPublicKey, result.publicKey):
result.publicKey = "0x" & result.publicKey result.publicKey = "0x" & result.publicKey
proc toApplicationInfo(jsonObj: JsonNode): ApplicationInfo = proc toApplicationInfo(jsonObj: JsonNode): ApplicationInfo =
discard jsonObj.getProp(ResponseInitialized, result.initialized) discard jsonObj.getProp(ResponseParamInitialized, result.initialized)
discard jsonObj.getProp(ResponseInstanceUID, result.instanceUID) discard jsonObj.getProp(ResponseParamInstanceUID, result.instanceUID)
discard jsonObj.getProp(ResponseVersion, result.version) discard jsonObj.getProp(ResponseParamVersion, result.version)
discard jsonObj.getProp(ResponseAvailableSlots, result.availableSlots) discard jsonObj.getProp(ResponseParamAvailableSlots, result.availableSlots)
discard jsonObj.getProp(ResponseKeyUID, result.keyUID) discard jsonObj.getProp(ResponseParamAppInfoKeyUID, result.keyUID)
proc toWalletAccount(jsonObj: JsonNode): WalletAccount =
discard jsonObj.getProp(ResponseParamPath, result.path)
discard jsonObj.getProp(ResponseParamAddress, result.address)
proc toCardMetadata(jsonObj: JsonNode): CardMetadata =
discard jsonObj.getProp(ResponseParamName, result.name)
var accountsArr: JsonNode
if jsonObj.getProp(ResponseParamWallets, accountsArr) and accountsArr.kind == JArray:
for acc in accountsArr:
result.walletAccounts.add(acc.toWalletAccount())
proc toKeycardEvent(jsonObj: JsonNode): KeycardEvent = proc toKeycardEvent(jsonObj: JsonNode): KeycardEvent =
discard jsonObj.getProp(ErrorKey, result.error) discard jsonObj.getProp(ResponseParamErrorKey, result.error)
discard jsonObj.getProp(RequestParamFreeSlots, result.freePairingSlots) discard jsonObj.getProp(ResponseParamFreeSlots, result.freePairingSlots)
discard jsonObj.getProp(RequestParamPINRetries, result.pinRetries) discard jsonObj.getProp(ResponseParamPINRetries, result.pinRetries)
discard jsonObj.getProp(RequestParamPUKRetries, result.pukRetries) discard jsonObj.getProp(ResponseParamPUKRetries, result.pukRetries)
if jsonObj.getProp(RequestParamKeyUID, result.keyUid): if jsonObj.getProp(ResponseParamKeyUID, result.keyUid):
result.keyUid = "0x" & result.keyUid result.keyUid = "0x" & result.keyUid
var obj: JsonNode var obj: JsonNode
if(jsonObj.getProp(RequestParamAppInfo, obj)): if(jsonObj.getProp(ResponseParamAppInfo, obj)):
result.applicationInfo = toApplicationInfo(obj) result.applicationInfo = toApplicationInfo(obj)
if(jsonObj.getProp(RequestParamEIP1581Key, obj)): if(jsonObj.getProp(ResponseParamEIP1581Key, obj)):
result.eip1581Key = toKeyDetails(obj) result.eip1581Key = toKeyDetails(obj)
if(jsonObj.getProp(RequestParamEncKey, obj)): if(jsonObj.getProp(ResponseParamEncKey, obj)):
result.encryptionKey = toKeyDetails(obj) result.encryptionKey = toKeyDetails(obj)
if(jsonObj.getProp(RequestParamMasterKey, obj)): if(jsonObj.getProp(ResponseParamMasterKey, obj)):
result.masterKey = toKeyDetails(obj) result.masterKey = toKeyDetails(obj)
if(jsonObj.getProp(RequestParamWalletKey, obj)): if(jsonObj.getProp(ResponseParamWalletKey, obj)):
result.walletKey = toKeyDetails(obj) result.walletKey = toKeyDetails(obj)
if(jsonObj.getProp(RequestParamWalleRootKey, obj)): if(jsonObj.getProp(ResponseParamWalleRootKey, obj)):
result.walletRootKey = toKeyDetails(obj) result.walletRootKey = toKeyDetails(obj)
if(jsonObj.getProp(RequestParamWhisperKey, obj)): if(jsonObj.getProp(ResponseParamWhisperKey, obj)):
result.whisperKey = toKeyDetails(obj) result.whisperKey = toKeyDetails(obj)
var indexesArr: JsonNode var indexesArr: JsonNode
if jsonObj.getProp(RequestParamMnemonicIdxs, indexesArr) and indexesArr.kind == JArray: if jsonObj.getProp(ResponseParamMnemonicIdxs, indexesArr) and indexesArr.kind == JArray:
for ind in indexesArr: for ind in indexesArr:
result.seedPhraseIndexes.add(ind.getInt) result.seedPhraseIndexes.add(ind.getInt)
if(jsonObj.getProp(ResponseParamCardMeta, obj)):
result.cardMetadata = toCardMetadata(obj)

View File

@ -6,7 +6,7 @@ import ../../../constants as status_const
import constants import constants
type FlowType {.pure.} = enum type KCSFlowType* {.pure.} = enum
NoFlow = -1 # this type is added only for the desktop app purpose NoFlow = -1 # this type is added only for the desktop app purpose
GetAppInfo = 0 # enumeration of these flows should follow enumeration in the `status-keycard-go` GetAppInfo = 0 # enumeration of these flows should follow enumeration in the `status-keycard-go`
RecoverAccount RecoverAccount
@ -53,11 +53,13 @@ QtObject:
events: EventEmitter events: EventEmitter
threadpool: ThreadPool threadpool: ThreadPool
closingApp: bool closingApp: bool
currentFlow: FlowType currentFlow: KCSFlowType
lastReceivedKeycardData: tuple[flowType: string, flowEvent: KeycardEvent]
setPayloadForCurrentFlow: JsonNode
################################################# #################################################
# Forward declaration section # Forward declaration section
proc runTimer(self: Service) proc startGetMetadataFlow*(self: Service)
################################################# #################################################
@ -74,7 +76,7 @@ QtObject:
result.events = events result.events = events
result.threadpool = threadpool result.threadpool = threadpool
result.closingApp = false result.closingApp = false
result.currentFlow = FlowType.NoFlow result.currentFlow = KCSFlowType.NoFlow
proc init*(self: Service) = proc init*(self: Service) =
debug "init keycard using ", pairingsJson=status_const.KEYCARDPAIRINGDATAFILE debug "init keycard using ", pairingsJson=status_const.KEYCARDPAIRINGDATAFILE
@ -98,42 +100,57 @@ QtObject:
let flowType = typeObj.getStr let flowType = typeObj.getStr
let flowEvent = toKeycardEvent(eventObj) let flowEvent = toKeycardEvent(eventObj)
self.lastReceivedKeycardData = (flowType: flowType, flowEvent: flowEvent)
self.events.emit(SignalKeycardResponse, KeycardArgs(flowType: flowType, flowEvent: flowEvent)) self.events.emit(SignalKeycardResponse, KeycardArgs(flowType: flowType, flowEvent: flowEvent))
proc receiveKeycardSignal(self: Service, signal: string) {.slot.} = proc receiveKeycardSignal(self: Service, signal: string) {.slot.} =
self.processSignal(signal) self.processSignal(signal)
proc getLastReceivedKeycardData*(self: Service): tuple[flowType: string, flowEvent: KeycardEvent] =
return self.lastReceivedKeycardData
proc buildSeedPhrasesFromIndexes*(self: Service, seedPhraseIndexes: seq[int]): seq[string] = proc buildSeedPhrasesFromIndexes*(self: Service, seedPhraseIndexes: seq[int]): seq[string] =
var seedPhrase: seq[string] var seedPhrase: seq[string]
for ind in seedPhraseIndexes: for ind in seedPhraseIndexes:
seedPhrase.add(englishWords[ind]) seedPhrase.add(englishWords[ind])
return seedPhrase return seedPhrase
proc updateLocalPayloadForCurrentFlow(self: Service, obj: JsonNode, cleanBefore = false) =
if cleanBefore:
self.setPayloadForCurrentFlow = %* {}
for k, v in obj:
self.setPayloadForCurrentFlow[k] = v
proc getCurrentFlow*(self: Service): KCSFlowType =
return self.currentFlow
proc startFlow(self: Service, payload: JsonNode) = proc startFlow(self: Service, payload: JsonNode) =
self.updateLocalPayloadForCurrentFlow(payload, cleanBefore = true)
let response = keycard_go.keycardStartFlow(self.currentFlow.int, $payload) let response = keycard_go.keycardStartFlow(self.currentFlow.int, $payload)
debug "keycardStartFlow", flowType=self.currentFlow.int, payload=payload, response=response debug "keycardStartFlow", currentFlow=self.currentFlow.int, payload=payload, response=response
proc resumeFlow(self: Service, payload: JsonNode) = proc resumeFlow(self: Service, payload: JsonNode) =
self.updateLocalPayloadForCurrentFlow(payload)
let response = keycard_go.keycardResumeFlow($payload) let response = keycard_go.keycardResumeFlow($payload)
debug "keycardResumeFlow", flowType=self.currentFlow.int, payload=payload, response=response debug "keycardResumeFlow", currentFlow=self.currentFlow.int, payload=payload, response=response
proc cancelCurrentFlow*(self: Service) = proc cancelCurrentFlow*(self: Service) =
let response = keycard_go.keycardCancelFlow() let response = keycard_go.keycardCancelFlow()
self.currentFlow = FlowType.NoFlow self.currentFlow = KCSFlowType.NoFlow
debug "keycardCancelFlow", flowType=self.currentFlow.int, response=response debug "keycardCancelFlow", currentFlow=self.currentFlow.int, response=response
proc generateRandomPUK*(self: Service): string = proc generateRandomPUK*(self: Service): string =
for i in 0 ..< PUKLengthForStatusApp: for i in 0 ..< PUKLengthForStatusApp:
result = result & $rand(0 .. 9) result = result & $rand(0 .. 9)
proc onTimeout(self: Service, response: string) {.slot.} = proc onTimeout(self: Service, response: string) {.slot.} =
if(self.closingApp or self.currentFlow == FlowType.NoFlow): if(self.closingApp or self.currentFlow == KCSFlowType.NoFlow):
return return
debug "onTimeout, about to start flow: ", flowType=self.currentFlow debug "onTimeout, about to start flow: ", currentFlow=self.currentFlow
self.startFlow(%* { }) self.startFlow(self.setPayloadForCurrentFlow)
proc runTimer(self: Service) = proc runTimer(self: Service) =
if(self.closingApp or self.currentFlow == FlowType.NoFlow): if(self.closingApp or self.currentFlow == KCSFlowType.NoFlow):
return return
let arg = TimerTaskArg( let arg = TimerTaskArg(
@ -148,7 +165,7 @@ QtObject:
var payload = %* { } var payload = %* { }
if factoryReset: if factoryReset:
payload[RequestParamFactoryReset] = %* factoryReset payload[RequestParamFactoryReset] = %* factoryReset
self.currentFlow = FlowType.LoadAccount self.currentFlow = KCSFlowType.LoadAccount
self.startFlow(payload) self.startFlow(payload)
proc startLoadAccountFlowWithSeedPhrase*(self: Service, seedPhraseLength: int, seedPhrase: string, factoryReset: bool) = proc startLoadAccountFlowWithSeedPhrase*(self: Service, seedPhraseLength: int, seedPhrase: string, factoryReset: bool) =
@ -163,38 +180,47 @@ QtObject:
} }
if factoryReset: if factoryReset:
payload[RequestParamFactoryReset] = %* factoryReset payload[RequestParamFactoryReset] = %* factoryReset
self.currentFlow = FlowType.LoadAccount self.currentFlow = KCSFlowType.LoadAccount
self.startFlow(payload) self.startFlow(payload)
proc startLoginFlow*(self: Service) = proc startLoginFlow*(self: Service) =
let payload = %* { } let payload = %* { }
self.currentFlow = FlowType.Login self.currentFlow = KCSFlowType.Login
self.startFlow(payload) self.startFlow(payload)
proc startLoginFlowAutomatically*(self: Service, pin: string) = proc startLoginFlowAutomatically*(self: Service, pin: string) =
let payload = %* { let payload = %* {
RequestParamPIN: pin RequestParamPIN: pin
} }
self.currentFlow = FlowType.Login self.currentFlow = KCSFlowType.Login
self.startFlow(payload) self.startFlow(payload)
proc startRecoverAccountFlow*(self: Service) = proc startRecoverAccountFlow*(self: Service) =
let payload = %* { } let payload = %* { }
self.currentFlow = FlowType.RecoverAccount self.currentFlow = KCSFlowType.RecoverAccount
self.startFlow(payload) self.startFlow(payload)
proc startGetAppInfoFlow*(self: Service, factoryReset: bool) = proc startGetAppInfoFlow*(self: Service, factoryReset: bool) =
var payload = %* { } var payload = %* { }
if factoryReset: if factoryReset:
payload[RequestParamFactoryReset] = %* factoryReset payload[RequestParamFactoryReset] = %* factoryReset
self.currentFlow = FlowType.GetAppInfo self.currentFlow = KCSFlowType.GetAppInfo
self.startFlow(payload) self.startFlow(payload)
proc startGetMetadataFlow*(self: Service) = proc startGetMetadataFlow*(self: Service) =
let payload = %* { let payload = %* {
RequestParamResolveAddr: true RequestParamResolveAddr: true
} }
self.currentFlow = FlowType.GetMetadata self.currentFlow = KCSFlowType.GetMetadata
self.startFlow(payload)
proc startStoreMetadataFlow*(self: Service, cardName: string, pin: string, walletPaths: seq[string]) =
let payload = %* {
RequestParamPIN: pin,
RequestParamCardName: cardName,
RequestParamWalletPaths: walletPaths
}
self.currentFlow = KCSFlowType.StoreMetadata
self.startFlow(payload) self.startFlow(payload)
proc storePin*(self: Service, pin: string, puk: string) = proc storePin*(self: Service, pin: string, puk: string) =

@ -1 +1 @@
Subproject commit 3fd18f5ff11a0efffa8a1a2a672351dd6da63d39 Subproject commit 16f37cb459b4aa2d24fa194bb40fd8a4e6541f93