2019-03-06 09:45:52 +00:00
|
|
|
package globalplatform
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"errors"
|
|
|
|
"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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-06 12:45:32 +00:00
|
|
|
func (cs *CommandSet) Select() error {
|
2019-03-06 13:31:40 +00:00
|
|
|
return cs.SelectAID(nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cs *CommandSet) SelectAID(aid []byte) error {
|
2019-03-06 09:45:52 +00:00
|
|
|
cmd := apdu.NewCommand(
|
|
|
|
0x00,
|
|
|
|
InsSelect,
|
|
|
|
uint8(0x04),
|
|
|
|
uint8(0x00),
|
2019-03-06 13:31:40 +00:00
|
|
|
aid,
|
2019-03-06 09:45:52 +00:00
|
|
|
)
|
|
|
|
|
2019-03-06 11:48:12 +00:00
|
|
|
cmd.SetLe(0)
|
2019-03-06 09:45:52 +00:00
|
|
|
resp, err := cs.c.Send(cmd)
|
2019-03-06 11:48:12 +00:00
|
|
|
|
2019-03-06 12:45:32 +00:00
|
|
|
return cs.checkOK(resp, 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 {
|
2019-03-06 11:48:12 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-06 13:31:40 +00:00
|
|
|
return apdu.NewErrBadResponse(resp.Sw, "unexpected response")
|
2019-03-06 09:45:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func generateHostChallenge() ([]byte, error) {
|
|
|
|
c := make([]byte, 8)
|
|
|
|
_, err := rand.Read(c)
|
|
|
|
return c, err
|
|
|
|
}
|