2019-03-11 11:24:09 +00:00
|
|
|
package keycard
|
|
|
|
|
|
|
|
import (
|
2019-03-13 12:49:26 +00:00
|
|
|
"crypto/rand"
|
|
|
|
"crypto/sha256"
|
|
|
|
|
2019-03-11 11:24:09 +00:00
|
|
|
"github.com/status-im/keycard-go/apdu"
|
2019-03-13 12:49:26 +00:00
|
|
|
"github.com/status-im/keycard-go/crypto"
|
2019-03-11 11:24:09 +00:00
|
|
|
"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
|
2019-03-11 11:24:09 +00:00
|
|
|
ApplicationInfo types.ApplicationInfo
|
2019-03-13 12:49:26 +00:00
|
|
|
PairingInfo *types.PairingInfo
|
2019-03-11 11:24:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewCommandSet(c types.Channel) *CommandSet {
|
|
|
|
return &CommandSet{
|
2019-03-13 12:49:26 +00:00
|
|
|
c: c,
|
|
|
|
sc: NewSecureChannel(c),
|
2019-03-11 11:24:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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-11 11:24:09 +00:00
|
|
|
|
2019-03-13 12:49:26 +00:00
|
|
|
if cs.ApplicationInfo.HasSecureChannelCapability() {
|
|
|
|
err = cs.sc.GenerateSecret(cs.ApplicationInfo.PublicKey)
|
2019-03-11 11:24:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-03-13 12:49:26 +00:00
|
|
|
cs.sc.Reset()
|
2019-03-11 11:24:09 +00:00
|
|
|
}
|
|
|
|
|
2019-03-13 12:49:26 +00:00
|
|
|
return nil
|
2019-03-11 11:24:09 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-03-11 11:24:09 +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")
|
|
|
|
}
|