keycard-go/command_set.go

136 lines
2.6 KiB
Go
Raw Normal View History

package keycard
import (
2019-03-13 12:49:26 +00:00
"crypto/rand"
"crypto/sha256"
"github.com/status-im/keycard-go/apdu"
2019-03-13 12:49:26 +00:00
"github.com/status-im/keycard-go/crypto"
"github.com/status-im/keycard-go/globalplatform"
"github.com/status-im/keycard-go/identifiers"
"github.com/status-im/keycard-go/types"
)
type CommandSet struct {
c types.Channel
2019-03-13 12:49:26 +00:00
sc *SecureChannel
ApplicationInfo types.ApplicationInfo
2019-03-13 12:49:26 +00:00
PairingInfo *types.PairingInfo
}
func NewCommandSet(c types.Channel) *CommandSet {
return &CommandSet{
2019-03-13 12:49:26 +00:00
c: c,
sc: NewSecureChannel(c),
}
}
func (cs *CommandSet) Select() error {
instanceAID, err := identifiers.KeycardInstanceAID(identifiers.KeycardDefaultInstanceIndex)
if err != nil {
return err
}
cmd := apdu.NewCommand(
0x00,
globalplatform.InsSelect,
uint8(0x04),
uint8(0x00),
instanceAID,
)
cmd.SetLe(0)
resp, err := cs.c.Send(cmd)
2019-03-13 12:49:26 +00:00
if err = cs.checkOK(resp, err); err != nil {
return err
}
appInfo, err := types.ParseApplicationInfo(resp.Data)
if err != nil {
return err
}
cs.ApplicationInfo = appInfo
2019-03-13 12:49:26 +00:00
if cs.ApplicationInfo.HasSecureChannelCapability() {
err = cs.sc.GenerateSecret(cs.ApplicationInfo.PublicKey)
if err != nil {
return err
}
2019-03-13 12:49:26 +00:00
cs.sc.Reset()
}
2019-03-13 12:49:26 +00:00
return nil
}
2019-03-11 11:50:16 +00:00
func (cs *CommandSet) Init(secrets *Secrets) error {
2019-03-13 12:49:26 +00:00
data, err := cs.sc.OneShotEncrypt(secrets)
2019-03-11 11:50:16 +00:00
if err != nil {
return err
}
2019-03-13 12:49:26 +00:00
init := NewCommandInit(data)
resp, err := cs.c.Send(init)
return cs.checkOK(resp, err)
}
func (cs *CommandSet) Pair(pairingPass string) error {
challenge := make([]byte, 32)
if _, err := rand.Read(challenge); err != nil {
return err
}
cmd := NewCommandPairFirstStep(challenge)
resp, err := cs.c.Send(cmd)
if err = cs.checkOK(resp, err); err != nil {
return err
}
cardCryptogram := resp.Data[:32]
cardChallenge := resp.Data[32:]
secretHash, err := crypto.VerifyCryptogram(challenge, pairingPass, cardCryptogram)
2019-03-11 11:50:16 +00:00
if err != nil {
return err
}
2019-03-13 12:49:26 +00:00
h := sha256.New()
h.Write(secretHash[:])
h.Write(cardChallenge)
cmd = NewCommandPairFinalStep(h.Sum(nil))
resp, err = cs.c.Send(cmd)
if err = cs.checkOK(resp, err); err != nil {
return err
}
2019-03-11 11:50:16 +00:00
2019-03-13 12:49:26 +00:00
h.Reset()
h.Write(secretHash[:])
h.Write(resp.Data[1:])
pairingKey := h.Sum(nil)
pairingIndex := resp.Data[0]
cs.PairingInfo = &types.PairingInfo{
Key: pairingKey,
Index: int(pairingIndex),
}
return nil
2019-03-11 11:50:16 +00:00
}
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 apdu.NewErrBadResponse(resp.Sw, "unexpected response")
}