parse BER-TLV with multiple length bytes

This commit is contained in:
Andrea Franz 2019-03-14 12:01:44 +01:00
parent d6f78d1951
commit d8a6a41ca7
No known key found for this signature in database
GPG Key ID: 4F0D2F2D9DE7F29D
2 changed files with 96 additions and 6 deletions

View File

@ -2,10 +2,17 @@ package apdu
import ( import (
"bytes" "bytes"
"encoding/binary"
"errors"
"fmt" "fmt"
"io" "io"
) )
var (
ErrUnsupportedLenth80 = errors.New("length cannot be 0x80")
ErrLengthTooBig = errors.New("length cannot be more than 3 bytes")
)
// 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 uint8
@ -36,7 +43,7 @@ func findTag(raw []byte, occurrence int, tags ...uint8) ([]byte, error) {
var ( var (
tag uint8 tag uint8
length uint8 length uint32
err error err error
) )
@ -49,11 +56,7 @@ func findTag(raw []byte, occurrence int, tags ...uint8) ([]byte, error) {
return nil, err return nil, err
} }
length, err = buf.ReadByte() length, buf, err = parseLength(buf)
if err != nil {
return nil, err
}
data := make([]byte, length) data := make([]byte, length)
if length != 0 { if length != 0 {
_, err = buf.Read(data) _, err = buf.Read(data)
@ -77,3 +80,34 @@ func findTag(raw []byte, occurrence int, tags ...uint8) ([]byte, error) {
} }
} }
} }
func parseLength(buf *bytes.Buffer) (uint32, *bytes.Buffer, error) {
length, err := buf.ReadByte()
if err != nil {
return 0, nil, err
}
if length == 0x80 {
return 0, nil, ErrUnsupportedLenth80
}
if length > 0x80 {
lengthSize := length - 0x80
if lengthSize > 3 {
return 0, nil, ErrLengthTooBig
}
data := make([]byte, lengthSize)
_, err = buf.Read(data)
if err != nil {
return 0, nil, err
}
num := make([]byte, 4)
copy(num[4-lengthSize:], data)
return binary.BigEndian.Uint32(num), buf, nil
}
return uint32(length), buf, nil
}

View File

@ -1,6 +1,7 @@
package apdu package apdu
import ( import (
"bytes"
"testing" "testing"
"github.com/status-im/keycard-go/hexutils" "github.com/status-im/keycard-go/hexutils"
@ -42,6 +43,61 @@ func TestFindTag(t *testing.T) {
assert.Equal(t, &ErrTagNotFound{uint8(0xC3)}, err) assert.Equal(t, &ErrTagNotFound{uint8(0xC3)}, err)
} }
func TestParseLength(t *testing.T) {
scenarios := []struct {
data []byte
expectedLength uint32
err error
}{
{
data: []byte{0x01, 0xAA},
expectedLength: 1,
err: nil,
},
{
data: []byte{0x7F, 0xAA},
expectedLength: 127,
err: nil,
},
{
data: []byte{0x81, 0x80, 0xAA},
expectedLength: 128,
err: nil,
},
{
data: []byte{0x82, 0x80, 0x80, 0xAA},
expectedLength: 32896,
err: nil,
},
{
data: []byte{0x83, 0x80, 0x80, 0x80, 0xAA},
expectedLength: 8421504,
err: nil,
},
{
data: []byte{0x80, 0xAA},
expectedLength: 0,
err: ErrUnsupportedLenth80,
},
{
data: []byte{0x84, 0xAA},
expectedLength: 0,
err: ErrLengthTooBig,
},
}
for _, s := range scenarios {
buf := bytes.NewBuffer(s.data)
length, _, err := parseLength(buf)
if s.err == nil {
assert.NoError(t, err)
assert.Equal(t, s.expectedLength, length)
} else {
assert.Equal(t, s.err, err)
}
}
}
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")