mirror of
https://github.com/status-im/keycard-go.git
synced 2025-01-09 11:32:34 +00:00
parse multi bytes ber-tlv tags
This commit is contained in:
parent
dbfad2a8b0
commit
448e4918b8
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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])
|
||||||
}
|
}
|
||||||
|
@ -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
73
types/card_status.go
Normal 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
|
||||||
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user