keycard-go/globalplatform/command_set.go

189 lines
3.9 KiB
Go
Raw Normal View History

2019-03-06 09:45:52 +00:00
package globalplatform
import (
"crypto/rand"
"errors"
"fmt"
"os"
"github.com/status-im/keycard-go/apdu"
2019-03-06 10:30:00 +00:00
"github.com/status-im/keycard-go/identifiers"
2019-03-06 09:45:52 +00:00
)
type LoadingCallback = func(loadingBlock, totalBlocks int)
2019-03-06 10:30:00 +00:00
const defaultKeycardInstanceAID = 1
2019-03-06 09:45:52 +00:00
type CommandSet struct {
c Channel
session *Session
}
func NewCommandSet(c Channel) *CommandSet {
return &CommandSet{
c: c,
}
}
func (cs *CommandSet) Select() ([]byte, error) {
2019-03-06 09:45:52 +00:00
cmd := apdu.NewCommand(
0x00,
InsSelect,
uint8(0x04),
uint8(0x00),
nil,
2019-03-06 09:45:52 +00:00
)
cmd.SetLe(0)
2019-03-06 09:45:52 +00:00
resp, err := cs.c.Send(cmd)
if err = cs.checkOK(resp, err); err != nil {
return nil, err
}
// issuer security domain
isd, _ := apdu.FindTag(resp.Data, 0x6F, 0x84)
return isd, err
2019-03-06 09:45:52 +00:00
}
func (cs *CommandSet) OpenSecureChannel() error {
hostChallenge, err := generateHostChallenge()
if err != nil {
return err
}
err = cs.initializeUpdate(hostChallenge)
if err != nil {
return err
}
return cs.externalAuthenticate()
}
func (cs *CommandSet) DeleteKeycardInstancesAndPackage() error {
2019-03-06 10:30:00 +00:00
instanceAID, err := identifiers.KeycardInstanceAID(defaultKeycardInstanceAID)
if err != nil {
return err
}
2019-03-06 09:45:52 +00:00
ids := [][]byte{
2019-03-06 10:30:00 +00:00
identifiers.NdefInstanceAID,
instanceAID,
identifiers.PackageAID,
2019-03-06 09:45:52 +00:00
}
for _, id := range ids {
cmd := NewCommandDelete(id)
resp, err := cs.c.Send(cmd)
if cs.checkOK(resp, err, SwOK, SwReferencedDataNotFound) != nil {
return err
}
}
return nil
}
func (cs *CommandSet) LoadKeycardPackage(capFile *os.File, callback LoadingCallback) error {
preLoad := NewCommandInstallForLoad(identifiers.PackageAID, []byte{})
2019-03-06 09:45:52 +00:00
resp, err := cs.c.Send(preLoad)
if err = cs.checkOK(resp, err); err != nil {
return err
}
load, err := NewLoadCommandStream(capFile)
if err != nil {
return err
}
for load.Next() {
cmd := load.GetCommand()
callback(int(load.Index()), load.BlocksCount())
resp, err = cs.c.Send(cmd)
if err = cs.checkOK(resp, err); err != nil {
return err
}
}
return nil
}
func (cs *CommandSet) InstallNDEFApplet(ndefRecord []byte) error {
2019-03-06 10:30:00 +00:00
return cs.installForInstall(
identifiers.PackageAID,
identifiers.NdefAID,
identifiers.NdefInstanceAID,
ndefRecord)
2019-03-06 09:45:52 +00:00
}
func (cs *CommandSet) InstallKeycardApplet() error {
2019-03-06 10:30:00 +00:00
instanceAID, err := identifiers.KeycardInstanceAID(defaultKeycardInstanceAID)
if err != nil {
return err
}
return cs.installForInstall(
identifiers.PackageAID,
identifiers.KeycardAID,
instanceAID,
[]byte{})
2019-03-06 09:45:52 +00:00
}
func (cs *CommandSet) installForInstall(packageAID, appletAID, instanceAID, params []byte) error {
cmd := NewCommandInstallForInstall(packageAID, appletAID, instanceAID, params)
resp, err := cs.c.Send(cmd)
return cs.checkOK(resp, err)
}
func (cs *CommandSet) initializeUpdate(hostChallenge []byte) error {
cmd := NewCommandInitializeUpdate(hostChallenge)
resp, err := cs.c.Send(cmd)
if err = cs.checkOK(resp, err); err != nil {
return err
}
// verify cryptogram and initialize session keys
2019-03-06 10:30:00 +00:00
keys := NewSCP02Keys(identifiers.CardTestKey, identifiers.CardTestKey)
2019-03-06 09:45:52 +00:00
session, err := NewSession(keys, resp, hostChallenge)
2019-03-06 11:55:57 +00:00
if err != nil {
return err
}
2019-03-06 09:45:52 +00:00
cs.c = NewSecureChannel(session, cs.c)
cs.session = session
return nil
}
func (cs *CommandSet) externalAuthenticate() error {
if cs.session == nil {
return errors.New("session must be initialized using initializeUpdate")
}
encKey := cs.session.Keys().Enc()
cmd, err := NewCommandExternalAuthenticate(encKey, cs.session.CardChallenge(), cs.session.HostChallenge())
if err != nil {
return err
}
resp, err := cs.c.Send(cmd)
return cs.checkOK(resp, err)
}
func (cs *CommandSet) checkOK(resp *apdu.Response, err error, allowedResponses ...uint16) error {
if len(allowedResponses) == 0 {
allowedResponses = []uint16{apdu.SwOK}
}
for _, code := range allowedResponses {
if code == resp.Sw {
return nil
}
}
return fmt.Errorf("unexpected response: %x", resp.Sw)
}
func generateHostChallenge() ([]byte, error) {
c := make([]byte, 8)
_, err := rand.Read(c)
return c, err
}