2018-10-24 11:42:00 +00:00
|
|
|
package actions
|
|
|
|
|
|
|
|
import (
|
2018-10-24 16:16:14 +00:00
|
|
|
"crypto/rand"
|
|
|
|
"crypto/sha256"
|
2018-10-27 16:52:39 +00:00
|
|
|
"crypto/sha512"
|
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
|
|
|
|
2018-11-06 09:25:54 +00:00
|
|
|
"github.com/status-im/hardware-wallet-go/apdu"
|
|
|
|
"github.com/status-im/hardware-wallet-go/globalplatform"
|
|
|
|
"github.com/status-im/hardware-wallet-go/lightwallet"
|
|
|
|
"github.com/status-im/hardware-wallet-go/lightwallet/crypto"
|
2018-10-24 11:42:00 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
ErrAlreadyInitialized = errors.New("card already initialized")
|
|
|
|
ErrNotInitialized = errors.New("card not initialized")
|
|
|
|
ErrUnknownApplicationInfoTemplate = errors.New("unknown application info template")
|
|
|
|
)
|
|
|
|
|
2018-10-24 16:16:14 +00:00
|
|
|
func SelectNotInitialized(c globalplatform.Channel, aid []byte) ([]byte, error) {
|
|
|
|
sel := globalplatform.NewCommandSelect(aid)
|
|
|
|
resp, err := c.Send(sel)
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
if resp.Data[0] != lightwallet.TagSelectResponsePreInitialized {
|
|
|
|
return nil, ErrAlreadyInitialized
|
|
|
|
}
|
|
|
|
|
|
|
|
return resp.Data[2:], nil
|
2018-10-24 11:42:00 +00:00
|
|
|
}
|
|
|
|
|
2018-10-24 16:16:14 +00:00
|
|
|
func SelectInitialized(c globalplatform.Channel, aid []byte) (*lightwallet.ApplicationInfo, error) {
|
2018-10-24 11:42:00 +00:00
|
|
|
sel := globalplatform.NewCommandSelect(aid)
|
|
|
|
resp, err := c.Send(sel)
|
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
|
|
|
|
}
|
|
|
|
|
2018-10-24 11:42:00 +00:00
|
|
|
if resp.Data[0] == lightwallet.TagSelectResponsePreInitialized {
|
|
|
|
return nil, ErrNotInitialized
|
|
|
|
}
|
|
|
|
|
|
|
|
return parseApplicationInfo(resp)
|
|
|
|
}
|
|
|
|
|
2018-10-24 16:16:14 +00:00
|
|
|
func Init(c globalplatform.Channel, cardPubKey []byte, secrets *lightwallet.Secrets, aid []byte) error {
|
|
|
|
secureChannel, err := lightwallet.NewSecureChannel(c, cardPubKey)
|
2018-10-24 11:42:00 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-10-24 16:16:14 +00:00
|
|
|
data, err := secureChannel.OneShotEncrypt(secrets)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-10-24 11:42:00 +00:00
|
|
|
}
|
|
|
|
|
2018-10-24 16:16:14 +00:00
|
|
|
init := lightwallet.NewCommandInit(data)
|
|
|
|
resp, err := c.Send(init)
|
2018-10-24 11:42:00 +00:00
|
|
|
|
2018-10-27 16:12:38 +00:00
|
|
|
return checkOKResponse(err, resp)
|
2018-10-24 16:16:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func Pair(c globalplatform.Channel, pairingPass string, pin string) (*lightwallet.PairingInfo, error) {
|
|
|
|
challenge := make([]byte, 32)
|
|
|
|
if _, err := rand.Read(challenge); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd := lightwallet.NewCommandPairFirstStep(challenge)
|
|
|
|
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:]
|
|
|
|
|
2018-10-27 15:13:32 +00:00
|
|
|
secretHash, err := crypto.VerifyCryptogram(challenge, pairingPass, cardCryptogram)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2018-10-24 16:16:14 +00:00
|
|
|
}
|
|
|
|
|
2018-10-27 15:13:32 +00:00
|
|
|
h := sha256.New()
|
2018-10-24 16:16:14 +00:00
|
|
|
h.Write(secretHash[:])
|
|
|
|
h.Write(cardChallenge)
|
|
|
|
cmd = lightwallet.NewCommandPairFinalStep(h.Sum(nil))
|
|
|
|
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]
|
|
|
|
|
|
|
|
return &lightwallet.PairingInfo{
|
|
|
|
PairingKey: pairingKey,
|
|
|
|
PairingIndex: int(pairingIndex),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2018-10-27 16:52:39 +00:00
|
|
|
func OpenSecureChannel(c globalplatform.Channel, appInfo *lightwallet.ApplicationInfo, pairingIndex uint8, pairingKey []byte) (*lightwallet.SecureChannel, error) {
|
2018-10-24 16:16:14 +00:00
|
|
|
sc, err := lightwallet.NewSecureChannel(c, appInfo.PublicKey)
|
|
|
|
cmd := lightwallet.NewCommandOpenSecureChannel(pairingIndex, sc.RawPublicKey())
|
|
|
|
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-10-27 16:52:39 +00:00
|
|
|
salt := resp.Data[:32]
|
|
|
|
iv := resp.Data[32:]
|
|
|
|
|
|
|
|
h := sha512.New()
|
|
|
|
h.Write(sc.Secret())
|
|
|
|
h.Write(pairingKey)
|
|
|
|
h.Write(salt)
|
|
|
|
data := h.Sum(nil)
|
|
|
|
|
|
|
|
encKey := data[:32]
|
|
|
|
macKey := data[32:]
|
|
|
|
|
|
|
|
sc.Init(iv, encKey, macKey)
|
|
|
|
|
|
|
|
return sc, nil
|
2018-10-24 11:42:00 +00:00
|
|
|
}
|
|
|
|
|
2018-10-24 16:16:14 +00:00
|
|
|
func parseApplicationInfo(resp *apdu.Response) (*lightwallet.ApplicationInfo, error) {
|
2018-10-24 11:42:00 +00:00
|
|
|
if resp.Data[0] != lightwallet.TagApplicationInfoTemplate {
|
|
|
|
return nil, ErrUnknownApplicationInfoTemplate
|
|
|
|
}
|
|
|
|
|
|
|
|
instanceUID, err := apdu.FindTag(resp.Data, lightwallet.TagApplicationInfoTemplate, uint8(0x8F))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
pubKey, err := apdu.FindTag(resp.Data, lightwallet.TagApplicationInfoTemplate, uint8(0x80))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
appVersion, err := apdu.FindTag(resp.Data, lightwallet.TagApplicationInfoTemplate, uint8(0x02))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
availableSlots, err := apdu.FindTagN(resp.Data, 1, lightwallet.TagApplicationInfoTemplate, uint8(0x02))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-10-24 11:44:58 +00:00
|
|
|
keyUID, err := apdu.FindTagN(resp.Data, 0, lightwallet.TagApplicationInfoTemplate, uint8(0x8E))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-10-24 16:16:14 +00:00
|
|
|
return &lightwallet.ApplicationInfo{
|
2018-10-24 11:42:00 +00:00
|
|
|
InstanceUID: instanceUID,
|
|
|
|
PublicKey: pubKey,
|
|
|
|
Version: appVersion,
|
|
|
|
AvailableSlots: availableSlots,
|
2018-10-24 11:44:58 +00:00
|
|
|
KeyUID: keyUID,
|
2018-10-24 11:42:00 +00:00
|
|
|
}, nil
|
|
|
|
}
|
2018-10-24 16:16:14 +00:00
|
|
|
|
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)
|
|
|
|
}
|