From adba9b37a6be91ae664d997e61e07ce951a5efb1 Mon Sep 17 00:00:00 2001 From: Michele Balistreri Date: Fri, 29 Nov 2024 16:04:29 +0900 Subject: [PATCH 1/4] add export of xpub keys --- internal/keycard_context.go | 10 +++++----- internal/types.go | 1 + pkg/flow/commands.go | 15 ++++++++++++++- pkg/flow/flow.go | 24 ++++++++++++++++++------ 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/internal/keycard_context.go b/internal/keycard_context.go index b953712..8350059 100644 --- a/internal/keycard_context.go +++ b/internal/keycard_context.go @@ -326,16 +326,16 @@ func (kc *KeycardContext) SignWithPath(data []byte, path string) (*types.Signatu return sig, nil } -func (kc *KeycardContext) ExportKey(derive bool, makeCurrent bool, onlyPublic bool, path string) (*KeyPair, error) { +func (kc *KeycardContext) ExportKey(derive bool, makeCurrent bool, p2 uint8, path string) (*KeyPair, error) { address := "" - privKey, pubKey, err := kc.cmdSet.ExportKey(derive, makeCurrent, onlyPublic, path) + exportedKey, err := kc.cmdSet.ExportKeyExtended(derive, makeCurrent, p2, path) if err != nil { Printf("exportKey failed %+v", err) return nil, err } - if pubKey != nil { - ecdsaPubKey, err := crypto.UnmarshalPubkey(pubKey) + if exportedKey.PubKey() != nil { + ecdsaPubKey, err := crypto.UnmarshalPubkey(exportedKey.PubKey()) if err != nil { return nil, err } @@ -343,7 +343,7 @@ func (kc *KeycardContext) ExportKey(derive bool, makeCurrent bool, onlyPublic bo address = crypto.PubkeyToAddress(*ecdsaPubKey).Hex() } - return &KeyPair{Address: address, PublicKey: pubKey, PrivateKey: privKey}, nil + return &KeyPair{Address: address, PublicKey: exportedKey.PubKey(), PrivateKey: exportedKey.PrivKey(), ChainCode: exportedKey.ChainCode()}, nil } func (kc *KeycardContext) loadSeed(seed []byte) ([]byte, error) { diff --git a/internal/types.go b/internal/types.go index 1b32c3e..84d67f1 100644 --- a/internal/types.go +++ b/internal/types.go @@ -53,6 +53,7 @@ type KeyPair struct { Address string `json:"address"` PublicKey HexString `json:"publicKey"` PrivateKey HexString `json:"privateKey,omitempty"` + ChainCode HexString `json:"chainCode,omitempty"` } type Wallet struct { diff --git a/pkg/flow/commands.go b/pkg/flow/commands.go index f3ddeb8..326950c 100644 --- a/pkg/flow/commands.go +++ b/pkg/flow/commands.go @@ -5,6 +5,7 @@ import ( "io" "strings" + keycard "github.com/status-im/keycard-go" "github.com/status-im/keycard-go/apdu" "github.com/status-im/keycard-go/derivationpath" ktypes "github.com/status-im/keycard-go/types" @@ -34,6 +35,7 @@ func (f *KeycardFlow) selectKeycard(kc *internal.KeycardContext) error { f.cardInfo.instanceUID = internal.Btox(appInfo.InstanceUID) f.cardInfo.keyUID = internal.Btox(appInfo.KeyUID) f.cardInfo.freeSlots = internal.BytesToInt(appInfo.AvailableSlots) + f.cardInfo.version = internal.BytesToInt(appInfo.Version) if !appInfo.Installed { return f.pauseAndRestart(SwapCard, internal.ErrorNotAKeycard) @@ -372,7 +374,18 @@ func (f *KeycardFlow) storeMetadata(kc *internal.KeycardContext) error { } func (f *KeycardFlow) exportKey(kc *internal.KeycardContext, path string, onlyPublic bool) (*internal.KeyPair, error) { - keyPair, err := kc.ExportKey(true, path == MasterPath, onlyPublic, path) + var p2 uint8 + if onlyPublic { + p2 = keycard.P2ExportKeyPublicOnly + } else { + p2 = keycard.P2ExportKeyPrivateAndPublic + } + + return f.exportKeyExtended(kc, path, p2) +} + +func (f *KeycardFlow) exportKeyExtended(kc *internal.KeycardContext, path string, p2 uint8) (*internal.KeyPair, error) { + keyPair, err := kc.ExportKey(true, path == MasterPath, p2, path) if internal.IsSCardError(err) { return nil, restartErr() diff --git a/pkg/flow/flow.go b/pkg/flow/flow.go index b1b1cb6..673b679 100644 --- a/pkg/flow/flow.go +++ b/pkg/flow/flow.go @@ -4,9 +4,10 @@ import ( "errors" "time" - "github.com/status-im/status-keycard-go/signal" + "github.com/status-im/keycard-go" "github.com/status-im/status-keycard-go/internal" "github.com/status-im/status-keycard-go/pkg/pairing" + "github.com/status-im/status-keycard-go/signal" ) type cardStatus struct { @@ -15,6 +16,7 @@ type cardStatus struct { freeSlots int pinRetries int pukRetries int + version int } type KeycardFlow struct { @@ -326,17 +328,27 @@ func (f *KeycardFlow) exportKeysFlow(kc *internal.KeycardContext, recover bool) } result[EIP1581Key] = key - key, err = f.exportKey(kc, WalletRoothPath, true) + var exportP2 uint8 + + if f.cardInfo.version < 0x0310 { + exportP2 = keycard.P2ExportKeyPublicOnly + } else { + exportP2 = keycard.P2ExportKeyExtendedPublic + } + + key, err = f.exportKeyExtended(kc, WalletRoothPath, exportP2) if err != nil { return nil, err } result[WalleRootKey] = key - key, err = f.exportKey(kc, WalletPath, true) - if err != nil { - return nil, err + if key.ChainCode == nil { + key, err = f.exportKey(kc, WalletPath, true) + if err != nil { + return nil, err + } + result[WalletKey] = key } - result[WalletKey] = key key, err = f.exportKey(kc, MasterPath, true) if err != nil { From 690d9a8e855c6020a43a4a397283a82a0f807e2f Mon Sep 17 00:00:00 2001 From: Michele Balistreri Date: Tue, 3 Dec 2024 12:27:37 +0900 Subject: [PATCH 2/4] add card authenticity check --- internal/keycard_context.go | 11 +++++++++++ internal/utils.go | 10 ++++++++++ pkg/flow/commands.go | 22 +++++++++++++++++++++- pkg/flow/flow.go | 13 +++++++++++++ pkg/flow/types.go | 10 ++++++++++ 5 files changed, 65 insertions(+), 1 deletion(-) diff --git a/internal/keycard_context.go b/internal/keycard_context.go index 8350059..e4b9c58 100644 --- a/internal/keycard_context.go +++ b/internal/keycard_context.go @@ -2,6 +2,7 @@ package internal import ( "crypto/sha512" + "encoding/hex" "errors" "runtime" "time" @@ -222,6 +223,16 @@ func (kc *KeycardContext) SelectApplet() (*types.ApplicationInfo, error) { return kc.cmdSet.ApplicationInfo, nil } +func (kc *KeycardContext) Identify() (string, error) { + ca, err := kc.cmdSet.Identify() + + if err != nil { + return "", err + } + + return hex.EncodeToString(ca), nil +} + func (kc *KeycardContext) Pair(pairingPassword string) (*types.PairingInfo, error) { err := kc.cmdSet.Pair(pairingPassword) if err != nil { diff --git a/internal/utils.go b/internal/utils.go index 36d2bca..b431b06 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -43,6 +43,16 @@ func BytesToInt(s []byte) int { return int(binary.BigEndian.Uint32(b[:])) } +func ContainsString(str string, s []string) bool { + for _, v := range s { + if v == str { + return true + } + } + + return false +} + func ToAppInfo(r *ktypes.ApplicationInfo) ApplicationInfo { return ApplicationInfo{ Initialized: r.Initialized, diff --git a/pkg/flow/commands.go b/pkg/flow/commands.go index 326950c..f9b08a9 100644 --- a/pkg/flow/commands.go +++ b/pkg/flow/commands.go @@ -130,6 +130,20 @@ func (f *KeycardFlow) initCard(kc *internal.KeycardContext) error { return restartErr() } +func (f *KeycardFlow) verifyAuthenticity(kc *internal.KeycardContext) error { + if (len(f.knownCA) == 0) || (f.cardInfo.instanceUID == f.params[SkipAuthUID]) { + return nil + } + + ca, err := kc.Identify() + + if (err != nil) || !internal.ContainsString(ca, f.knownCA) { + return authenticityErr() + } + + return nil +} + func (f *KeycardFlow) openSC(kc *internal.KeycardContext, giveup bool) error { var pairing *internal.PairingInfo @@ -161,11 +175,17 @@ func (f *KeycardFlow) openSC(kc *internal.KeycardContext, giveup bool) error { f.pairings.Delete(f.cardInfo.instanceUID) } + err := f.verifyAuthenticity(kc) + + if err != nil { + return err + } + if giveup { return giveupErr() } - err := f.pair(kc) + err = f.pair(kc) if err != nil { return err diff --git a/pkg/flow/flow.go b/pkg/flow/flow.go index 673b679..16f398f 100644 --- a/pkg/flow/flow.go +++ b/pkg/flow/flow.go @@ -26,6 +26,7 @@ type KeycardFlow struct { pairings *pairing.Store params FlowParams cardInfo cardStatus + knownCA []string } func NewFlow(storageDir string) (*KeycardFlow, error) { @@ -38,11 +39,23 @@ func NewFlow(storageDir string) (*KeycardFlow, error) { flow := &KeycardFlow{ wakeUp: make(chan (struct{})), pairings: p, + knownCA: []string{}, } return flow, nil } +func NewFlowWithCA(storageDir string, knownCA []string) (*KeycardFlow, error) { + f, err := NewFlow(storageDir) + + if err != nil { + return nil, err + } + + f.knownCA = knownCA + return f, nil +} + func (f *KeycardFlow) Start(flowType FlowType, params FlowParams) error { if f.state != Idle { return errors.New("already running") diff --git a/pkg/flow/types.go b/pkg/flow/types.go index d3b5e65..42f6e24 100644 --- a/pkg/flow/types.go +++ b/pkg/flow/types.go @@ -7,6 +7,7 @@ type RunState int type restartError struct{} type giveupError struct{} +type authenticityError struct{} func restartErr() (e *restartError) { return &restartError{} @@ -24,6 +25,14 @@ func (e *giveupError) Error() string { return "giveup" } +func authenticityErr() (e *authenticityError) { + return &authenticityError{} +} + +func (e *authenticityError) Error() string { + return "authenticity" +} + const ( GetAppInfo FlowType = iota RecoverAccount @@ -104,6 +113,7 @@ const ( CardMeta = "card-metadata" CardName = "card-name" WalletPaths = "wallet-paths" + SkipAuthUID = "skip-auth-uid" ) const ( From e5b99984753702ccee28988203d630c3cabbec33 Mon Sep 17 00:00:00 2001 From: Michele Balistreri Date: Wed, 11 Dec 2024 10:47:55 +0900 Subject: [PATCH 3/4] export walletkey in all cases for now --- pkg/flow/flow.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/flow/flow.go b/pkg/flow/flow.go index 16f398f..9f5681a 100644 --- a/pkg/flow/flow.go +++ b/pkg/flow/flow.go @@ -355,13 +355,13 @@ func (f *KeycardFlow) exportKeysFlow(kc *internal.KeycardContext, recover bool) } result[WalleRootKey] = key - if key.ChainCode == nil { - key, err = f.exportKey(kc, WalletPath, true) - if err != nil { - return nil, err - } - result[WalletKey] = key + //if key.ChainCode == nil { + key, err = f.exportKey(kc, WalletPath, true) + if err != nil { + return nil, err } + result[WalletKey] = key + //} key, err = f.exportKey(kc, MasterPath, true) if err != nil { From c042eb6df6d39f429d5a2894c31945b4c06cc061 Mon Sep 17 00:00:00 2001 From: Michele Balistreri Date: Mon, 20 Jan 2025 10:55:36 +0100 Subject: [PATCH 4/4] update scard dependency --- go.mod | 4 +--- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 5c731f4..1d3e846 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,9 @@ module github.com/status-im/status-keycard-go go 1.16 require ( - github.com/ebfe/scard v0.0.0-20190212122703-c3d1b1916a95 + github.com/ebfe/scard v0.0.0-20241214075232-7af069cabc25 github.com/ethereum/go-ethereum v1.10.26 github.com/status-im/keycard-go v0.3.3 golang.org/x/crypto v0.1.0 golang.org/x/text v0.4.0 ) - -replace github.com/ebfe/scard => github.com/keycard-tech/scard v0.0.0-20241212105412-f6a0ad2a2912 \ No newline at end of file diff --git a/go.sum b/go.sum index 331ff5b..5360c6c 100644 --- a/go.sum +++ b/go.sum @@ -87,6 +87,8 @@ github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5O github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= +github.com/ebfe/scard v0.0.0-20241214075232-7af069cabc25 h1:vXmXuiy1tgifTqWAAaU+ESu1goRp4B3fdhemWMMrS4g= +github.com/ebfe/scard v0.0.0-20241214075232-7af069cabc25/go.mod h1:BkYEeWL6FbT4Ek+TcOBnPzEKnL7kOq2g19tTQXkorHY= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -205,8 +207,6 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/keycard-tech/scard v0.0.0-20241212105412-f6a0ad2a2912 h1:lu2sIPLxtoL8ZbjstVIPiJDoW/3jE9EdMbbgfNixQdg= -github.com/keycard-tech/scard v0.0.0-20241212105412-f6a0ad2a2912/go.mod h1:BkYEeWL6FbT4Ek+TcOBnPzEKnL7kOq2g19tTQXkorHY= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=