update keycard-go

This commit is contained in:
Andrea Franz 2019-03-28 11:59:21 +01:00
parent c6ba1d013f
commit 36ec6a81ea
No known key found for this signature in database
GPG Key ID: 4F0D2F2D9DE7F29D
7 changed files with 192 additions and 85 deletions

6
Gopkg.lock generated
View File

@ -42,7 +42,7 @@
[[projects]]
branch = "develop"
digest = "1:9732855298c5522b25234e718d3d42d9d21b2c2ed6ffd49c18a9c102c50b290c"
digest = "1:354e8b1b5bb2aeab3a10a51eeed5937526c2c90a0b8147e8b050ee04fbf9b84a"
name = "github.com/status-im/keycard-go"
packages = [
".",
@ -53,10 +53,11 @@
"globalplatform/crypto",
"hexutils",
"identifiers",
"io",
"types",
]
pruneopts = "NUT"
revision = "72d8e3de8b61c902cebf29a7bec348ea9b425173"
revision = "3cdaf543d74e0d07a419773326bb1ddd1a5d6cd0"
[[projects]]
branch = "master"
@ -106,6 +107,7 @@
"github.com/status-im/keycard-go/globalplatform",
"github.com/status-im/keycard-go/hexutils",
"github.com/status-im/keycard-go/identifiers",
"github.com/status-im/keycard-go/io",
"github.com/status-im/keycard-go/types",
]
solver-name = "gps-cdcl"

View File

