380 lines
7.2 KiB
Go
Raw Normal View History

package statuskeycardgo
2021-10-14 13:14:43 +03:00
2021-10-15 10:35:01 +03:00
import (
"errors"
"github.com/status-im/status-keycard-go/signal"
)
2021-10-14 13:14:43 +03:00
2021-10-20 11:44:57 +03:00
type cardStatus struct {
instanceUID string
keyUID string
freeSlots int
pinRetries int
pukRetries int
}
2021-10-18 14:50:56 +03:00
type KeycardFlow struct {
flowType FlowType
state runState
wakeUp chan (struct{})
2021-10-19 12:19:02 +03:00
pairings *pairingStore
params FlowParams
2021-10-20 11:44:57 +03:00
cardInfo cardStatus
2021-10-18 14:50:56 +03:00
}
func NewFlow(storageDir string) (*KeycardFlow, error) {
2021-10-19 12:19:02 +03:00
p, err := newPairingStore(storageDir)
if err != nil {
return nil, err
}
2021-10-18 14:50:56 +03:00
flow := &KeycardFlow{
2021-10-19 12:19:02 +03:00
wakeUp: make(chan (struct{})),
pairings: p,
2021-10-14 13:14:43 +03:00
}
return flow, nil
}
2021-10-18 14:50:56 +03:00
func (f *KeycardFlow) Start(flowType FlowType, params FlowParams) error {
2021-10-15 10:35:01 +03:00
if f.state != Idle {
2021-10-14 13:14:43 +03:00
return errors.New("already running")
}
f.flowType = flowType
f.params = params
2021-10-15 10:35:01 +03:00
f.state = Running
2021-10-14 13:14:43 +03:00
go f.runFlow()
return nil
}
2021-10-18 14:50:56 +03:00
func (f *KeycardFlow) Resume(params FlowParams) error {
2021-10-15 10:35:01 +03:00
if f.state != Paused {
2021-10-14 13:14:43 +03:00
return errors.New("only paused flows can be resumed")
}
for k, v := range params {
f.params[k] = v
}
2021-10-15 10:35:01 +03:00
f.state = Resuming
2021-10-14 13:14:43 +03:00
f.wakeUp <- struct{}{}
return nil
}
2021-10-18 14:50:56 +03:00
func (f *KeycardFlow) Cancel() error {
2021-10-14 13:14:43 +03:00
prevState := f.state
2021-10-15 10:35:01 +03:00
if prevState != Idle {
2021-10-14 13:14:43 +03:00
return errors.New("cannot cancel idle flow")
}
2021-10-15 10:35:01 +03:00
f.state = Cancelling
if prevState == Paused {
2021-10-14 13:14:43 +03:00
f.wakeUp <- struct{}{}
}
return nil
}
2021-10-18 14:50:56 +03:00
func (f *KeycardFlow) runFlow() {
2021-10-18 10:19:09 +03:00
var result FlowStatus
2021-10-18 16:25:20 +03:00
var err error
for {
2021-10-20 11:44:57 +03:00
f.cardInfo = cardStatus{freeSlots: -1, pinRetries: -1, pukRetries: -1}
2021-10-18 16:25:20 +03:00
result, err = f.connectedFlow()
2021-10-15 10:35:01 +03:00
2021-10-18 16:25:20 +03:00
if _, ok := err.(*restartError); !ok {
if result == nil {
result = FlowStatus{ErrorKey: err.Error()}
}
break
}
2021-10-15 10:35:01 +03:00
}
if f.state != Cancelling {
signal.SendEvent(FlowResult, result)
}
f.state = Idle
}
2021-10-18 16:25:20 +03:00
func (f *KeycardFlow) connectedFlow() (FlowStatus, error) {
2021-10-15 10:35:01 +03:00
kc := f.connect()
defer f.closeKeycard(kc)
if kc == nil {
2021-10-18 16:25:20 +03:00
return nil, errors.New(ErrorConnection)
}
2021-10-20 11:44:57 +03:00
if factoryReset, ok := f.params[FactoryReset]; ok && factoryReset.(bool) {
err := f.factoryReset(kc)
if err != nil {
return nil, err
}
}
2021-10-18 16:25:20 +03:00
err := f.selectKeycard(kc)
if err != nil {
return nil, err
2021-10-15 10:35:01 +03:00
}
switch f.flowType {
2021-10-15 12:38:06 +03:00
case GetAppInfo:
return f.getAppInfoFlow(kc)
2021-10-18 15:47:27 +03:00
case RecoverAccount:
return f.recoverAccountFlow(kc)
2021-10-15 10:35:01 +03:00
default:
2021-10-18 16:25:20 +03:00
return nil, errors.New(ErrorUnknownFlow)
2021-10-15 10:35:01 +03:00
}
}
2021-10-20 11:44:57 +03:00
func (f *KeycardFlow) pause(action string, errMsg string) {
status := FlowParams{}
if errMsg != "" {
status[ErrorKey] = errMsg
}
if f.cardInfo.freeSlots != -1 {
status[InstanceUID] = f.cardInfo.instanceUID
status[KeyUID] = f.cardInfo.keyUID
status[FreeSlots] = f.cardInfo.freeSlots
}
if f.cardInfo.pinRetries != -1 {
status[PINRetries] = f.cardInfo.pinRetries
status[PUKRetries] = f.cardInfo.pukRetries
}
2021-10-15 10:35:01 +03:00
signal.SendEvent(action, status)
f.state = Paused
}
2021-10-20 11:44:57 +03:00
func (f *KeycardFlow) pauseAndWait(action string, errMsg string) error {
f.pause(action, errMsg)
2021-10-15 10:35:01 +03:00
<-f.wakeUp
2021-10-15 12:38:06 +03:00
if f.state == Resuming {
f.state = Running
2021-10-18 16:29:55 +03:00
return nil
2021-10-15 12:38:06 +03:00
} else {
2021-10-18 16:29:55 +03:00
return errors.New("cancel")
2021-10-15 12:38:06 +03:00
}
2021-10-15 10:35:01 +03:00
}
2021-10-20 11:44:57 +03:00
func (f *KeycardFlow) pauseAndRestart(action string, errMsg string) error {
err := f.pauseAndWait(action, errMsg)
2021-10-18 16:34:15 +03:00
if err != nil {
return err
}
return restartErr()
}
2021-10-18 14:50:56 +03:00
func (f *KeycardFlow) closeKeycard(kc *keycardContext) {
2021-10-15 10:35:01 +03:00
if kc != nil {
kc.stop()
}
}
2021-10-18 14:50:56 +03:00
func (f *KeycardFlow) connect() *keycardContext {
2021-10-15 10:35:01 +03:00
kc, err := startKeycardContext()
if err != nil {
return nil
}
2021-10-20 11:44:57 +03:00
f.pause(InsertCard, "")
2021-10-15 10:35:01 +03:00
select {
case <-f.wakeUp:
if f.state != Cancelling {
panic("Resuming is not expected during connection")
}
return nil
case <-kc.connected:
if kc.runErr != nil {
return nil
}
2021-10-18 14:50:56 +03:00
signal.SendEvent(CardInserted, FlowStatus{})
2021-10-15 10:35:01 +03:00
return kc
}
}
2021-10-20 11:44:57 +03:00
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")
}
2021-10-18 15:47:27 +03:00
func (f *KeycardFlow) selectKeycard(kc *keycardContext) error {
2021-10-15 12:38:06 +03:00
appInfo, err := kc.selectApplet()
2021-10-20 11:44:57 +03:00
f.cardInfo.instanceUID = tox(appInfo.InstanceUID)
f.cardInfo.keyUID = tox(appInfo.KeyUID)
f.cardInfo.freeSlots = bytesToInt(appInfo.AvailableSlots)
2021-10-15 12:38:06 +03:00
if err != nil {
2021-10-18 16:36:53 +03:00
return restartErr()
2021-10-15 12:38:06 +03:00
}
2021-10-18 14:50:56 +03:00
if !appInfo.Installed {
2021-10-20 11:44:57 +03:00
return f.pauseAndRestart(SwapCard, ErrorNotAKeycard)
2021-10-18 14:50:56 +03:00
}
2021-10-15 12:38:06 +03:00
if requiredInstanceUID, ok := f.params[InstanceUID]; ok {
2021-10-20 11:44:57 +03:00
if f.cardInfo.instanceUID != requiredInstanceUID {
return f.pauseAndRestart(SwapCard, InstanceUID)
2021-10-15 12:38:06 +03:00
}
}
if requiredKeyUID, ok := f.params[KeyUID]; ok {
2021-10-20 11:44:57 +03:00
if f.cardInfo.keyUID != requiredKeyUID {
return f.pauseAndRestart(SwapCard, KeyUID)
2021-10-15 12:38:06 +03:00
}
}
2021-10-14 13:14:43 +03:00
2021-10-18 15:47:27 +03:00
return nil
2021-10-14 13:14:43 +03:00
}
2021-10-15 12:38:06 +03:00
2021-10-19 12:19:02 +03:00
func (f *KeycardFlow) pair(kc *keycardContext) error {
2021-10-20 11:44:57 +03:00
if f.cardInfo.freeSlots == 0 {
return f.pauseAndRestart(SwapCard, FreeSlots)
}
2021-10-19 12:19:02 +03:00
if pairingPass, ok := f.params[PairingPass]; ok {
pairing, err := kc.pair(pairingPass.(string))
if err == nil {
2021-10-20 11:44:57 +03:00
return f.pairings.store(f.cardInfo.instanceUID, toPairInfo(pairing))
2021-10-19 12:19:02 +03:00
} else if isSCardError(err) {
return restartErr()
}
delete(f.params, PairingPass)
}
2021-10-20 11:44:57 +03:00
err := f.pauseAndWait(EnterPairing, "")
2021-10-19 12:19:02 +03:00
if err != nil {
return err
}
return f.pair(kc)
}
2021-10-20 11:44:57 +03:00
func (f *KeycardFlow) initCard(kc *keycardContext) error {
//NOTE: after init a restart of the flow is always needed
return errors.New("not implemented")
}
2021-10-19 12:19:02 +03:00
func (f *KeycardFlow) openSC(kc *keycardContext) error {
2021-10-20 11:44:57 +03:00
if !kc.cmdSet.ApplicationInfo.Initialized {
return f.initCard(kc)
}
pairing := f.pairings.get(f.cardInfo.instanceUID)
2021-10-19 12:19:02 +03:00
if pairing != nil {
err := kc.openSecureChannel(pairing.Index, pairing.Key)
if err == nil {
2021-10-20 11:44:57 +03:00
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
2021-10-19 12:19:02 +03:00
return nil
} else if isSCardError(err) {
return restartErr()
}
2021-10-20 11:44:57 +03:00
f.pairings.delete(f.cardInfo.instanceUID)
2021-10-19 12:19:02 +03:00
}
err := f.pair(kc)
if err != nil {
return err
}
return f.openSC(kc)
}
2021-10-20 11:44:57 +03:00
func (f *KeycardFlow) unblockPUK(kc *keycardContext) error {
2021-10-18 16:25:20 +03:00
return errors.New("not yet implemented")
}
2021-10-20 11:44:57 +03:00
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)
}
2021-10-19 12:19:02 +03:00
func (f *KeycardFlow) openSCAndAuthenticate(kc *keycardContext) error {
err := f.openSC(kc)
if err != nil {
return err
}
return f.authenticate(kc)
}
2021-10-18 16:25:20 +03:00
func (f *KeycardFlow) getAppInfoFlow(kc *keycardContext) (FlowStatus, error) {
return FlowStatus{ErrorKey: ErrorOK, AppInfo: toAppInfo(kc.cmdSet.ApplicationInfo)}, nil
}
func (f *KeycardFlow) recoverAccountFlow(kc *keycardContext) (FlowStatus, error) {
err := f.openSCAndAuthenticate(kc)
2021-10-15 12:38:06 +03:00
if err != nil {
2021-10-18 16:25:20 +03:00
return nil, err
2021-10-15 12:38:06 +03:00
}
2021-10-18 16:25:20 +03:00
return nil, errors.New("not yet implemented")
2021-10-18 15:47:27 +03:00
}