diff --git a/examples/example-go/main.go b/examples/example-go/main.go index 3eb7a48..82d9e9a 100644 --- a/examples/example-go/main.go +++ b/examples/example-go/main.go @@ -92,4 +92,5 @@ func main() { testFlow(skg.GetAppInfo, skg.FlowParams{}) testFlow(skg.RecoverAccount, skg.FlowParams{skg.PIN: "234567"}) + testFlow(skg.UnpairThis, skg.FlowParams{skg.PIN: correctPIN}) } diff --git a/flow.go b/flow.go index c794391..0fa9432 100644 --- a/flow.go +++ b/flow.go @@ -104,38 +104,6 @@ func (f *KeycardFlow) runFlow() { f.state = Idle } -func (f *KeycardFlow) connectedFlow() (FlowStatus, error) { - kc := f.connect() - defer f.closeKeycard(kc) - - if kc == nil { - return nil, errors.New(ErrorConnection) - } - - if factoryReset, ok := f.params[FactoryReset]; ok && factoryReset.(bool) { - err := f.factoryReset(kc) - - if err != nil { - return nil, err - } - } - - err := f.selectKeycard(kc) - - if err != nil { - return nil, err - } - - switch f.flowType { - case GetAppInfo: - return f.getAppInfoFlow(kc) - case RecoverAccount: - return f.recoverAccountFlow(kc) - default: - return nil, errors.New(ErrorUnknownFlow) - } -} - func (f *KeycardFlow) pause(action string, errMsg string) { status := FlowParams{} @@ -159,6 +127,10 @@ func (f *KeycardFlow) pause(action string, errMsg string) { } func (f *KeycardFlow) pauseAndWait(action string, errMsg string) error { + if f.state == Cancelling { + return errors.New("cancel") + } + f.pause(action, errMsg) <-f.wakeUp @@ -210,158 +182,38 @@ func (f *KeycardFlow) connect() *keycardContext { } } -func (f *KeycardFlow) factoryReset(kc *keycardContext) error { - // on success, remove the FactoryReset switch to avoid re-triggering it - // if card is disconnected/reconnected - delete(f.params, FactoryReset) - return errors.New("not implemented") -} +func (f *KeycardFlow) connectedFlow() (FlowStatus, error) { + kc := f.connect() + defer f.closeKeycard(kc) -func (f *KeycardFlow) selectKeycard(kc *keycardContext) error { - appInfo, err := kc.selectApplet() - - f.cardInfo.instanceUID = tox(appInfo.InstanceUID) - f.cardInfo.keyUID = tox(appInfo.KeyUID) - f.cardInfo.freeSlots = bytesToInt(appInfo.AvailableSlots) - - if err != nil { - return restartErr() + if kc == nil { + return nil, errors.New(ErrorConnection) } - if !appInfo.Installed { - return f.pauseAndRestart(SwapCard, ErrorNotAKeycard) - } + if factoryReset, ok := f.params[FactoryReset]; ok && factoryReset.(bool) { + err := f.factoryReset(kc) - if requiredInstanceUID, ok := f.params[InstanceUID]; ok { - if f.cardInfo.instanceUID != requiredInstanceUID { - return f.pauseAndRestart(SwapCard, InstanceUID) + if err != nil { + return nil, err } } - if requiredKeyUID, ok := f.params[KeyUID]; ok { - if f.cardInfo.keyUID != requiredKeyUID { - return f.pauseAndRestart(SwapCard, KeyUID) - } - } - - return nil -} - -func (f *KeycardFlow) pair(kc *keycardContext) error { - if f.cardInfo.freeSlots == 0 { - return f.pauseAndRestart(SwapCard, FreeSlots) - } - - if pairingPass, ok := f.params[PairingPass]; ok { - pairing, err := kc.pair(pairingPass.(string)) - - if err == nil { - return f.pairings.store(f.cardInfo.instanceUID, toPairInfo(pairing)) - } else if isSCardError(err) { - return restartErr() - } - - delete(f.params, PairingPass) - } - - err := f.pauseAndWait(EnterPairing, "") + err := f.selectKeycard(kc) if err != nil { - return err + return nil, err } - return f.pair(kc) -} - -func (f *KeycardFlow) initCard(kc *keycardContext) error { - //NOTE: after init a restart of the flow is always needed - return errors.New("not implemented") -} - -func (f *KeycardFlow) openSC(kc *keycardContext) error { - if !kc.cmdSet.ApplicationInfo.Initialized { - return f.initCard(kc) + switch f.flowType { + case GetAppInfo: + return f.getAppInfoFlow(kc) + case RecoverAccount: + return f.recoverAccountFlow(kc) + case UnpairThis: + return f.unpairThisFlow(kc) + default: + return nil, errors.New(ErrorUnknownFlow) } - - pairing := f.pairings.get(f.cardInfo.instanceUID) - - if pairing != nil { - err := kc.openSecureChannel(pairing.Index, pairing.Key) - - if err == nil { - appStatus, err := kc.getStatusApplication() - - if err != nil { - // getStatus can only fail for connection errors - return restartErr() - } - - f.cardInfo.pinRetries = appStatus.PinRetryCount - f.cardInfo.pukRetries = appStatus.PUKRetryCount - - return nil - } else if isSCardError(err) { - return restartErr() - } - - f.pairings.delete(f.cardInfo.instanceUID) - } - - err := f.pair(kc) - - if err != nil { - return err - } - - return f.openSC(kc) -} - -func (f *KeycardFlow) unblockPUK(kc *keycardContext) error { - return errors.New("not yet implemented") -} - -func (f *KeycardFlow) authenticate(kc *keycardContext) error { - if f.cardInfo.pukRetries == 0 { - return f.pauseAndRestart(SwapCard, PUKRetries) - } else if f.cardInfo.pinRetries == 0 { - // succesful PUK unblock leaves the card authenticated - return f.unblockPUK(kc) - } - - pinError := "" - - if pin, ok := f.params[PIN]; ok { - err := kc.verifyPin(pin.(string)) - - if err == nil { - f.cardInfo.pinRetries = maxPINRetries - return nil - } else if isSCardError(err) { - return restartErr() - } else if leftRetries, ok := getPinRetries(err); ok { - f.cardInfo.pinRetries = leftRetries - } - - pinError = PIN - } - - err := f.pauseAndWait(EnterPIN, pinError) - - if err != nil { - return err - } - - return f.authenticate(kc) -} - -func (f *KeycardFlow) openSCAndAuthenticate(kc *keycardContext) error { - err := f.openSC(kc) - - if err != nil { - return err - } - - return f.authenticate(kc) } func (f *KeycardFlow) getAppInfoFlow(kc *keycardContext) (FlowStatus, error) { @@ -377,3 +229,13 @@ func (f *KeycardFlow) recoverAccountFlow(kc *keycardContext) (FlowStatus, error) return nil, errors.New("not yet implemented") } + +func (f *KeycardFlow) unpairThisFlow(kc *keycardContext) (FlowStatus, error) { + err := f.openSCAndAuthenticate(kc) + + if err != nil { + return nil, err + } + + return nil, errors.New("not yet implemented") +} diff --git a/flow_commands.go b/flow_commands.go new file mode 100644 index 0000000..ec94620 --- /dev/null +++ b/flow_commands.go @@ -0,0 +1,157 @@ +package statuskeycardgo + +import "errors" + +func (f *KeycardFlow) factoryReset(kc *keycardContext) error { + // on success, remove the FactoryReset switch to avoid re-triggering it + // if card is disconnected/reconnected + delete(f.params, FactoryReset) + return errors.New("not implemented") +} + +func (f *KeycardFlow) selectKeycard(kc *keycardContext) error { + appInfo, err := kc.selectApplet() + + f.cardInfo.instanceUID = tox(appInfo.InstanceUID) + f.cardInfo.keyUID = tox(appInfo.KeyUID) + f.cardInfo.freeSlots = bytesToInt(appInfo.AvailableSlots) + + if err != nil { + return restartErr() + } + + if !appInfo.Installed { + return f.pauseAndRestart(SwapCard, ErrorNotAKeycard) + } + + if requiredInstanceUID, ok := f.params[InstanceUID]; ok { + if f.cardInfo.instanceUID != requiredInstanceUID { + return f.pauseAndRestart(SwapCard, InstanceUID) + } + } + + if requiredKeyUID, ok := f.params[KeyUID]; ok { + if f.cardInfo.keyUID != requiredKeyUID { + return f.pauseAndRestart(SwapCard, KeyUID) + } + } + + return nil +} + +func (f *KeycardFlow) pair(kc *keycardContext) error { + if f.cardInfo.freeSlots == 0 { + return f.pauseAndRestart(SwapCard, FreeSlots) + } + + if pairingPass, ok := f.params[PairingPass]; ok { + pairing, err := kc.pair(pairingPass.(string)) + + if err == nil { + return f.pairings.store(f.cardInfo.instanceUID, toPairInfo(pairing)) + } else if isSCardError(err) { + return restartErr() + } + + delete(f.params, PairingPass) + } + + err := f.pauseAndWait(EnterPairing, "") + + if err != nil { + return err + } + + return f.pair(kc) +} + +func (f *KeycardFlow) initCard(kc *keycardContext) error { + //NOTE: after init a restart of the flow is always needed + return errors.New("not implemented") +} + +func (f *KeycardFlow) openSC(kc *keycardContext) error { + if !kc.cmdSet.ApplicationInfo.Initialized { + return f.initCard(kc) + } + + pairing := f.pairings.get(f.cardInfo.instanceUID) + + if pairing != nil { + err := kc.openSecureChannel(pairing.Index, pairing.Key) + + if err == nil { + appStatus, err := kc.getStatusApplication() + + if err != nil { + // getStatus can only fail for connection errors + return restartErr() + } + + f.cardInfo.pinRetries = appStatus.PinRetryCount + f.cardInfo.pukRetries = appStatus.PUKRetryCount + + return nil + } else if isSCardError(err) { + return restartErr() + } + + f.pairings.delete(f.cardInfo.instanceUID) + } + + err := f.pair(kc) + + if err != nil { + return err + } + + return f.openSC(kc) +} + +func (f *KeycardFlow) unblockPUK(kc *keycardContext) error { + return errors.New("not yet implemented") +} + +func (f *KeycardFlow) authenticate(kc *keycardContext) error { + if f.cardInfo.pukRetries == 0 { + return f.pauseAndRestart(SwapCard, PUKRetries) + } else if f.cardInfo.pinRetries == 0 { + // succesful PUK unblock leaves the card authenticated + return f.unblockPUK(kc) + } + + pinError := "" + + if pin, ok := f.params[PIN]; ok { + err := kc.verifyPin(pin.(string)) + + if err == nil { + f.cardInfo.pinRetries = maxPINRetries + return nil + } else if isSCardError(err) { + return restartErr() + } else if leftRetries, ok := getPinRetries(err); ok { + f.cardInfo.pinRetries = leftRetries + } + + pinError = PIN + } + + err := f.pauseAndWait(EnterPIN, pinError) + + if err != nil { + return err + } + + return f.authenticate(kc) +} + +func (f *KeycardFlow) openSCAndAuthenticate(kc *keycardContext) error { + err := f.openSC(kc) + + if err != nil { + return err + } + + return f.authenticate(kc) +}