mirror of
https://github.com/status-im/keycard-go.git
synced 2025-02-20 23:58:13 +00:00
add pair command
This commit is contained in:
parent
c8997c1063
commit
f51eb1d5bc
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user