add get status command

This commit is contained in:
Andrea Franz 2019-03-13 16:20:51 +01:00
parent 91bbfbffb4
commit a0138cd86d
No known key found for this signature in database
GPG Key ID: 4F0D2F2D9DE7F29D
6 changed files with 166 additions and 35 deletions

View File

@ -68,7 +68,7 @@ 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) {
func (i *Initializer) Info() (*types.ApplicationInfo, error) {
logger.Info("info started")
cmdSet := keycard.NewCommandSet(i.c)
@ -104,6 +104,41 @@ func (i *Initializer) Pair(pairingPass string) (*types.PairingInfo, error) {
return cmdSet.PairingInfo, err
}
func (i *Initializer) Status(key []byte, index int) (*types.ApplicationStatus, 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("open secure channel")
cmdSet.SetPairingInfo(key, index)
err = cmdSet.OpenSecureChannel()
if err != nil {
logger.Error("open secure channel failed", "error", err)
return nil, err
}
logger.Info("get status")
cmdSet.SetPairingInfo(key, index)
appStatus, err := cmdSet.GetStatus()
if err != nil {
logger.Error("get status failed", "error", err)
return nil, err
}
return appStatus, nil
}
func (i *Initializer) initGPSecureChannel(sdaid []byte) error {
// select card manager
err := i.selectAID(sdaid)

View File

@ -49,7 +49,7 @@ func init() {
"delete": commandDelete,
"init": commandInit,
"pair": commandPair,
// "status": commandStatus,
"status": commandStatus,
}
if len(os.Args) < 2 {
@ -168,14 +168,14 @@ func askHex(description string) []byte {
return data
}
func askUint8(description string) uint8 {
func askInt(description string) int {
s := ask(description)
i, err := strconv.ParseUint(s, 10, 8)
i, err := strconv.ParseInt(s, 10, 8)
if err != nil {
stdlog.Fatal(err)
}
return uint8(i)
return int(i)
}
func commandInstall(card *scard.Card) error {
@ -258,20 +258,20 @@ func commandPair(card *scard.Card) error {
return nil
}
// func commandStatus(card *scard.Card) error {
// i := NewInitializer(card)
// index := askUint8("Pairing index")
// key := askHex("Pairing key")
func commandStatus(card *scard.Card) error {
i := NewInitializer(card)
key := askHex("Pairing key")
index := askInt("Pairing index")
// appStatus, err := i.Status(index, key)
// if err != nil {
// return err
// }
appStatus, err := i.Status(key, index)
if err != nil {
return err
}
// fmt.Printf("Pin retry count: %d\n", appStatus.PinRetryCount)
// fmt.Printf("PUK retry count: %d\n", appStatus.PUKRetryCount)
// fmt.Printf("Key initialized: %v\n", appStatus.KeyInitialized)
// fmt.Printf("Public key derivation: %v\n", appStatus.PubKeyDerivation)
fmt.Printf("Pin retry count: %d\n", appStatus.PinRetryCount)
fmt.Printf("PUK retry count: %d\n", appStatus.PUKRetryCount)
fmt.Printf("Key initialized: %v\n", appStatus.KeyInitialized)
fmt.Printf("Public key derivation: %v\n", appStatus.PubKeyDerivation)
// return nil
// }
return nil
}

View File

@ -3,6 +3,7 @@ package keycard
import (
"crypto/rand"
"crypto/sha256"
"errors"
"github.com/status-im/keycard-go/apdu"
"github.com/status-im/keycard-go/crypto"
@ -14,7 +15,7 @@ import (
type CommandSet struct {
c types.Channel
sc *SecureChannel
ApplicationInfo types.ApplicationInfo
ApplicationInfo *types.ApplicationInfo
PairingInfo *types.PairingInfo
}
@ -25,6 +26,13 @@ func NewCommandSet(c types.Channel) *CommandSet {
}
}
func (cs *CommandSet) SetPairingInfo(key []byte, index int) {
cs.PairingInfo = &types.PairingInfo{
Key: key,
Index: index,
}
}
func (cs *CommandSet) Select() error {
instanceAID, err := identifiers.KeycardInstanceAID(identifiers.KeycardDefaultInstanceIndex)
if err != nil {
@ -120,6 +128,50 @@ func (cs *CommandSet) Pair(pairingPass string) error {
return nil
}
func (cs *CommandSet) OpenSecureChannel() error {
if cs.ApplicationInfo == nil {
return errors.New("cannot open secure channel without setting PairingInfo")
}
cmd := NewCommandOpenSecureChannel(uint8(cs.PairingInfo.Index), cs.sc.RawPublicKey())
resp, err := cs.c.Send(cmd)
if err = cs.checkOK(resp, err); err != nil {
return err
}
encKey, macKey, iv := crypto.DeriveSessionKeys(cs.sc.Secret(), cs.PairingInfo.Key, resp.Data)
cs.sc.Init(iv, encKey, macKey)
err = cs.mutualAuthenticate()
if err != nil {
return err
}
return nil
}
func (cs *CommandSet) GetStatus() (*types.ApplicationStatus, error) {
cmd := NewCommandGetStatusApplication()
resp, err := cs.sc.Send(cmd)
if err = cs.checkOK(resp, err); err != nil {
return nil, err
}
return types.ParseApplicationStatus(resp.Data)
}
func (cs *CommandSet) mutualAuthenticate() error {
data := make([]byte, 32)
if _, err := rand.Read(data); err != nil {
return err
}
cmd := NewCommandMutuallyAuthenticate(data)
resp, err := cs.sc.Send(cmd)
return cs.checkOK(resp, err)
}
func (cs *CommandSet) checkOK(resp *apdu.Response, err error, allowedResponses ...uint16) error {
if len(allowedResponses) == 0 {
allowedResponses = []uint16{apdu.SwOK}

View File

@ -62,8 +62,11 @@ func (a *ApplicationInfo) HasNDEFCapability() bool {
return a.HasCapability(CapabilityNDEF)
}
func ParseApplicationInfo(data []byte) (info ApplicationInfo, err error) {
info.Installed = true
func ParseApplicationInfo(data []byte) (*ApplicationInfo, error) {
info := &ApplicationInfo{
Installed: true,
}
if data[0] == TagSelectResponsePreInitialized {
info.PublicKey = data[2:]
info.Capabilities = CapabilityCredentialsManagement
@ -78,32 +81,32 @@ func ParseApplicationInfo(data []byte) (info ApplicationInfo, err error) {
info.Initialized = true
if data[0] != TagApplicationInfoTemplate {
return info, ErrWrongApplicationInfoTemplate
return nil, ErrWrongApplicationInfoTemplate
}
instanceUID, err := apdu.FindTag(data, TagApplicationInfoTemplate, uint8(0x8F))
if err != nil {
return info, err
return nil, err
}
pubKey, err := apdu.FindTag(data, TagApplicationInfoTemplate, uint8(0x80))
if err != nil {
return info, err
return nil, err
}
appVersion, err := apdu.FindTag(data, TagApplicationInfoTemplate, uint8(0x02))
if err != nil {
return info, err
return nil, err
}
availableSlots, err := apdu.FindTagN(data, 1, TagApplicationInfoTemplate, uint8(0x02))
if err != nil {
return info, err
return nil, err
}
keyUID, err := apdu.FindTagN(data, 0, TagApplicationInfoTemplate, uint8(0x8E))
if err != nil {
return info, err
return nil, err
}
capabilities := CapabilityAll

View File

@ -0,0 +1,48 @@
package types
import (
"bytes"
"errors"
"github.com/status-im/keycard-go/apdu"
)
var ErrApplicationStatusTemplateNotFound = errors.New("application status template not found")
type ApplicationStatus struct {
PinRetryCount int
PUKRetryCount int
KeyInitialized bool
PubKeyDerivation bool
}
func ParseApplicationStatus(data []byte) (*ApplicationStatus, error) {
appStatus := &ApplicationStatus{}
tpl, err := apdu.FindTag(data, TagApplicationStatusTemplate)
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
}

View File

@ -7,13 +7,6 @@ type Channel interface {
Send(*apdu.Command) (*apdu.Response, error)
}
type ApplicationStatus struct {
PinRetryCount int
PUKRetryCount int
KeyInitialized bool
PubKeyDerivation bool
}
type PairingInfo struct {
Key []byte
Index int