keycard-go/secure_channel.go

134 lines
3.0 KiB
Go
Raw Normal View History

2019-03-11 10:05:28 +00:00
package keycard
2018-10-22 17:33:53 +00:00
import (
2018-11-06 17:38:13 +00:00
"bytes"
2018-10-22 17:33:53 +00:00
"crypto/ecdsa"
2018-11-06 17:38:13 +00:00
"errors"
2018-10-22 17:33:53 +00:00
ethcrypto "github.com/ethereum/go-ethereum/crypto"
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"
2019-03-01 17:44:07 +00:00
"github.com/status-im/keycard-go/globalplatform"
2019-03-21 22:04:24 +00:00
"github.com/status-im/keycard-go/hexutils"
2019-03-11 10:49:00 +00:00
"github.com/status-im/keycard-go/types"
2018-10-22 17:33:53 +00:00
)
2018-11-06 17:38:13 +00:00
var ErrInvalidResponseMAC = errors.New("invalid response MAC")
2018-10-22 17:33:53 +00:00
type SecureChannel struct {
2019-03-11 10:49:00 +00:00
c types.Channel
2019-03-13 12:49:26 +00:00
open bool
2018-10-24 16:16:14 +00:00
secret []byte
publicKey *ecdsa.PublicKey
2018-10-27 16:52:39 +00:00
encKey []byte
macKey []byte
iv []byte
2018-10-22 17:33:53 +00:00
}
2019-03-13 12:49:26 +00:00
func NewSecureChannel(c types.Channel) *SecureChannel {
return &SecureChannel{
c: c,
}
}
func (sc *SecureChannel) GenerateSecret(cardPubKeyData []byte) error {
2018-10-22 17:33:53 +00:00
key, err := ethcrypto.GenerateKey()
if err != nil {
2019-03-13 12:49:26 +00:00
return err
2018-10-22 17:33:53 +00:00
}
2019-03-13 12:49:26 +00:00
cardPubKey, err := ethcrypto.UnmarshalPubkey(cardPubKeyData)
2018-10-22 17:33:53 +00:00
if err != nil {
2019-03-13 12:49:26 +00:00
return err
2018-10-22 17:33:53 +00:00
}
2019-03-13 12:49:26 +00:00
sc.publicKey = &key.PublicKey
sc.secret = crypto.GenerateECDHSharedSecret(key, cardPubKey)
2018-10-22 17:33:53 +00:00
2019-03-13 12:49:26 +00:00
return nil
}
func (sc *SecureChannel) Reset() {
sc.open = false
2018-10-22 17:33:53 +00:00
}
2018-10-27 16:52:39 +00:00
func (sc *SecureChannel) Init(iv, encKey, macKey []byte) {
sc.iv = iv
sc.encKey = encKey
sc.macKey = macKey
2019-03-13 12:49:26 +00:00
sc.open = true
2018-10-27 16:52:39 +00:00
}
func (sc *SecureChannel) Secret() []byte {
return sc.secret
}
2018-10-24 16:16:14 +00:00
func (sc *SecureChannel) PublicKey() *ecdsa.PublicKey {
return sc.publicKey
}
func (sc *SecureChannel) RawPublicKey() []byte {
return ethcrypto.FromECDSAPub(sc.publicKey)
}
2018-10-22 17:33:53 +00:00
func (sc *SecureChannel) Send(cmd *apdu.Command) (*apdu.Response, error) {
2019-03-13 12:49:26 +00:00
if sc.open {
encData, err := crypto.EncryptData(cmd.Data, sc.encKey, sc.iv)
if err != nil {
return nil, err
}
meta := []byte{cmd.Cla, cmd.Ins, cmd.P1, cmd.P2, byte(len(encData) + 16), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
if err = sc.updateIV(meta, encData); err != nil {
return nil, err
}
newData := append(sc.iv, encData...)
cmd.Data = newData
2018-11-06 17:38:13 +00:00
}
resp, err := sc.c.Send(cmd)
if err != nil {
return nil, err
}
if resp.Sw != globalplatform.SwOK {
return nil, apdu.NewErrBadResponse(resp.Sw, "unexpected sw in secure channel")
}
rmeta := []byte{byte(len(resp.Data)), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
rmac := resp.Data[:len(sc.iv)]
rdata := resp.Data[len(sc.iv):]
plainData, err := crypto.DecryptData(rdata, sc.encKey, sc.iv)
if err = sc.updateIV(rmeta, rdata); err != nil {
return nil, err
}
if !bytes.Equal(sc.iv, rmac) {
return nil, ErrInvalidResponseMAC
}
2019-03-21 21:59:43 +00:00
logger.Debug("apdu response decrypted", "hex", hexutils.BytesToHexWithSpaces(plainData))
2018-11-06 17:38:13 +00:00
return apdu.ParseResponse(plainData)
}
func (sc *SecureChannel) updateIV(meta, data []byte) error {
mac, err := crypto.CalculateMac(meta, data, sc.macKey)
if err != nil {
return err
}
sc.iv = mac
return nil
2018-10-22 17:33:53 +00:00
}
func (sc *SecureChannel) OneShotEncrypt(secrets *Secrets) ([]byte, error) {
2018-10-24 16:16:14 +00:00
pubKeyData := ethcrypto.FromECDSAPub(sc.publicKey)
2018-10-22 17:33:53 +00:00
data := append([]byte(secrets.Pin()), []byte(secrets.Puk())...)
data = append(data, secrets.PairingToken()...)
return crypto.OneShotEncrypt(pubKeyData, sc.secret, data)
}