mirror of
https://github.com/status-im/keycard-cli.git
synced 2025-02-28 20:00:38 +00:00
add cash applet features
This commit is contained in:
parent
cc4e5638f2
commit
90247e62fd
4
Gopkg.lock
generated
4
Gopkg.lock
generated
@ -42,7 +42,7 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "develop"
|
||||
digest = "1:428d2c2f86c07c78f39d379b809c317b3792d0248e76d60bf78dcf101596c770"
|
||||
digest = "1:9b4833a900fe5a52f64346bde7749ea58797e8d010ca05d5a78e2ba5ca1864cd"
|
||||
name = "github.com/status-im/keycard-go"
|
||||
packages = [
|
||||
".",
|
||||
@ -57,7 +57,7 @@
|
||||
"types",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "9d48af884d5b92339229bf57df18d0f882ba70be"
|
||||
revision = "6dd40a46baa0d4c8b6fcb69f7e2362e21d89df4d"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -7,9 +7,12 @@ gp-select
|
||||
gp-open-secure-channel
|
||||
gp-delete D2760000850101
|
||||
gp-delete A00000080400010101
|
||||
gp-delete A00000080400010301
|
||||
gp-delete A0000008040001
|
||||
gp-load _assets/keycard_v2.2.1.cap A0000008040001
|
||||
gp-load _assets/keycard_v3.0.cap A0000008040001
|
||||
# NDEF applet
|
||||
gp-install-for-install A0000008040001 A000000804000102 D2760000850101 0024d40f12616e64726f69642e636f6d3a706b67696d2e7374617475732e657468657265756d
|
||||
# Keycard applet
|
||||
gp-install-for-install A0000008040001 A000000804000101 A00000080400010101
|
||||
# Cash applet
|
||||
gp-install-for-install A0000008040001 A000000804000103 A00000080400010301
|
||||
|
@ -1,43 +0,0 @@
|
||||
# select with raw apdu command
|
||||
gp-send-apdu 00A4040000
|
||||
|
||||
# install
|
||||
gp-open-secure-channel
|
||||
gp-delete D2760000850101
|
||||
gp-delete A00000080400010101
|
||||
gp-delete A0000008040001
|
||||
gp-load _assets/keycard_v2.2.1.cap A0000008040001
|
||||
gp-install-for-install A0000008040001 A000000804000102 D2760000850101 0024d40f12616e64726f69642e636f6d3a706b67696d2e7374617475732e657468657265756d
|
||||
gp-install-for-install A0000008040001 A000000804000101 A00000080400010101
|
||||
|
||||
# init
|
||||
keycard-select
|
||||
keycard-init
|
||||
|
||||
# pair
|
||||
keycard-select
|
||||
keycard-pair
|
||||
keycard-open-secure-channel
|
||||
|
||||
# get status
|
||||
keycard-get-status
|
||||
|
||||
# verify PIN
|
||||
keycard-verify-pin {{ session_pin }}
|
||||
|
||||
# change secrets
|
||||
keycard-change-pin 888888
|
||||
keycard-change-puk 111222333444
|
||||
keycard-change-pairing-secret foobarbaz
|
||||
|
||||
# sign
|
||||
keycard-generate-key
|
||||
keycard-derive-key m/44'/60'/0'/0/0
|
||||
keycard-sign 0000000000000000000000000000000000000000000000000000000000000000
|
||||
|
||||
# remove master key
|
||||
keycard-remove-key
|
||||
|
||||
# unpair and check card status
|
||||
keycard-unpair {{ session_pairing_index }}
|
||||
keycard-select
|
@ -15,8 +15,9 @@ var (
|
||||
errCardNotInitialized = errors.New("card not initialized")
|
||||
errCardAlreadyInitialized = errors.New("card already initialized")
|
||||
|
||||
ErrNotInitialized = errors.New("card not initialized")
|
||||
ErrNotInstalled = errors.New("applet not initialized")
|
||||
ErrNotInitialized = errors.New("card not initialized")
|
||||
ErrNotInstalled = errors.New("applet not initialized")
|
||||
ErrCashNotInstalled = errors.New("cash applet not initialized")
|
||||
)
|
||||
|
||||
// Initializer defines a struct with methods to install applets and initialize a card.
|
||||
|
@ -83,6 +83,12 @@ func (i *Installer) Install(capFile *os.File, overwriteApplet bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("installing Cash applet")
|
||||
if err = cmdSet.InstallCashApplet(); err != nil {
|
||||
logger.Error("installing Cash applet failed", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
elapsed := time.Now().Sub(startTime)
|
||||
logger.Info(fmt.Sprintf("installation completed in %f seconds", elapsed.Seconds()))
|
||||
return err
|
||||
|
61
shell.go
61
shell.go
@ -94,6 +94,7 @@ type Shell struct {
|
||||
Secrets *keycard.Secrets
|
||||
gpCmdSet *globalplatform.CommandSet
|
||||
kCmdSet *keycard.CommandSet
|
||||
cashCmdSet *keycard.CashCommandSet
|
||||
commands map[string]shellCommand
|
||||
out *bytes.Buffer
|
||||
tplFuncMap template.FuncMap
|
||||
@ -103,11 +104,12 @@ func NewShell(t keycardio.Transmitter) *Shell {
|
||||
c := keycardio.NewNormalChannel(t)
|
||||
|
||||
s := &Shell{
|
||||
t: t,
|
||||
c: c,
|
||||
kCmdSet: keycard.NewCommandSet(c),
|
||||
gpCmdSet: globalplatform.NewCommandSet(c),
|
||||
out: new(bytes.Buffer),
|
||||
t: t,
|
||||
c: c,
|
||||
kCmdSet: keycard.NewCommandSet(c),
|
||||
cashCmdSet: keycard.NewCashCommandSet(c),
|
||||
gpCmdSet: globalplatform.NewCommandSet(c),
|
||||
out: new(bytes.Buffer),
|
||||
}
|
||||
|
||||
tplFuncs := &TemplateFuncs{s}
|
||||
@ -143,6 +145,8 @@ func NewShell(t keycardio.Transmitter) *Shell {
|
||||
"keycard-sign-pinless": s.commandKeycardSignPinless,
|
||||
"keycard-sign-message-pinless": s.commandKeycardSignMessagePinless,
|
||||
"keycard-set-pinless-path": s.commandKeycardSetPinlessPath,
|
||||
"cash-select": s.commandCashSelect,
|
||||
"cash-sign": s.commandCashSign,
|
||||
}
|
||||
|
||||
return s
|
||||
@ -259,7 +263,7 @@ func (s *Shell) commandGPDelete(args ...string) error {
|
||||
|
||||
logger.Info(fmt.Sprintf("delete %x", aid))
|
||||
|
||||
return s.gpCmdSet.Delete(aid)
|
||||
return s.gpCmdSet.DeleteObject(aid)
|
||||
}
|
||||
|
||||
func (s *Shell) commandGPLoad(args ...string) error {
|
||||
@ -781,6 +785,51 @@ func (s *Shell) commandKeycardSetPinlessPath(args ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Shell) commandCashSelect(args ...string) error {
|
||||
if err := s.requireArgs(args, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("select cash")
|
||||
err := s.cashCmdSet.Select()
|
||||
info := s.cashCmdSet.CashApplicationInfo
|
||||
|
||||
s.write(fmt.Sprintf("Installed: %v\n", info.Installed))
|
||||
s.write(fmt.Sprintf("PublicKey: %x\n", info.PublicKey))
|
||||
s.write(fmt.Sprintf("PublicKeyData: %x\n", info.PublicKeyData))
|
||||
s.write(fmt.Sprintf("Version: %x\n\n", info.Version))
|
||||
|
||||
if e, ok := err.(*apdu.ErrBadResponse); ok && e.Sw == globalplatform.SwFileNotFound {
|
||||
logger.Error("select cash failed", "error", err)
|
||||
return ErrCashNotInstalled
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Shell) commandCashSign(args ...string) error {
|
||||
if err := s.requireArgs(args, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := s.parseHex(args[0])
|
||||
if err != nil {
|
||||
logger.Error("failed parsing hex data", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("sign")
|
||||
sig, err := s.cashCmdSet.Sign(data)
|
||||
if err != nil {
|
||||
logger.Error("sign failed", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
s.writeSignatureInfo(sig)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Shell) requireArgs(args []string, possibleArgsN ...int) error {
|
||||
for _, n := range possibleArgsN {
|
||||
if len(args) == n {
|
||||
|
70
vendor/github.com/status-im/keycard-go/cash_command_set.go
generated
vendored
Normal file
70
vendor/github.com/status-im/keycard-go/cash_command_set.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
package keycard
|
||||
|
||||
import (
|
||||
"github.com/status-im/keycard-go/apdu"
|
||||
"github.com/status-im/keycard-go/globalplatform"
|
||||
"github.com/status-im/keycard-go/identifiers"
|
||||
"github.com/status-im/keycard-go/types"
|
||||
)
|
||||
|
||||
type CashCommandSet struct {
|
||||
c types.Channel
|
||||
CashApplicationInfo *types.CashApplicationInfo
|
||||
}
|
||||
|
||||
func NewCashCommandSet(c types.Channel) *CashCommandSet {
|
||||
return &CashCommandSet{
|
||||
c: c,
|
||||
CashApplicationInfo: &types.CashApplicationInfo{},
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *CashCommandSet) Select() error {
|
||||
cmd := globalplatform.NewCommandSelect(identifiers.CashInstanceAID)
|
||||
cmd.SetLe(0)
|
||||
resp, err := cs.c.Send(cmd)
|
||||
if err = cs.checkOK(resp, err); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
appInfo, err := types.ParseCashApplicationInfo(resp.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cs.CashApplicationInfo = appInfo
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs *CashCommandSet) Sign(data []byte) (*types.Signature, error) {
|
||||
cmd, err := NewCommandSign(data, 0x00, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := cs.c.Send(cmd)
|
||||
if err = cs.checkOK(resp, err); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return types.ParseSignature(data, resp.Data)
|
||||
}
|
||||
|
||||
func (cs *CashCommandSet) checkOK(resp *apdu.Response, err error, allowedResponses ...uint16) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(allowedResponses) == 0 {
|
||||
allowedResponses = []uint16{apdu.SwOK}
|
||||
}
|
||||
|
||||
for _, code := range allowedResponses {
|
||||
if code == resp.Sw {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return apdu.NewErrBadResponse(resp.Sw, "unexpected response")
|
||||
}
|
6
vendor/github.com/status-im/keycard-go/command_set.go
generated
vendored
6
vendor/github.com/status-im/keycard-go/command_set.go
generated
vendored
@ -23,8 +23,9 @@ type CommandSet struct {
|
||||
|
||||
func NewCommandSet(c types.Channel) *CommandSet {
|
||||
return &CommandSet{
|
||||
c: c,
|
||||
sc: NewSecureChannel(c),
|
||||
c: c,
|
||||
sc: NewSecureChannel(c),
|
||||
ApplicationInfo: &types.ApplicationInfo{},
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +46,6 @@ func (cs *CommandSet) Select() error {
|
||||
cmd.SetLe(0)
|
||||
resp, err := cs.c.Send(cmd)
|
||||
if err = cs.checkOK(resp, err); err != nil {
|
||||
cs.ApplicationInfo = &types.ApplicationInfo{}
|
||||
return err
|
||||
}
|
||||
|
||||
|
40
vendor/github.com/status-im/keycard-go/globalplatform/command_set.go
generated
vendored
40
vendor/github.com/status-im/keycard-go/globalplatform/command_set.go
generated
vendored
@ -57,29 +57,19 @@ func (cs *CommandSet) DeleteKeycardInstancesAndPackage() error {
|
||||
return ErrSecureChannelNotOpen
|
||||
}
|
||||
|
||||
instanceAID, err := identifiers.KeycardInstanceAID(identifiers.KeycardDefaultInstanceIndex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ids := [][]byte{
|
||||
identifiers.NdefInstanceAID,
|
||||
instanceAID,
|
||||
identifiers.PackageAID,
|
||||
}
|
||||
|
||||
for _, aid := range ids {
|
||||
err := cs.Delete(aid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return cs.DeleteObjectAndRelatedObject(identifiers.PackageAID)
|
||||
}
|
||||
|
||||
func (cs *CommandSet) Delete(aid []byte) error {
|
||||
cmd := NewCommandDelete(aid)
|
||||
func (cs *CommandSet) DeleteObject(aid []byte) error {
|
||||
return cs.Delete(aid, P2DeleteObject)
|
||||
}
|
||||
|
||||
func (cs *CommandSet) DeleteObjectAndRelatedObject(aid []byte) error {
|
||||
return cs.Delete(aid, P2DeleteObjectAndRelatedObject)
|
||||
}
|
||||
|
||||
func (cs *CommandSet) Delete(aid []byte, p2 uint8) error {
|
||||
cmd := NewCommandDelete(aid, p2)
|
||||
resp, err := cs.sc.Send(cmd)
|
||||
return cs.checkOK(resp, err, SwOK, SwReferencedDataNotFound)
|
||||
}
|
||||
@ -137,6 +127,14 @@ func (cs *CommandSet) InstallKeycardApplet() error {
|
||||
[]byte{})
|
||||
}
|
||||
|
||||
func (cs *CommandSet) InstallCashApplet() error {
|
||||
return cs.InstallForInstall(
|
||||
identifiers.PackageAID,
|
||||
identifiers.CashAID,
|
||||
identifiers.CashInstanceAID,
|
||||
[]byte{})
|
||||
}
|
||||
|
||||
func (cs *CommandSet) InstallForInstall(packageAID, appletAID, instanceAID, params []byte) error {
|
||||
cmd := NewCommandInstallForInstall(packageAID, appletAID, instanceAID, params)
|
||||
resp, err := cs.sc.Send(cmd)
|
||||
|
8
vendor/github.com/status-im/keycard-go/globalplatform/commands.go
generated
vendored
8
vendor/github.com/status-im/keycard-go/globalplatform/commands.go
generated
vendored
@ -31,7 +31,9 @@ const (
|
||||
P1GetStatusExecLoadFiles = 0x20
|
||||
P1GetStatusExecLoadFilesAndModules = 0x10
|
||||
|
||||
P2GetStatusTLVData = 0x02
|
||||
P2GetStatusTLVData = 0x02
|
||||
P2DeleteObject = 0x00
|
||||
P2DeleteObjectAndRelatedObject = 0x80
|
||||
|
||||
Sw1ResponseDataIncomplete = 0x61
|
||||
|
||||
@ -108,7 +110,7 @@ func NewCommandGetResponse(length uint8) *apdu.Command {
|
||||
}
|
||||
|
||||
// NewCommandDelete returns a Delete command as defined in the globalplatform specifications.
|
||||
func NewCommandDelete(aid []byte) *apdu.Command {
|
||||
func NewCommandDelete(aid []byte, p2 uint8) *apdu.Command {
|
||||
data := []byte{tagDeleteAID, byte(len(aid))}
|
||||
data = append(data, aid...)
|
||||
|
||||
@ -116,7 +118,7 @@ func NewCommandDelete(aid []byte) *apdu.Command {
|
||||
ClaGp,
|
||||
InsDelete,
|
||||
0,
|
||||
0,
|
||||
p2,
|
||||
data,
|
||||
)
|
||||
}
|
||||
|
9
vendor/github.com/status-im/keycard-go/identifiers/identifiers.go
generated
vendored
9
vendor/github.com/status-im/keycard-go/identifiers/identifiers.go
generated
vendored
@ -6,11 +6,16 @@ var (
|
||||
GlobalPlatformDefaultKey = []byte{0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f}
|
||||
KeycardDevelopmentKey = []byte{0xc2, 0x12, 0xe0, 0x73, 0xff, 0x8b, 0x4b, 0xbf, 0xaf, 0xf4, 0xde, 0x8a, 0xb6, 0x55, 0x22, 0x1f}
|
||||
|
||||
PackageAID = []byte{0xA0, 0x00, 0x00, 0x08, 0x04, 0x00, 0x01}
|
||||
KeycardAID = []byte{0xA0, 0x00, 0x00, 0x08, 0x04, 0x00, 0x01, 0x01}
|
||||
PackageAID = []byte{0xA0, 0x00, 0x00, 0x08, 0x04, 0x00, 0x01}
|
||||
|
||||
KeycardAID = []byte{0xA0, 0x00, 0x00, 0x08, 0x04, 0x00, 0x01, 0x01}
|
||||
|
||||
NdefAID = []byte{0xA0, 0x00, 0x00, 0x08, 0x04, 0x00, 0x01, 0x02}
|
||||
NdefInstanceAID = []byte{0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01}
|
||||
|
||||
CashAID = []byte{0xA0, 0x00, 0x00, 0x08, 0x04, 0x00, 0x01, 0x03}
|
||||
CashInstanceAID = []byte{0xA0, 0x00, 0x00, 0x08, 0x04, 0x00, 0x01, 0x03, 0x01}
|
||||
|
||||
KeycardDefaultInstanceIndex = 1
|
||||
|
||||
ErrInvalidInstanceIndex = errors.New("instance index must be between 1 and 255")
|
||||
|
41
vendor/github.com/status-im/keycard-go/types/cash_application_info.go
generated
vendored
Normal file
41
vendor/github.com/status-im/keycard-go/types/cash_application_info.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
package types
|
||||
|
||||
import "github.com/status-im/keycard-go/apdu"
|
||||
|
||||
type CashApplicationInfo struct {
|
||||
Installed bool
|
||||
PublicKey []byte
|
||||
PublicKeyData []byte
|
||||
Version []byte
|
||||
}
|
||||
|
||||
func ParseCashApplicationInfo(data []byte) (*CashApplicationInfo, error) {
|
||||
info := &CashApplicationInfo{}
|
||||
|
||||
if data[0] != TagApplicationInfoTemplate {
|
||||
return nil, ErrWrongApplicationInfoTemplate
|
||||
}
|
||||
|
||||
info.Installed = true
|
||||
|
||||
pubKey, err := apdu.FindTag(data, apdu.Tag{TagApplicationInfoTemplate}, apdu.Tag{0x80})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubKeyData, err := apdu.FindTag(data, apdu.Tag{TagApplicationInfoTemplate}, apdu.Tag{0x82})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
appVersion, err := apdu.FindTag(data, apdu.Tag{TagApplicationInfoTemplate}, apdu.Tag{0x02})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info.PublicKey = pubKey
|
||||
info.PublicKeyData = pubKeyData
|
||||
info.Version = appVersion
|
||||
|
||||
return info, nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user