keycard-go/actions.go

152 lines
3.7 KiB
Go
Raw Normal View History

2019-03-11 10:05:28 +00:00
package keycard
2018-10-24 11:42:00 +00:00
import (
2018-11-07 13:39:58 +00:00
"bytes"
2018-10-24 16:16:14 +00:00
"crypto/rand"
"crypto/sha256"
2018-10-24 11:42:00 +00:00
"errors"
2018-10-24 16:16:14 +00:00
"fmt"
2018-10-24 11:42:00 +00:00
2019-03-01 17:44:07 +00:00
"github.com/status-im/keycard-go/apdu"
2019-03-11 10:05:28 +00:00
"github.com/status-im/keycard-go/crypto"
"github.com/status-im/keycard-go/types"
2018-10-24 11:42:00 +00:00
)
var (
2018-11-07 13:39:58 +00:00
ErrAlreadyInitialized = errors.New("card already initialized")
ErrWrongApplicationInfoTemplate = errors.New("wrong application info template")
ErrApplicationStatusTemplateNotFound = errors.New("application status template not found")
2018-10-24 11:42:00 +00:00
)
2019-03-13 12:49:26 +00:00
func Pair(c types.Channel, pairingPass string) (*types.PairingInfo, error) {
2018-10-24 16:16:14 +00:00
challenge := make([]byte, 32)
if _, err := rand.Read(challenge); err != nil {
return nil, err
}
2019-03-11 10:05:28 +00:00
cmd := NewCommandPairFirstStep(challenge)
2018-10-24 16:16:14 +00:00
resp, err := c.Send(cmd)
2018-10-27 16:12:38 +00:00
if err = checkOKResponse(err, resp); err != nil {
2018-10-24 16:16:14 +00:00
return nil, err
}
cardCryptogram := resp.Data[:32]
cardChallenge := resp.Data[32:]
secretHash, err := crypto.VerifyCryptogram(challenge, pairingPass, cardCryptogram)
if err != nil {
return nil, err
2018-10-24 16:16:14 +00:00
}
h := sha256.New()
2018-10-24 16:16:14 +00:00
h.Write(secretHash[:])
h.Write(cardChallenge)
2019-03-11 10:05:28 +00:00
cmd = NewCommandPairFinalStep(h.Sum(nil))
2018-10-24 16:16:14 +00:00
resp, err = c.Send(cmd)
2018-10-27 16:12:38 +00:00
if err = checkOKResponse(err, resp); err != nil {
2018-10-24 16:16:14 +00:00
return nil, err
}
h.Reset()
h.Write(secretHash[:])
h.Write(resp.Data[1:])
pairingKey := h.Sum(nil)
pairingIndex := resp.Data[0]
2019-03-11 10:05:28 +00:00
return &types.PairingInfo{
2018-11-06 11:54:11 +00:00
Key: pairingKey,
Index: int(pairingIndex),
2018-10-24 16:16:14 +00:00
}, nil
}
2019-03-11 10:49:00 +00:00
func OpenSecureChannel(c types.Channel, appInfo *types.ApplicationInfo, pairingIndex uint8, pairingKey []byte) (*SecureChannel, error) {
2019-03-13 12:49:26 +00:00
sc := NewSecureChannel(c)
2019-03-11 10:05:28 +00:00
cmd := NewCommandOpenSecureChannel(pairingIndex, sc.RawPublicKey())
2018-10-24 16:16:14 +00:00
resp, err := c.Send(cmd)
2018-10-27 16:12:38 +00:00
if err = checkOKResponse(err, resp); err != nil {
2018-10-27 16:52:39 +00:00
return nil, err
2018-10-24 16:16:14 +00:00
}
2018-10-24 11:42:00 +00:00
2018-11-06 17:38:13 +00:00
encKey, macKey, iv := crypto.DeriveSessionKeys(sc.Secret(), pairingKey, resp.Data)
sc.Init(iv, encKey, macKey)
2018-10-27 16:52:39 +00:00
2018-11-06 17:38:13 +00:00
err = mutualAuthenticate(sc)
if err != nil {
return nil, err
}
2018-10-27 16:52:39 +00:00
2018-11-06 17:38:13 +00:00
return sc, nil
}
2018-10-27 16:52:39 +00:00
2019-03-11 10:05:28 +00:00
func mutualAuthenticate(sc *SecureChannel) error {
2018-11-06 17:38:13 +00:00
data := make([]byte, 32)
if _, err := rand.Read(data); err != nil {
return err
}
2018-10-27 16:52:39 +00:00
2019-03-11 10:05:28 +00:00
cmd := NewCommandMutuallyAuthenticate(data)
2018-11-06 17:38:13 +00:00
resp, err := sc.Send(cmd)
return checkOKResponse(err, resp)
}
2019-03-11 10:49:00 +00:00
func GetStatusApplication(c types.Channel) (*types.ApplicationStatus, error) {
2019-03-11 10:05:28 +00:00
cmd := NewCommandGetStatusApplication()
2018-11-07 13:39:58 +00:00
resp, err := c.Send(cmd)
if err = checkOKResponse(err, resp); err != nil {
return nil, err
}
return parseApplicationStatus(resp.Data)
2018-10-24 11:42:00 +00:00
}
2019-03-11 10:05:28 +00:00
func parseApplicationStatus(data []byte) (*types.ApplicationStatus, error) {
appStatus := &types.ApplicationStatus{}
2018-11-07 13:39:58 +00:00
2019-03-11 10:05:28 +00:00
tpl, err := apdu.FindTag(data, TagApplicationStatusTemplate)
2018-11-07 13:39:58 +00:00
if err != nil {
return nil, ErrApplicationStatusTemplateNotFound
}
if pinRetryCount, err := apdu.FindTag(tpl, uint8(0x02)); err == nil && len(pinRetryCount) == 1 {
appStatus.PinRetryCount = int(pinRetryCount[0])
}
if pukRetryCount, err := apdu.FindTagN(tpl, 1, uint8(0x02)); err == nil && len(pukRetryCount) == 1 {
appStatus.PUKRetryCount = int(pukRetryCount[0])
}
if keyInitialized, err := apdu.FindTag(tpl, uint8(0x01)); err == nil {
if bytes.Equal(keyInitialized, []byte{0xFF}) {
appStatus.KeyInitialized = true
}
}
if keyDerivationSupported, err := apdu.FindTagN(tpl, 1, uint8(0x01)); err == nil {
if bytes.Equal(keyDerivationSupported, []byte{0xFF}) {
appStatus.PubKeyDerivation = true
}
}
return appStatus, nil
}
2018-10-27 16:12:38 +00:00
func checkOKResponse(err error, resp *apdu.Response) error {
if err != nil {
return err
}
2018-10-24 16:16:14 +00:00
return checkResponse(resp, apdu.SwOK)
}
func checkResponse(resp *apdu.Response, allowedResponses ...uint16) error {
for _, code := range allowedResponses {
if code == resp.Sw {
return nil
}
}
return fmt.Errorf("unexpected response: %x", resp.Sw)
}