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" "io"
) )
type Tag []byte
var ( var (
ErrUnsupportedLenth80 = errors.New("length cannot be 0x80") ErrUnsupportedLenth80 = errors.New("length cannot be 0x80")
ErrLengthTooBig = errors.New("length cannot be more than 3 bytes") 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. // ErrTagNotFound is an error returned if a tag is not found in a TLV sequence.
type ErrTagNotFound struct { type ErrTagNotFound struct {
tag uint8 tag Tag
} }
// Error implements the error interface // Error implements the error interface
@ -24,16 +26,16 @@ func (e *ErrTagNotFound) Error() string {
} }
// FindTag searches for a tag value within a TLV sequence. // 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...) return findTag(raw, 0, tags...)
} }
// FindTagN searches for a tag value within a TLV sequence and returns the n occurrence // 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...) 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 { if len(tags) == 0 {
return raw, nil return raw, nil
} }
@ -42,13 +44,13 @@ func findTag(raw []byte, occurrence int, tags ...uint8) ([]byte, error) {
buf := bytes.NewBuffer(raw) buf := bytes.NewBuffer(raw)
var ( var (
tag uint8 tag Tag
length uint32 length uint32
err error err error
) )
for { for {
tag, err = buf.ReadByte() tag, buf, err = parseTag(buf)
switch { switch {
case err == io.EOF: case err == io.EOF:
return []byte{}, &ErrTagNotFound{target} 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 it's the last tag in the search path, we start counting the occurrences
if len(tags) == 1 && occurrence > 0 { if len(tags) == 1 && occurrence > 0 {
occurrence-- occurrence--
@ -115,3 +117,29 @@ func parseLength(buf *bytes.Buffer) (uint32, *bytes.Buffer, error) {
return uint32(length), buf, nil 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/status-im/keycard-go/hexutils"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestFindTag(t *testing.T) { 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") 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.NoError(t, err)
assert.Equal(t, "BB CC", hexutils.BytesToHexWithSpaces(tagData)) 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.NoError(t, err)
assert.Equal(t, "C3 02 11 22", hexutils.BytesToHexWithSpaces(tagData)) 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.NoError(t, err)
assert.Equal(t, "88 99", hexutils.BytesToHexWithSpaces(tagData)) 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.NoError(t, err)
assert.Equal(t, "11 22", hexutils.BytesToHexWithSpaces(tagData)) assert.Equal(t, "11 22", hexutils.BytesToHexWithSpaces(tagData))
// tag not found // tag not found
data = hexutils.HexToBytes("C1 00") data = hexutils.HexToBytes("C1 00")
_, err = FindTag(data, uint8(0xC2)) _, err = FindTag(data, Tag{0xC2})
assert.Equal(t, &ErrTagNotFound{uint8(0xC2)}, err) assert.Equal(t, &ErrTagNotFound{Tag{0xC2}}, err)
// sub-tag not found // sub-tag not found
data = hexutils.HexToBytes("C1 02 C2 00") data = hexutils.HexToBytes("C1 02 C2 00")
_, err = FindTag(data, uint8(0xC1), uint8(0xC3)) _, err = FindTag(data, Tag{0xC1}, Tag{0xC3})
assert.Equal(t, &ErrTagNotFound{uint8(0xC3)}, err) assert.Equal(t, &ErrTagNotFound{Tag{0xC3}}, err)
} }
func TestParseLength(t *testing.T) { func TestParseLength(t *testing.T) {
@ -101,11 +102,34 @@ func TestParseLength(t *testing.T) {
func TestFindTagN(t *testing.T) { func TestFindTagN(t *testing.T) {
data := hexutils.HexToBytes("0A 01 A1 0A 01 A2") 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.NoError(t, err)
assert.Equal(t, "A1", hexutils.BytesToHexWithSpaces(tagData)) 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.NoError(t, err)
assert.Equal(t, "A2", hexutils.BytesToHexWithSpaces(tagData)) 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) return cs.checkOK(resp, err)
} }
func (cs *CommandSet) GetStatus() error { func (cs *CommandSet) GetStatus() (*types.CardStatus, error) {
cmd := NewCommandGetStatus([]byte{}, P1GetStatusIssuerSecurityDomain) cmd := NewCommandGetStatus([]byte{}, P1GetStatusIssuerSecurityDomain)
resp, err := cs.sc.Send(cmd) 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 { func (cs *CommandSet) Channel() types.Channel {

View File

@ -84,33 +84,33 @@ func ParseApplicationInfo(data []byte) (*ApplicationInfo, error) {
return nil, ErrWrongApplicationInfoTemplate 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 { if err != nil {
return nil, err 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 { if err != nil {
return nil, err 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 { if err != nil {
return nil, err 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 { if err != nil {
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
capabilities := CapabilityAll capabilities := CapabilityAll
capabilitiesBytes, err := apdu.FindTag(data, TagApplicationInfoCapabilities) capabilitiesBytes, err := apdu.FindTag(data, apdu.Tag{TagApplicationInfoCapabilities})
if err == nil && len(capabilitiesBytes) > 0 { if err == nil && len(capabilitiesBytes) > 0 {
capabilities = Capability(capabilitiesBytes[0]) capabilities = Capability(capabilitiesBytes[0])
} }

View File

@ -20,22 +20,22 @@ type ApplicationStatus struct {
} }
func ParseApplicationStatus(data []byte) (*ApplicationStatus, error) { func ParseApplicationStatus(data []byte) (*ApplicationStatus, error) {
tpl, err := apdu.FindTag(data, TagApplicationStatusTemplate) tpl, err := apdu.FindTag(data, apdu.Tag{TagApplicationStatusTemplate})
if err != nil { if err != nil {
return parseKeyPathStatus(data) return parseKeyPathStatus(data)
} }
appStatus := &ApplicationStatus{} 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]) 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]) 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}) { if bytes.Equal(keyInitialized, []byte{0xFF}) {
appStatus.KeyInitialized = true 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) { 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 { if err != nil {
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
@ -33,7 +33,7 @@ func ParseSignature(message, resp []byte) (*Signature, error) {
r = r[len(r)-32:] 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 { if err != nil {
return nil, err return nil, err
} }