mirror of
https://github.com/status-im/status-keycard-go.git
synced 2025-02-06 11:33:32 +00:00
Merge pull request #8 from keycard-tech/keycard-310
Add 3.1 functionalities
This commit is contained in:
commit
bd8b4820bf
4
go.mod
4
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
|
4
go.sum
4
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=
|
||||
|
@ -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 {
|
||||
@ -326,16 +337,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 +354,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) {
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
@ -128,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
|
||||
|
||||
@ -159,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
|
||||
@ -372,7 +394,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()
|
||||
|
@ -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 {
|
||||
@ -24,6 +26,7 @@ type KeycardFlow struct {
|
||||
pairings *pairing.Store
|
||||
params FlowParams
|
||||
cardInfo cardStatus
|
||||
knownCA []string
|
||||
}
|
||||
|
||||
func NewFlow(storageDir string) (*KeycardFlow, error) {
|
||||
@ -36,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")
|
||||
@ -326,17 +341,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
|
||||
|
||||
//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 {
|
||||
|
@ -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 (
|
||||
|
Loading…
x
Reference in New Issue
Block a user