@ -12,6 +12,8 @@ import (
"github.com/status-im/keycard-go/types"
)
var ErrNoAvailablePairingSlots = errors.New("no available pairing slots")
type CommandSet struct {
c types.Channel
sc *SecureChannel
@ -93,6 +95,10 @@ func (cs *CommandSet) Pair(pairingPass string) error {
cmd := NewCommandPairFirstStep(challenge)
resp, err := cs.c.Send(cmd)
if resp.Sw == SwNoAvailablePairingSlots {
return ErrNoAvailablePairingSlots
}
if err = cs.checkOK(resp, err); err != nil {
return err
}
@ -129,6 +135,12 @@ func (cs *CommandSet) Pair(pairingPass string) error {
return nil
}
func (cs *CommandSet) Unpair(index uint8) error {
cmd := NewCommandUnpair(index)
resp, err := cs.sc.Send(cmd)
return cs.checkOK(resp, err)
}
func (cs *CommandSet) OpenSecureChannel() error {
if cs.ApplicationInfo == nil {
return errors.New("cannot open secure channel without setting PairingInfo")
@ -176,6 +188,28 @@ func (cs *CommandSet) VerifyPIN(pin string) error {
return cs.checkOK(resp, err)
}
func (cs *CommandSet) ChangePIN(pin string) error {
cmd := NewCommandChangePIN(pin)
resp, err := cs.sc.Send(cmd)
return cs.checkOK(resp, err)
}
func (cs *CommandSet) ChangePUK(puk string) error {
cmd := NewCommandChangePUK(puk)
resp, err := cs.sc.Send(cmd)
return cs.checkOK(resp, err)
}
func (cs *CommandSet) ChangePairingSecret(password string) error {
secret := generatePairingToken(password)
cmd := NewCommandChangePairingSecret(secret)
resp, err := cs.sc.Send(cmd)
return cs.checkOK(resp, err)
}
func (cs *CommandSet) GenerateKey() ([]byte, error) {
cmd := NewCommandGenerateKey()
resp, err := cs.sc.Send(cmd)
@ -186,6 +220,12 @@ func (cs *CommandSet) GenerateKey() ([]byte, error) {
return resp.Data, nil
}
func (cs *CommandSet) RemoveKey() error {
cmd := NewCommandRemoveKey()
resp, err := cs.sc.Send(cmd)
return cs.checkOK(resp, err)
}
func (cs *CommandSet) DeriveKey(path string) error {
cmd, err := NewCommandDeriveKey(path)
if err != nil {

View File

@ -11,32 +11,40 @@ import (
)
const (
InsInit = uint8(0xFE)
InsOpenSecureChannel = uint8(0x10)
InsMutuallyAuthenticate = uint8(0x11)
InsPair = uint8(0x12)
InsGetStatus = uint8(0xF2)
InsGenerateKey = uint8(0xD4)
InsVerifyPIN = uint8(0x20)
InsDeriveKey = uint8(0xD1)
InsSign = uint8(0xC0)
InsSetPinlessPath = uint8(0xC1)
InsInit = 0xFE
InsOpenSecureChannel = 0x10
InsMutuallyAuthenticate = 0x11
InsPair = 0x12
InsUnpair = 0x13
InsGetStatus = 0xF2
InsGenerateKey = 0xD4
InsRemoveKey = 0xD3
InsVerifyPIN = 0x20
InsChangePIN = 0x21
InsDeriveKey = 0xD1
InsSign = 0xC0
InsSetPinlessPath = 0xC1
P1PairingFirstStep = uint8(0x00)
P1PairingFinalStep = uint8(0x01)
P1GetStatusApplication = uint8(0x00)
P1GetStatusKeyPath = uint8(0x01)
P1DeriveKeyFromMaster = uint8(0x00)
P1DeriveKeyFromParent = uint8(0x01)
P1DeriveKeyFromCurrent = uint8(0x10)
P1PairingFirstStep = 0x00
P1PairingFinalStep = 0x01
P1GetStatusApplication = 0x00
P1GetStatusKeyPath = 0x01
P1DeriveKeyFromMaster = 0x00
P1DeriveKeyFromParent = 0x01
P1DeriveKeyFromCurrent = 0x10
P1ChangePinPIN = 0x00
P1ChangePinPUK = 0x01
P1ChangePinPairingSecret = 0x02
SwNoAvailablePairingSlots = 0x6A84
)
func NewCommandInit(data []byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsInit,
uint8(0x00),
uint8(0x00),
0,
0,
data,
)
}
@ -46,7 +54,7 @@ func NewCommandPairFirstStep(challenge []byte) *apdu.Command {
globalplatform.ClaGp,
InsPair,
P1PairingFirstStep,
uint8(0x00),
0,
challenge,
)
}
@ -56,17 +64,27 @@ func NewCommandPairFinalStep(cryptogramHash []byte) *apdu.Command {
globalplatform.ClaGp,
InsPair,
P1PairingFinalStep,
uint8(0x00),
0,
cryptogramHash,
)
}
func NewCommandUnpair(index uint8) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsUnpair,
index,
0,
[]byte{},
)
}
func NewCommandOpenSecureChannel(pairingIndex uint8, pubKey []byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsOpenSecureChannel,
pairingIndex,
uint8(0x00),
0,
pubKey,
)
}
@ -75,8 +93,8 @@ func NewCommandMutuallyAuthenticate(data []byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsMutuallyAuthenticate,
uint8(0x00),
uint8(0x00),
0,
0,
data,
)
}
@ -86,7 +104,7 @@ func NewCommandGetStatus(p1 uint8) *apdu.Command {
globalplatform.ClaGp,
InsGetStatus,
p1,
uint8(0x00),
0,
[]byte{},
)
}
@ -95,8 +113,18 @@ func NewCommandGenerateKey() *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsGenerateKey,
uint8(0),
uint8(0),
0,
0,
[]byte{},
)
}
func NewCommandRemoveKey() *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsRemoveKey,
0,
0,
[]byte{},
)
}
@ -105,12 +133,42 @@ func NewCommandVerifyPIN(pin string) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsVerifyPIN,
uint8(0),
uint8(0),
0,
0,
[]byte(pin),
)
}
func NewCommandChangePIN(pin string) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsChangePIN,
P1ChangePinPIN,
0,
[]byte(pin),
)
}
func NewCommandChangePUK(puk string) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsChangePIN,
P1ChangePinPUK,
0,
[]byte(puk),
)
}
func NewCommandChangePairingSecret(secret []byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsChangePIN,
P1ChangePinPairingSecret,
0,
secret,
)
}
func NewCommandDeriveKey(pathStr string) (*apdu.Command, error) {
startingPoint, path, err := derivationpath.Decode(pathStr)
if err != nil {
@ -140,7 +198,7 @@ func NewCommandDeriveKey(pathStr string) (*apdu.Command, error) {
globalplatform.ClaGp,
InsDeriveKey,
p1,
uint8(0),
0,
data.Bytes(),
), nil
}
@ -165,8 +223,8 @@ func NewCommandSetPinlessPath(pathStr string) (*apdu.Command, error) {
return apdu.NewCommand(
globalplatform.ClaGp,
InsSetPinlessPath,
uint8(0),
uint8(0),
0,
0,
data.Bytes(),
), nil
}
@ -179,8 +237,8 @@ func NewCommandSign(data []byte) (*apdu.Command, error) {
return apdu.NewCommand(
globalplatform.ClaGp,
InsSign,
uint8(0),
uint8(0),
0,
0,
data,
), nil
}

View File

@ -7,43 +7,43 @@ import (
// Constants used in apdu commands and responses as defined by iso7816 and globalplatform.
const (
ClaISO7816 = uint8(0x00)
ClaGp = uint8(0x80)
ClaMac = uint8(0x84)
ClaISO7816 = 0x00
ClaGp = 0x80
ClaMac = 0x84
InsSelect = uint8(0xA4)
InsInitializeUpdate = uint8(0x50)
InsExternalAuthenticate = uint8(0x82)
InsGetResponse = uint8(0xC0)
InsDelete = uint8(0xE4)
InsLoad = uint8(0xE8)
InsInstall = uint8(0xE6)
InsGetStatus = uint8(0xF2)
InsSelect = 0xA4
InsInitializeUpdate = 0x50
InsExternalAuthenticate = 0x82
InsGetResponse = 0xC0
InsDelete = 0xE4
InsLoad = 0xE8
InsInstall = 0xE6
InsGetStatus = 0xF2
P1ExternalAuthenticateCMAC = uint8(0x01)
P1InstallForLoad = uint8(0x02)
P1InstallForInstall = uint8(0x04)
P1InstallForMakeSelectable = uint8(0x08)
P1LoadMoreBlocks = uint8(0x00)
P1LoadLastBlock = uint8(0x80)
P1GetStatusIssuerSecurityDomain = uint8(0x80)
P1GetStatusApplications = uint8(0x40)
P1GetStatusExecLoadFiles = uint8(0x20)
P1GetStatusExecLoadFilesAndModules = uint8(0x10)
P1ExternalAuthenticateCMAC = 0x01
P1InstallForLoad = 0x02
P1InstallForInstall = 0x04
P1InstallForMakeSelectable = 0x08
P1LoadMoreBlocks = 0x00
P1LoadLastBlock = 0x80
P1GetStatusIssuerSecurityDomain = 0x80
P1GetStatusApplications = 0x40
P1GetStatusExecLoadFiles = 0x20
P1GetStatusExecLoadFilesAndModules = 0x10
P2GetStatusTLVData = uint8(0x02)
P2GetStatusTLVData = 0x02
Sw1ResponseDataIncomplete = uint8(0x61)
Sw1ResponseDataIncomplete = 0x61
SwOK = uint16(0x9000)
SwFileNotFound = uint16(0x6A82)
SwReferencedDataNotFound = uint16(0x6A88)
SwSecurityConditionNotSatisfied = uint16(0x6982)
SwAuthenticationMethodBlocked = uint16(0x6983)
SwOK = 0x9000
SwFileNotFound = 0x6A82
SwReferencedDataNotFound = 0x6A88
SwSecurityConditionNotSatisfied = 0x6982
SwAuthenticationMethodBlocked = 0x6983
tagDeleteAID = byte(0x4F)
tagLoadFileDataBlock = byte(0xC4)
tagGetStatusAID = byte(0x4F)
tagDeleteAID = 0x4F
tagLoadFileDataBlock = 0xC4
tagGetStatusAID = 0x4F
)
// NewCommandSelect returns a Select command as defined in the globalplatform specifications.
@ -51,8 +51,8 @@ func NewCommandSelect(aid []byte) *apdu.Command {
c := apdu.NewCommand(
ClaISO7816,
InsSelect,
uint8(0x04),
uint8(0x00),
0x04,
0,
aid,
)
@ -68,8 +68,8 @@ func NewCommandInitializeUpdate(challenge []byte) *apdu.Command {
c := apdu.NewCommand(
ClaGp,
InsInitializeUpdate,
uint8(0x00),
uint8(0x00),
0,
0,
challenge,
)
@ -91,7 +91,7 @@ func NewCommandExternalAuthenticate(encKey, cardChallenge, hostChallenge []byte)
ClaMac,
InsExternalAuthenticate,
P1ExternalAuthenticateCMAC,
uint8(0x00),
0,
hostCryptogram,
), nil
}
@ -101,8 +101,8 @@ func NewCommandGetResponse(length uint8) *apdu.Command {
c := apdu.NewCommand(
ClaISO7816,
InsGetResponse,
uint8(0),
uint8(0),
0,
0,
nil,
)
@ -119,8 +119,8 @@ func NewCommandDelete(aid []byte) *apdu.Command {
return apdu.NewCommand(
ClaGp,
InsDelete,
uint8(0x00),
uint8(0x00),
0,
0,
data,
)
}
@ -138,7 +138,7 @@ func NewCommandInstallForLoad(aid, sdaid []byte) *apdu.Command {
ClaGp,
InsInstall,
P1InstallForLoad,
uint8(0x00),
0,
data,
)
}
@ -171,7 +171,7 @@ func NewCommandInstallForInstall(pkgAID, appletAID, instanceAID, params []byte)
ClaGp,
InsInstall,
P1InstallForInstall|P1InstallForMakeSelectable,
uint8(0x00),
0,
data,
)
}

