diff --git a/apdu/utils.go b/apdu/utils.go index 86cfaa2..3962b64 100644 --- a/apdu/utils.go +++ b/apdu/utils.go @@ -2,10 +2,17 @@ package apdu import ( "bytes" + "encoding/binary" + "errors" "fmt" "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. type ErrTagNotFound struct { tag uint8 @@ -36,7 +43,7 @@ func findTag(raw []byte, occurrence int, tags ...uint8) ([]byte, error) { var ( tag uint8 - length uint8 + length uint32 err error ) @@ -49,11 +56,7 @@ func findTag(raw []byte, occurrence int, tags ...uint8) ([]byte, error) { return nil, err } - length, err = buf.ReadByte() - if err != nil { - return nil, err - } - + length, buf, err = parseLength(buf) data := make([]byte, length) if length != 0 { _, 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 +} diff --git a/apdu/utils_test.go b/apdu/utils_test.go index 0e61027..32656d0 100644 --- a/apdu/utils_test.go +++ b/apdu/utils_test.go @@ -1,6 +1,7 @@ package apdu import ( + "bytes" "testing" "github.com/status-im/keycard-go/hexutils" @@ -42,6 +43,61 @@ func TestFindTag(t *testing.T) { 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) { data := hexutils.HexToBytes("0A 01 A1 0A 01 A2")