keycard-go/commands.go

402 lines
8.3 KiB
Go
Raw Normal View History

2019-03-11 10:05:28 +00:00
package keycard
2018-10-22 17:33:53 +00:00
import (
2019-03-15 00:03:24 +00:00
"bytes"
"encoding/binary"
"fmt"
2019-03-01 17:44:07 +00:00
"github.com/status-im/keycard-go/apdu"
2019-03-15 00:03:24 +00:00
"github.com/status-im/keycard-go/derivationpath"
2019-03-01 17:44:07 +00:00
"github.com/status-im/keycard-go/globalplatform"
2018-10-22 17:33:53 +00:00
)
const (
2019-03-28 10:52:17 +00:00
InsInit = 0xFE
InsFactoryReset = 0xFD
2019-03-28 10:52:17 +00:00
InsOpenSecureChannel = 0x10
InsMutuallyAuthenticate = 0x11
InsPair = 0x12
InsUnpair = 0x13
InsIdentify = 0x14
2019-03-28 10:52:17 +00:00
InsGetStatus = 0xF2
InsGenerateKey = 0xD4
InsRemoveKey = 0xD3
InsVerifyPIN = 0x20
InsChangePIN = 0x21
2021-10-22 09:47:47 +00:00
InsUnblockPIN = 0x22
2019-03-28 10:52:17 +00:00
InsDeriveKey = 0xD1
InsExportKey = 0xC2
2019-03-28 10:52:17 +00:00
InsSign = 0xC0
InsSetPinlessPath = 0xC1
2022-08-02 10:18:52 +00:00
InsGetData = 0xCA
2020-01-07 11:56:50 +00:00
InsLoadKey = 0xD0
2021-10-19 09:22:39 +00:00
InsGenerateMnemonic = 0xD2
2022-08-02 10:18:52 +00:00
InsStoreData = 0xE2
2019-03-28 10:52:17 +00:00
2019-04-24 13:30:14 +00:00
P1PairingFirstStep = 0x00
P1PairingFinalStep = 0x01
P1GetStatusApplication = 0x00
P1GetStatusKeyPath = 0x01
P1DeriveKeyFromMaster = 0x00
P1DeriveKeyFromParent = 0x40
P1DeriveKeyFromCurrent = 0x80
P1ChangePinPIN = 0x00
P1ChangePinPUK = 0x01
P1ChangePinPairingSecret = 0x02
P1SignCurrentKey = 0x00
P1SignDerive = 0x01
P1SignDeriveAndMakeCurrent = 0x02
P1SignPinless = 0x03
2022-08-02 10:18:52 +00:00
P1StoreDataPublic = 0x00
P1StoreDataNDEF = 0x01
P1StoreDataCash = 0x02
2020-01-07 11:56:50 +00:00
P1ExportKeyCurrent = 0x00
P1ExportKeyDerive = 0x01
P1ExportKeyDeriveAndMakeCurrent = 0x02
P2ExportKeyPrivateAndPublic = 0x00
P2ExportKeyPublicOnly = 0x01
P2ExportKeyExtendedPublic = 0x02
2020-01-07 11:56:50 +00:00
P1LoadKeySeed = 0x03
P1FactoryResetMagic = 0xAA
P2FactoryResetMagic = 0x55
2019-04-24 13:30:14 +00:00
SwNoAvailablePairingSlots = 0x6A84
2018-10-22 17:33:53 +00:00
)
func NewCommandInit(data []byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsInit,
2019-03-28 10:52:17 +00:00
0,
0,
2018-10-22 17:33:53 +00:00
data,
)
}
2018-10-24 16:16:14 +00:00
func NewCommandPairFirstStep(challenge []byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsPair,
P1PairingFirstStep,
2019-03-28 10:52:17 +00:00
0,
2018-10-24 16:16:14 +00:00
challenge,
)
}
func NewCommandPairFinalStep(cryptogramHash []byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsPair,
P1PairingFinalStep,
2019-03-28 10:52:17 +00:00
0,
2018-10-24 16:16:14 +00:00
cryptogramHash,
)
}
2019-03-27 12:05:46 +00:00
func NewCommandUnpair(index uint8) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsUnpair,
index,
2019-03-28 10:52:17 +00:00
0,
2019-03-27 12:05:46 +00:00
[]byte{},
)
}
func NewCommandIdentify(challenge []byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsIdentify,
0,
0,
challenge,
)
}
2018-10-24 16:16:14 +00:00
func NewCommandOpenSecureChannel(pairingIndex uint8, pubKey []byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsOpenSecureChannel,
pairingIndex,
2019-03-28 10:52:17 +00:00
0,
2018-10-24 16:16:14 +00:00
pubKey,
)
}
2018-11-06 17:38:13 +00:00
func NewCommandMutuallyAuthenticate(data []byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsMutuallyAuthenticate,
2019-03-28 10:52:17 +00:00
0,
0,
2018-11-06 17:38:13 +00:00
data,
)
}
2018-11-07 13:39:58 +00:00
func NewCommandGetStatus(p1 uint8) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsGetStatus,
p1,
2019-03-28 10:52:17 +00:00
0,
2018-11-07 13:39:58 +00:00
[]byte{},
)
}
2019-03-14 15:31:30 +00:00
func NewCommandGenerateKey() *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsGenerateKey,
2019-03-28 10:52:17 +00:00
0,
0,
2019-03-14 15:31:30 +00:00
[]byte{},
)
}
2021-10-19 09:22:39 +00:00
func NewCommandGenerateMnemonic(checksumSize byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsGenerateMnemonic,
checksumSize,
0,
[]byte{},
)
}
2019-03-27 10:22:20 +00:00
func NewCommandRemoveKey() *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsRemoveKey,
2019-03-28 10:52:17 +00:00
0,
0,
2019-03-27 10:22:20 +00:00
[]byte{},
)
}
2019-03-14 15:31:30 +00:00
func NewCommandVerifyPIN(pin string) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsVerifyPIN,
2019-03-28 10:52:17 +00:00
0,
0,
2019-03-14 15:31:30 +00:00
[]byte(pin),
)
}
2019-03-15 00:03:24 +00:00
2019-03-27 13:34:55 +00:00
func NewCommandChangePIN(pin string) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsChangePIN,
P1ChangePinPIN,
2019-03-28 10:52:17 +00:00
0,
2019-03-27 13:34:55 +00:00
[]byte(pin),
)
}
2021-10-22 09:47:47 +00:00
func NewCommandUnblockPIN(puk string, newPIN string) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsUnblockPIN,
0,
0,
[]byte(puk+newPIN),
)
}
func NewCommandChangePUK(puk string) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsChangePIN,
P1ChangePinPUK,
2019-03-28 10:52:17 +00:00
0,
[]byte(puk),
)
}
func NewCommandChangePairingSecret(secret []byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsChangePIN,
P1ChangePinPairingSecret,
2019-03-28 10:52:17 +00:00
0,
secret,
)
}
2020-01-07 11:56:50 +00:00
func NewCommandLoadSeed(seed []byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsLoadKey,
P1LoadKeySeed,
0,
seed,
)
}
2019-03-15 00:03:24 +00:00
func NewCommandDeriveKey(pathStr string) (*apdu.Command, error) {
2019-03-15 15:40:22 +00:00
startingPoint, path, err := derivationpath.Decode(pathStr)
2019-03-15 00:03:24 +00:00
if err != nil {
return nil, err
}
p1, err := derivationP1FromStartingPoint(startingPoint)
if err != nil {
return nil, err
2019-03-15 00:03:24 +00:00
}
data := new(bytes.Buffer)
for _, segment := range path {
if err := binary.Write(data, binary.BigEndian, segment); err != nil {
return nil, err
}
}
return apdu.NewCommand(
globalplatform.ClaGp,
InsDeriveKey,
p1,
2019-03-28 10:52:17 +00:00
0,
2019-03-15 00:03:24 +00:00
data.Bytes(),
), nil
}
// Export a key
2022-08-04 08:08:55 +00:00
//
// @param {p1}
2022-08-04 08:08:55 +00:00
// 0x00: current key - returns the key that is currently loaded and ready for signing. Does not use derivation path
// 0x01: derive - returns derived key
// 0x02: derive and make current - returns derived key and also sets it to the current key
// @param {p2}
// 0x00: return public and private key pair
// 0x01: return only the public key
// 0x02: return extended public key
2022-08-04 08:08:55 +00:00
// @param {pathStr}
// Derivation path of format "m/x/x/x/x/x", e.g. "m/44'/0'/0'/0/0"
func NewCommandExportKey(p1 uint8, p2 uint8, pathStr string) (*apdu.Command, error) {
startingPoint, path, err := derivationpath.Decode(pathStr)
if err != nil {
return nil, err
}
deriveP1, err := derivationP1FromStartingPoint(startingPoint)
if err != nil {
return nil, err
}
data := new(bytes.Buffer)
for _, segment := range path {
if err := binary.Write(data, binary.BigEndian, segment); err != nil {
return nil, err
}
}
return apdu.NewCommand(
globalplatform.ClaGp,
InsExportKey,
p1|deriveP1,
p2,
data.Bytes(),
), nil
}
2019-03-16 09:03:35 +00:00
func NewCommandSetPinlessPath(pathStr string) (*apdu.Command, error) {
startingPoint, path, err := derivationpath.Decode(pathStr)
if err != nil {
return nil, err
}
2019-04-09 08:25:56 +00:00
if len(path) > 0 && startingPoint != derivationpath.StartingPointMaster {
2019-03-16 09:03:35 +00:00
return nil, fmt.Errorf("pinless path must be set with an absolute path")
}
data := new(bytes.Buffer)
for _, segment := range path {
if err := binary.Write(data, binary.BigEndian, segment); err != nil {
return nil, err
}
}
return apdu.NewCommand(
globalplatform.ClaGp,
InsSetPinlessPath,
2019-03-28 10:52:17 +00:00
0,
0,
2019-03-16 09:03:35 +00:00
data.Bytes(),
), nil
}
2019-11-14 11:46:15 +00:00
func NewCommandSign(data []byte, p1 uint8, pathStr string) (*apdu.Command, error) {
2019-03-15 00:03:24 +00:00
if len(data) != 32 {
return nil, fmt.Errorf("data length must be 32, got %d", len(data))
}
2019-11-14 11:46:15 +00:00
if p1 == P1SignDerive || p1 == P1SignDeriveAndMakeCurrent {
_, path, err := derivationpath.Decode(pathStr)
if err != nil {
return nil, err
}
pathData := new(bytes.Buffer)
for _, segment := range path {
if err := binary.Write(pathData, binary.BigEndian, segment); err != nil {
return nil, err
}
}
data = append(data, pathData.Bytes()...)
}
2019-03-15 00:03:24 +00:00
return apdu.NewCommand(
globalplatform.ClaGp,
InsSign,
2019-04-09 08:25:56 +00:00
p1,
1,
2019-03-15 00:03:24 +00:00
data,
), nil
}
2022-08-02 10:18:52 +00:00
func NewCommandGetData(typ uint8) *apdu.Command {
return apdu.NewCommand(
2022-08-02 10:18:52 +00:00
globalplatform.ClaGp,
InsGetData,
typ,
0,
[]byte{},
2022-08-02 10:18:52 +00:00
)
}
func NewCommandStoreData(typ uint8, data []byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsStoreData,
typ,
0,
data,
)
}
func NewCommandFactoryReset() *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsFactoryReset,
P1FactoryResetMagic,
P2FactoryResetMagic,
[]byte{},
)
}
// Internal function. Get the type of starting point for the derivation path.
// Used for both DeriveKey and ExportKey
func derivationP1FromStartingPoint(s derivationpath.StartingPoint) (uint8, error) {
switch s {
case derivationpath.StartingPointMaster:
return P1DeriveKeyFromMaster, nil
case derivationpath.StartingPointParent:
return P1DeriveKeyFromParent, nil
case derivationpath.StartingPointCurrent:
return P1DeriveKeyFromCurrent, nil
default:
return uint8(0), fmt.Errorf("invalid startingPoint %d", s)
}
}