View File

@ -1,10 +1,14 @@
package globalplatform
package io
import (
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/keycard-go/apdu"
"github.com/status-im/keycard-go/globalplatform"
"github.com/status-im/keycard-go/hexutils"
)
var logger = log.New("package", "io")
// Transmitter defines an interface with one method to transmit raw commands and receive raw responses.
type Transmitter interface {
Transmit([]byte) ([]byte, error)
@ -41,8 +45,8 @@ func (c *NormalChannel) Send(cmd *apdu.Command) (*apdu.Response, error) {
return nil, err
}
if resp.Sw1 == Sw1ResponseDataIncomplete && (cmd.Cla != ClaISO7816 || cmd.Ins != InsGetResponse) {
getResponse := NewCommandGetResponse(resp.Sw2)
if resp.Sw1 == globalplatform.Sw1ResponseDataIncomplete && (cmd.Cla != globalplatform.ClaISO7816 || cmd.Ins != globalplatform.InsGetResponse) {
getResponse := globalplatform.NewCommandGetResponse(resp.Sw2)
return c.Send(getResponse)
}

View File

@ -9,6 +9,7 @@ import (
"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/hexutils"
"github.com/status-im/keycard-go/types"
)
@ -107,6 +108,8 @@ func (sc *SecureChannel) Send(cmd *apdu.Command) (*apdu.Response, error) {
return nil, ErrInvalidResponseMAC
}
logger.Debug("apdu response decrypted", "hex", hexutils.BytesToHexWithSpaces(plainData))
return apdu.ParseResponse(plainData)
}

View File

@ -18,10 +18,10 @@ const (
)
const (
CapabilitySecureChannel Capability = iota + 1
CapabilityKeyManagement Capability = 1 << iota
CapabilityCredentialsManagement Capability = 1 << iota
CapabilityNDEF Capability = 1 << iota
CapabilitySecureChannel Capability = 1 << iota
CapabilityKeyManagement
CapabilityCredentialsManagement
CapabilityNDEF
CapabilityAll = CapabilitySecureChannel |
CapabilityKeyManagement |