parse multi bytes ber-tlv tags

This commit is contained in:
Andrea Franz 2019-04-18 01:36:40 +02:00
parent dbfad2a8b0
commit 448e4918b8
No known key found for this signature in database
GPG Key ID: 4F0D2F2D9DE7F29D
7 changed files with 161 additions and 32 deletions

View File

@ -8,6 +8,8 @@ import (
"io"
)
type Tag []byte
var (
ErrUnsupportedLenth80 = errors.New("length cannot be 0x80")
ErrLengthTooBig = errors.New("length cannot be more than 3 bytes")
@ -15,7 +17,7 @@ var (
// ErrTagNotFound is an error returned if a tag is not found in a TLV sequence.
type ErrTagNotFound struct {
tag uint8
tag Tag
}
// Error implements the error interface
@ -24,16 +26,16 @@ func (e *ErrTagNotFound) Error() string {
}
// FindTag searches for a tag value within a TLV sequence.
func FindTag(raw []byte, tags ...uint8) ([]byte, error) {
func FindTag(raw []byte, tags ...Tag) ([]byte, error) {
return findTag(raw, 0, tags...)
}
// FindTagN searches for a tag value within a TLV sequence and returns the n occurrence
func FindTagN(raw []byte, n int, tags ...uint8) ([]byte, error) {
func FindTagN(raw []byte, n int, tags ...Tag) ([]byte, error) {
return findTag(raw, n, tags...)
}
func findTag(raw []byte, occurrence int, tags ...uint8) ([]byte, error) {
func findTag(raw []byte, occurrence int, tags ...Tag) ([]byte, error) {
if len(tags) == 0 {
return raw, nil
}
@ -42,13 +44,13 @@ func findTag(raw []byte, occurrence int, tags ...uint8) ([]byte, error) {
buf := bytes.NewBuffer(raw)
var (
tag uint8
tag Tag
length uint32
err error
)
for {
tag, err = buf.ReadByte()
tag, buf, err = parseTag(buf)
switch {
case err == io.EOF:
return []byte{}, &ErrTagNotFound{target}
@ -69,7 +71,7 @@ func findTag(raw []byte, occurrence int, tags ...uint8) ([]byte, error) {
}
}
if tag == target {
if bytes.Equal(tag, target) {
// if it's the last tag in the search path, we start counting the occurrences
if len(tags) == 1 && occurrence > 0 {
occurrence--
@ -115,3 +117,29 @@ func parseLength(buf *bytes.Buffer) (uint32, *bytes.Buffer, error) {
return uint32(length), buf, nil
}
func parseTag(buf *bytes.Buffer) (Tag, *bytes.Buffer, error) {
tag := make(Tag, 0)
b, err := buf.ReadByte()
if err != nil {
return nil, nil, err
}
tag = append(tag, b)
if b&0x1F != 0x1F {
return tag, buf, nil
}
for {
b, err = buf.ReadByte()
if err != nil {
return nil, nil, err
}
tag = append(tag, b)
if b&0x80 != 0x80 {
return tag, buf, nil
}
}
}

View File

@ -6,6 +6,7 @@ import (
"github.com/status-im/keycard-go/hexutils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFindTag(t *testing.T) {
@ -16,31 +17,31 @@ func TestFindTag(t *testing.T) {
data := hexutils.HexToBytes("C1 02 BB CC C2 04 C3 02 11 22 C3 02 88 99")
tagData, err = FindTag(data, uint8(0xC1))
tagData, err = FindTag(data, Tag{0xC1})
assert.NoError(t, err)
assert.Equal(t, "BB CC", hexutils.BytesToHexWithSpaces(tagData))
tagData, err = FindTag(data, uint8(0xC2))
tagData, err = FindTag(data, Tag{0xC2})
assert.NoError(t, err)
assert.Equal(t, "C3 02 11 22", hexutils.BytesToHexWithSpaces(tagData))
tagData, err = FindTag(data, uint8(0xC3))
tagData, err = FindTag(data, Tag{0xC3})
assert.NoError(t, err)
assert.Equal(t, "88 99", hexutils.BytesToHexWithSpaces(tagData))
tagData, err = FindTag(data, uint8(0xC2), uint8(0xC3))
tagData, err = FindTag(data, Tag{0xC2}, Tag{0xC3})
assert.NoError(t, err)
assert.Equal(t, "11 22", hexutils.BytesToHexWithSpaces(tagData))
// tag not found
data = hexutils.HexToBytes("C1 00")
_, err = FindTag(data, uint8(0xC2))
assert.Equal(t, &ErrTagNotFound{uint8(0xC2)}, err)
_, err = FindTag(data, Tag{0xC2})
assert.Equal(t, &ErrTagNotFound{Tag{0xC2}}, err)
// sub-tag not found
data = hexutils.HexToBytes("C1 02 C2 00")
_, err = FindTag(data, uint8(0xC1), uint8(0xC3))
assert.Equal(t, &ErrTagNotFound{uint8(0xC3)}, err)
_, err = FindTag(data, Tag{0xC1}, Tag{0xC3})
assert.Equal(t, &ErrTagNotFound{Tag{0xC3}}, err)
}
func TestParseLength(t *testing.T) {
@ -101,11 +102,34 @@ func TestParseLength(t *testing.T) {
func TestFindTagN(t *testing.T) {
data := hexutils.HexToBytes("0A 01 A1 0A 01 A2")
tagData, err := FindTagN(data, 0, uint8(0x0A))
tagData, err := FindTagN(data, 0, Tag{0x0A})
assert.NoError(t, err)
assert.Equal(t, "A1", hexutils.BytesToHexWithSpaces(tagData))
tagData, err = FindTagN(data, 1, uint8(0x0A))
tagData, err = FindTagN(data, 1, Tag{0x0A})
assert.NoError(t, err)
assert.Equal(t, "A2", hexutils.BytesToHexWithSpaces(tagData))
}
func TestParseTag(t *testing.T) {
scenarios := []struct {
rawTag []byte
expectedTag Tag
}{
{
rawTag: []byte{0x01, 0x02},
expectedTag: Tag{0x01},
},
{
rawTag: []byte{0x9F, 0x70, 0x01},
expectedTag: Tag{0x9f, 0x70},
},
}
for _, s := range scenarios {
buf := bytes.NewBuffer(s.rawTag)
tag, _, err := parseTag(buf)
require.Nil(t, err)
assert.Equal(t, s.expectedTag, tag)
}
}

View File

@ -143,10 +143,14 @@ func (cs *CommandSet) InstallForInstall(packageAID, appletAID, instanceAID, para
return cs.checkOK(resp, err)
}
func (cs *CommandSet) GetStatus() error {
func (cs *CommandSet) GetStatus() (*types.CardStatus, error) {
cmd := NewCommandGetStatus([]byte{}, P1GetStatusIssuerSecurityDomain)
resp, err := cs.sc.Send(cmd)
return cs.checkOK(resp, err)
if err = cs.checkOK(resp, err); err != nil {
return nil, err
}
return types.ParseCardStatus(resp.Data)
}
func (cs *CommandSet) Channel() types.Channel {

View File

@ -84,33 +84,33 @@ func ParseApplicationInfo(data []byte) (*ApplicationInfo, error) {
return nil, ErrWrongApplicationInfoTemplate
}
instanceUID, err := apdu.FindTag(data, TagApplicationInfoTemplate, uint8(0x8F))
instanceUID, err := apdu.FindTag(data, apdu.Tag{TagApplicationInfoTemplate}, apdu.Tag{0x8F})
if err != nil {
return nil, err
}
pubKey, err := apdu.FindTag(data, TagApplicationInfoTemplate, uint8(0x80))
pubKey, err := apdu.FindTag(data, apdu.Tag{TagApplicationInfoTemplate}, apdu.Tag{0x80})
if err != nil {
return nil, err
}
appVersion, err := apdu.FindTag(data, TagApplicationInfoTemplate, uint8(0x02))
appVersion, err := apdu.FindTag(data, apdu.Tag{TagApplicationInfoTemplate}, apdu.Tag{0x02})
if err != nil {
return nil, err
}
availableSlots, err := apdu.FindTagN(data, 1, TagApplicationInfoTemplate, uint8(0x02))
availableSlots, err := apdu.FindTagN(data, 1, apdu.Tag{TagApplicationInfoTemplate}, apdu.Tag{0x02})
if err != nil {
return nil, err
}
keyUID, err := apdu.FindTagN(data, 0, TagApplicationInfoTemplate, uint8(0x8E))
keyUID, err := apdu.FindTagN(data, 0, apdu.Tag{TagApplicationInfoTemplate}, apdu.Tag{0x8E})
if err != nil {
return nil, err
}
capabilities := CapabilityAll
capabilitiesBytes, err := apdu.FindTag(data, TagApplicationInfoCapabilities)
capabilitiesBytes, err := apdu.FindTag(data, apdu.Tag{TagApplicationInfoCapabilities})
if err == nil && len(capabilitiesBytes) > 0 {
capabilities = Capability(capabilitiesBytes[0])
}

View File

@ -20,22 +20,22 @@ type ApplicationStatus struct {
}
func ParseApplicationStatus(data []byte) (*ApplicationStatus, error) {
tpl, err := apdu.FindTag(data, TagApplicationStatusTemplate)
tpl, err := apdu.FindTag(data, apdu.Tag{TagApplicationStatusTemplate})
if err != nil {
return parseKeyPathStatus(data)
}
appStatus := &ApplicationStatus{}
if pinRetryCount, err := apdu.FindTag(tpl, uint8(0x02)); err == nil && len(pinRetryCount) == 1 {
if pinRetryCount, err := apdu.FindTag(tpl, apdu.Tag{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 {
if pukRetryCount, err := apdu.FindTagN(tpl, 1, apdu.Tag{0x02}); err == nil && len(pukRetryCount) == 1 {
appStatus.PUKRetryCount = int(pukRetryCount[0])
}
if keyInitialized, err := apdu.FindTag(tpl, uint8(0x01)); err == nil {
if keyInitialized, err := apdu.FindTag(tpl, apdu.Tag{0x01}); err == nil {
if bytes.Equal(keyInitialized, []byte{0xFF}) {
appStatus.KeyInitialized = true
}

73
types/card_status.go Normal file
View File

@ -0,0 +1,73 @@
package types
import (
"fmt"
"github.com/status-im/keycard-go/apdu"
)
type lifeCycle byte
var (
TagGetStatusTemplate = apdu.Tag{0xE3}
TagGetStatusLifeCycleState = apdu.Tag{0x9F, 0x70}
)
const (
LifeCycleOpReady lifeCycle = 0x01
LifeCycleInitialized = 0x07
LifeCycleSecured = 0x0F
LifeCycleCardLocked = 0x7F
LifeCycleTerminated = 0xFF
)
func (lc lifeCycle) String() string {
switch lc {
case LifeCycleOpReady:
return "OP_READY"
case LifeCycleInitialized:
return "INITIALIZED"
case LifeCycleSecured:
return "SECURED"
case LifeCycleCardLocked:
return "CARD_LOCKED"
case LifeCycleTerminated:
return "TERMINATED"
default:
return "UNKNOWN"
}
}
type ErrInvalidLifeCycleValue struct {
lc []byte
}
func (e *ErrInvalidLifeCycleValue) Error() string {
return fmt.Sprintf("life cycle value must be 1 byte. got %d byte: %x", len(e.lc), e.lc)
}
type CardStatus struct {
lc lifeCycle
}
func (cs *CardStatus) LifeCycle() string {
return cs.lc.String()
}
func ParseCardStatus(data []byte) (*CardStatus, error) {
tpl, err := apdu.FindTag(data, TagGetStatusTemplate)
if err != nil {
return nil, err
}
lc, err := apdu.FindTag(tpl, TagGetStatusLifeCycleState)
if err != nil {
return nil, err
}
if len(lc) != 1 {
return nil, &ErrInvalidLifeCycleValue{lc}
}
return &CardStatus{lifeCycle(lc[0])}, nil
}

View File

@ -19,12 +19,12 @@ type Signature struct {
}
func ParseSignature(message, resp []byte) (*Signature, error) {
pubKey, err := apdu.FindTag(resp, TagSignatureTemplate, uint8(0x80))
pubKey, err := apdu.FindTag(resp, apdu.Tag{TagSignatureTemplate}, apdu.Tag{0x80})
if err != nil {
return nil, err
}
r, err := apdu.FindTagN(resp, 0, TagSignatureTemplate, uint8(0x30), uint8(0x02))
r, err := apdu.FindTagN(resp, 0, apdu.Tag{TagSignatureTemplate}, apdu.Tag{0x30}, apdu.Tag{0x02})
if err != nil {
return nil, err
}
@ -33,7 +33,7 @@ func ParseSignature(message, resp []byte) (*Signature, error) {
r = r[len(r)-32:]
}
s, err := apdu.FindTagN(resp, 1, TagSignatureTemplate, uint8(0x30), uint8(0x02))
s, err := apdu.FindTagN(resp, 1, apdu.Tag{TagSignatureTemplate}, apdu.Tag{0x30}, apdu.Tag{0x02})
if err != nil {
return nil, err
}