Merge pull request #8 from keycard-tech/keycard-310

Add 3.1 functionalities
This commit is contained in:
Michele Balistreri 2025-01-20 19:22:23 +09:00 committed by GitHub
commit bd8b4820bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 102 additions and 14 deletions

4
go.mod
View File

@ -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
View File

@ -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=

View File

@ -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) {

View File

@ -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 {

View File

@ -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,

View File

@ -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()

View File

@ -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 {

View File

@ -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 (