168 lines
3.4 KiB
Go
168 lines
3.4 KiB
Go
package keycard
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
|
|
"github.com/status-im/keycard-go/apdu"
|
|
"github.com/status-im/keycard-go/derivationpath"
|
|
"github.com/status-im/keycard-go/globalplatform"
|
|
)
|
|
|
|
const (
|
|
InsInit = uint8(0xFE)
|
|
InsOpenSecureChannel = uint8(0x10)
|
|
InsMutuallyAuthenticate = uint8(0x11)
|
|
InsPair = uint8(0x12)
|
|
InsGetStatus = uint8(0xF2)
|
|
InsGenerateKey = uint8(0xD4)
|
|
InsVerifyPIN = uint8(0x20)
|
|
InsDeriveKey = uint8(0xD1)
|
|
InsSign = uint8(0xC0)
|
|
|
|
P1PairingFirstStep = uint8(0x00)
|
|
P1PairingFinalStep = uint8(0x01)
|
|
P1GetStatusApplication = uint8(0x00)
|
|
P1GetStatusKeyPath = uint8(0x01)
|
|
P1DeriveKeyFromMaster = uint8(0x00)
|
|
P1DeriveKeyFromParent = uint8(0x01)
|
|
P1DeriveKeyFromCurrent = uint8(0x10)
|
|
)
|
|
|
|
func NewCommandInit(data []byte) *apdu.Command {
|
|
return apdu.NewCommand(
|
|
globalplatform.ClaGp,
|
|
InsInit,
|
|
uint8(0x00),
|
|
uint8(0x00),
|
|
data,
|
|
)
|
|
}
|
|
|
|
func NewCommandPairFirstStep(challenge []byte) *apdu.Command {
|
|
return apdu.NewCommand(
|
|
globalplatform.ClaGp,
|
|
InsPair,
|
|
P1PairingFirstStep,
|
|
uint8(0x00),
|
|
challenge,
|
|
)
|
|
}
|
|
|
|
func NewCommandPairFinalStep(cryptogramHash []byte) *apdu.Command {
|
|
return apdu.NewCommand(
|
|
globalplatform.ClaGp,
|
|
InsPair,
|
|
P1PairingFinalStep,
|
|
uint8(0x00),
|
|
cryptogramHash,
|
|
)
|
|
}
|
|
|
|
func NewCommandOpenSecureChannel(pairingIndex uint8, pubKey []byte) *apdu.Command {
|
|
return apdu.NewCommand(
|
|
globalplatform.ClaGp,
|
|
InsOpenSecureChannel,
|
|
pairingIndex,
|
|
uint8(0x00),
|
|
pubKey,
|
|
)
|
|
}
|
|
|
|
func NewCommandMutuallyAuthenticate(data []byte) *apdu.Command {
|
|
return apdu.NewCommand(
|
|
globalplatform.ClaGp,
|
|
InsMutuallyAuthenticate,
|
|
uint8(0x00),
|
|
uint8(0x00),
|
|
data,
|
|
)
|
|
}
|
|
|
|
func NewCommandGetStatus(p1 uint8) *apdu.Command {
|
|
return apdu.NewCommand(
|
|
globalplatform.ClaGp,
|
|
InsGetStatus,
|
|
p1,
|
|
uint8(0x00),
|
|
[]byte{},
|
|
)
|
|
}
|
|
|
|
func NewCommandGetStatusApplication() *apdu.Command {
|
|
return NewCommandGetStatus(P1GetStatusApplication)
|
|
}
|
|
|
|
func NewCommandGetStatusKeyPath() *apdu.Command {
|
|
return NewCommandGetStatus(P1GetStatusKeyPath)
|
|
}
|
|
|
|
func NewCommandGenerateKey() *apdu.Command {
|
|
return apdu.NewCommand(
|
|
globalplatform.ClaGp,
|
|
InsGenerateKey,
|
|
uint8(0),
|
|
uint8(0),
|
|
[]byte{},
|
|
)
|
|
}
|
|
|
|
func NewCommandVerifyPIN(pin string) *apdu.Command {
|
|
return apdu.NewCommand(
|
|
globalplatform.ClaGp,
|
|
InsVerifyPIN,
|
|
uint8(0),
|
|
uint8(0),
|
|
[]byte(pin),
|
|
)
|
|
}
|
|
|
|
func NewCommandDeriveKey(pathStr string) (*apdu.Command, error) {
|
|
startingPoint, path, err := derivationpath.Parse(pathStr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var p1 uint8
|
|
switch startingPoint {
|
|
case derivationpath.StartingPointMaster:
|
|
p1 = P1DeriveKeyFromMaster
|
|
case derivationpath.StartingPointParent:
|
|
p1 = P1DeriveKeyFromParent
|
|
case derivationpath.StartingPointCurrent:
|
|
p1 = P1DeriveKeyFromCurrent
|
|
default:
|
|
return nil, fmt.Errorf("invalid startingPoint %d", startingPoint)
|
|
}
|
|
|
|
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,
|
|
uint8(0),
|
|
data.Bytes(),
|
|
), nil
|
|
}
|
|
|
|
func NewCommandSign(data []byte) (*apdu.Command, error) {
|
|
if len(data) != 32 {
|
|
return nil, fmt.Errorf("data length must be 32, got %d", len(data))
|
|
}
|
|
|
|
return apdu.NewCommand(
|
|
globalplatform.ClaGp,
|
|
InsSign,
|
|
uint8(0),
|
|
uint8(0),
|
|
data,
|
|
), nil
|
|
}
|