add pair command

This commit is contained in:
Andrea Franz 2019-03-13 13:49:26 +01:00
parent c8997c1063
commit f51eb1d5bc
No known key found for this signature in database
GPG Key ID: 4F0D2F2D9DE7F29D
6 changed files with 148 additions and 106 deletions

View File

@ -18,7 +18,7 @@ var (
ErrApplicationStatusTemplateNotFound = errors.New("application status template not found")
)
func Pair(c types.Channel, pairingPass string, pin string) (*types.PairingInfo, error) {
func Pair(c types.Channel, pairingPass string) (*types.PairingInfo, error) {
challenge := make([]byte, 32)
if _, err := rand.Read(challenge); err != nil {
return nil, err
@ -61,7 +61,7 @@ func Pair(c types.Channel, pairingPass string, pin string) (*types.PairingInfo,
}
func OpenSecureChannel(c types.Channel, appInfo *types.ApplicationInfo, pairingIndex uint8, pairingKey []byte) (*SecureChannel, error) {
sc, err := NewSecureChannel(c, appInfo.PublicKey)
sc := NewSecureChannel(c)
cmd := NewCommandOpenSecureChannel(pairingIndex, sc.RawPublicKey())
resp, err := c.Send(cmd)
if err = checkOKResponse(err, resp); err != nil {

View File

@ -4,7 +4,6 @@ import (
"crypto/rand"
"errors"
"fmt"
"os"
keycard "github.com/status-im/keycard-go"
"github.com/status-im/keycard-go/apdu"
@ -59,6 +58,7 @@ func (i *Initializer) Init() (*keycard.Secrets, error) {
return nil, errCardAlreadyInitialized
}
logger.Info("initializing")
err = cmdSet.Init(secrets)
if err != nil {
return nil, err
@ -69,16 +69,41 @@ func (i *Initializer) Init() (*keycard.Secrets, error) {
// Info returns a types.ApplicationInfo struct with info about the card.
func (i *Initializer) Info() (types.ApplicationInfo, error) {
logger.Info("info started")
cmdSet := keycard.NewCommandSet(i.c)
logger.Info("select keycard applet")
err := cmdSet.Select()
if e, ok := err.(*apdu.ErrBadResponse); ok && e.Sw == globalplatform.SwFileNotFound {
err = nil
} else {
logger.Error("select failed", "error", err)
}
return cmdSet.ApplicationInfo, err
}
func (i *Initializer) Pair(pairingPass string) (*types.PairingInfo, error) {
logger.Info("pairing started")
cmdSet := keycard.NewCommandSet(i.c)
logger.Info("select keycard applet")
err := cmdSet.Select()
if err != nil {
logger.Error("select failed", "error", err)
return nil, err
}
if !cmdSet.ApplicationInfo.Initialized {
logger.Error("pairing failed", "error", ErrNotInitialized)
return nil, ErrNotInitialized
}
logger.Info("pairing")
err = cmdSet.Pair(pairingPass)
return cmdSet.PairingInfo, err
}
func (i *Initializer) initGPSecureChannel(sdaid []byte) error {
// select card manager
err := i.selectAID(sdaid)
@ -136,57 +161,6 @@ func (i *Initializer) externalAuthenticate(session *globalplatform.Session) erro
return err
}
func (i *Initializer) deleteAID(aids ...[]byte) error {
for _, aid := range aids {
del := globalplatform.NewCommandDelete(aid)
_, err := i.send("delete", del, globalplatform.SwOK, globalplatform.SwReferencedDataNotFound)
if err != nil {
return err
}
}
return nil
}
func (i *Initializer) installApplets(capFile *os.File) error {
// install for load
preLoad := globalplatform.NewCommandInstallForLoad(identifiers.PackageAID, identifiers.CardManagerAID)
_, err := i.send("install for load", preLoad)
if err != nil {
return err
}
// load
load, err := globalplatform.NewLoadCommandStream(capFile)
if err != nil {
return err
}
for load.Next() {
cmd := load.GetCommand()
_, err = i.send(fmt.Sprintf("load %d of 40", load.Index()+1), cmd)
if err != nil {
return err
}
}
installNdef := globalplatform.NewCommandInstallForInstall(identifiers.PackageAID, identifiers.NdefAID, identifiers.NdefInstanceAID, []byte{})
_, err = i.send("install for install (ndef)", installNdef)
if err != nil {
return err
}
instanceAID, err := identifiers.KeycardInstanceAID(1)
if err != nil {
return err
}
installWallet := globalplatform.NewCommandInstallForInstall(identifiers.PackageAID, identifiers.KeycardAID, instanceAID, []byte{})
_, err = i.send("install for install (wallet)", installWallet)
return err
}
func (i *Initializer) send(description string, cmd *apdu.Command, allowedResponses ...uint16) (*apdu.Response, error) {
logger.Debug("sending apdu command", "name", description)
resp, err := i.c.Send(cmd)

View File

@ -48,8 +48,8 @@ func init() {
"info": commandInfo,
"delete": commandDelete,
"init": commandInit,
// "pair": commandPair,
// "status": commandStatus,
"pair": commandPair,
// "status": commandStatus,
}
if len(os.Args) < 2 {
@ -244,20 +244,19 @@ func commandInit(card *scard.Card) error {
return nil
}
// func commandPair(card *scard.Card) error {
// i := NewInitializer(card)
// pairingPass := ask("Pairing password")
// pin := ask("PIN")
// info, err := i.Pair(pairingPass, pin)
// if err != nil {
// return err
// }
func commandPair(card *scard.Card) error {
i := NewInitializer(card)
pairingPass := ask("Pairing password")
info, err := i.Pair(pairingPass)
if err != nil {
return err
}
// fmt.Printf("Pairing key 0x%x\n", info.Key)
// fmt.Printf("Pairing Index %d\n", info.Index)
fmt.Printf("Pairing key 0x%x\n", info.Key)
fmt.Printf("Pairing Index %d\n", info.Index)
// return nil
// }
return nil
}
// func commandStatus(card *scard.Card) error {
// i := NewInitializer(card)

View File

@ -1,7 +1,11 @@
package keycard
import (
"crypto/rand"
"crypto/sha256"
"github.com/status-im/keycard-go/apdu"
"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"
@ -9,12 +13,15 @@ import (
type CommandSet struct {
c types.Channel
sc *SecureChannel
ApplicationInfo types.ApplicationInfo
PairingInfo *types.PairingInfo
}
func NewCommandSet(c types.Channel) *CommandSet {
return &CommandSet{
c: c,
c: c,
sc: NewSecureChannel(c),
}
}
@ -34,29 +41,31 @@ func (cs *CommandSet) Select() error {
cmd.SetLe(0)
resp, err := cs.c.Send(cmd)
err = cs.checkOK(resp, err)
if err == nil {
appInfo, err := types.ParseApplicationInfo(resp.Data)
if err != nil {
return err
}
cs.ApplicationInfo = appInfo
return nil
if err = cs.checkOK(resp, err); err != nil {
return err
}
return err
}
func (cs *CommandSet) Init(secrets *Secrets) error {
secureChannel, err := NewSecureChannel(cs.c, cs.ApplicationInfo.PublicKey)
appInfo, err := types.ParseApplicationInfo(resp.Data)
if err != nil {
return err
}
data, err := secureChannel.OneShotEncrypt(secrets)
cs.ApplicationInfo = appInfo
if cs.ApplicationInfo.HasSecureChannelCapability() {
err = cs.sc.GenerateSecret(cs.ApplicationInfo.PublicKey)
if err != nil {
return err
}
cs.sc.Reset()
}
return nil
}
func (cs *CommandSet) Init(secrets *Secrets) error {
data, err := cs.sc.OneShotEncrypt(secrets)
if err != nil {
return err
}
@ -67,6 +76,50 @@ func (cs *CommandSet) Init(secrets *Secrets) error {
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)
if err != nil {
return err
}
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
}
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
}
func (cs *CommandSet) checkOK(resp *apdu.Response, err error, allowedResponses ...uint16) error {
if len(allowedResponses) == 0 {
allowedResponses = []uint16{apdu.SwOK}

View File

@ -16,6 +16,7 @@ var ErrInvalidResponseMAC = errors.New("invalid response MAC")
type SecureChannel struct {
c types.Channel
open bool
secret []byte
publicKey *ecdsa.PublicKey
encKey []byte
@ -23,30 +24,38 @@ type SecureChannel struct {
iv []byte
}
func NewSecureChannel(c types.Channel, cardKeyData []byte) (*SecureChannel, error) {
func NewSecureChannel(c types.Channel) *SecureChannel {
return &SecureChannel{
c: c,
}
}
func (sc *SecureChannel) GenerateSecret(cardPubKeyData []byte) error {
key, err := ethcrypto.GenerateKey()
if err != nil {
return nil, err
return err
}
cardPubKey, err := ethcrypto.UnmarshalPubkey(cardKeyData)
cardPubKey, err := ethcrypto.UnmarshalPubkey(cardPubKeyData)
if err != nil {
return nil, err
return err
}
secret := crypto.GenerateECDHSharedSecret(key, cardPubKey)
sc.publicKey = &key.PublicKey
sc.secret = crypto.GenerateECDHSharedSecret(key, cardPubKey)
return &SecureChannel{
c: c,
secret: secret,
publicKey: &key.PublicKey,
}, nil
return nil
}
func (sc *SecureChannel) Reset() {
sc.open = false
}
func (sc *SecureChannel) Init(iv, encKey, macKey []byte) {
sc.iv = iv
sc.encKey = encKey
sc.macKey = macKey
sc.open = true
}
func (sc *SecureChannel) Secret() []byte {
@ -62,18 +71,20 @@ func (sc *SecureChannel) RawPublicKey() []byte {
}
func (sc *SecureChannel) Send(cmd *apdu.Command) (*apdu.Response, error) {
encData, err := crypto.EncryptData(cmd.Data, sc.encKey, sc.iv)
if err != nil {
return nil, err
}
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
}
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
newData := append(sc.iv, encData...)
cmd.Data = newData
}
resp, err := sc.c.Send(cmd)
if err != nil {

View File

@ -67,6 +67,11 @@ func ParseApplicationInfo(data []byte) (info ApplicationInfo, err error) {
if data[0] == TagSelectResponsePreInitialized {
info.PublicKey = data[2:]
info.Capabilities = CapabilityCredentialsManagement
if len(info.PublicKey) > 0 {
info.Capabilities = info.Capabilities | CapabilitySecureChannel
}
return info, nil
}