Skip to content

Commit

Permalink
Encoding.ascii tlv tag (#236)
Browse files Browse the repository at this point in the history
* add encoding.AsciiTLVTag

* remove blank line

* add encoding.AsciiTLVTag

* add encoding.AsciiTLVTag

* add encoding.AsciiTLVTag

* add encoding.AsciiTLVTag

* add unit tests

* use require instead of assert

* don't add new encoding, define the prefixer for unknown tags

* fix ineffectual assignment to maxLen (ineffassign)

* by default, we use BER-TVL prefix

* only one test

* fix doc
  • Loading branch information
jerome-laforge authored Jun 7, 2023
1 parent f600fa5 commit ba8b8b3
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 10 deletions.
29 changes: 22 additions & 7 deletions field/composite.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"math"
"reflect"
"regexp"
"strconv"
Expand Down Expand Up @@ -539,9 +540,13 @@ func (f *Composite) unpackSubfieldsByBitmap(data []byte) (int, error) {
return off, nil
}

// ignoredMaxLen is a constant meant to be used in encoders that don't use the maxLength argument during
// length decoding.
const ignoredMaxLen int = 0
const (
// ignoredMaxLen is a constant meant to be used in encoders that don't use the maxLength argument during
// length decoding.
ignoredMaxLen int = 0
// maxLenOfUnknownTag is max int in order to never hit this limit.
maxLenOfUnknownTag = math.MaxInt
)

func (f *Composite) unpackSubfieldsByTag(data []byte) (int, error) {
offset := 0
Expand All @@ -558,9 +563,19 @@ func (f *Composite) unpackSubfieldsByTag(data []byte) (int, error) {
tag := string(tagBytes)
if _, ok := f.spec.Subfields[tag]; !ok {
if f.skipUnknownTLVTags() {
// Obtain the length of the unknown tag and add it to the offset.
// Because BER-TLV lengths are decoded dynamically, the maxLen method argument is ignored.
fieldLength, readed, err := prefix.BerTLV.DecodeLength(ignoredMaxLen, data[offset:])
// to obtain the length of the unknown tag and add it to the offset we need to decode its length
// by default, we use BER-TVL prefix because BER-TLV lengths are decoded dynamically, the maxLen method argument is ignored.
var (
pref prefix.Prefixer = prefix.BerTLV
maxLen = ignoredMaxLen
)
// but if PrefUnknownTLV prefix is set, use it and hope that length of all unknown tags is encoded using this prefixer
if f.spec.Tag.PrefUnknownTLV != nil {
pref = f.spec.Tag.PrefUnknownTLV
maxLen = maxLenOfUnknownTag
}

fieldLength, readed, err := pref.DecodeLength(maxLen, data[offset:])
if err != nil {
return 0, err
}
Expand Down Expand Up @@ -589,7 +604,7 @@ func (f *Composite) unpackSubfieldsByTag(data []byte) (int, error) {
}

func (f *Composite) skipUnknownTLVTags() bool {
return f.spec.Tag != nil && f.spec.Tag.SkipUnknownTLVTags && f.spec.Tag.Enc == encoding.BerTLVTag
return f.spec.Tag != nil && f.spec.Tag.SkipUnknownTLVTags && (f.spec.Tag.Enc == encoding.BerTLVTag || f.spec.Tag.PrefUnknownTLV != nil)
}

func validateCompositeSpec(spec *Spec) error {
Expand Down
45 changes: 45 additions & 0 deletions field/composite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,33 @@ var (
}),
},
}

tlvASCIILSpec = &Spec{
Length: 999,
Description: "ASCII Unknown TLV with PrefUnknownTLV: prefix.ASCII.L",
Pref: prefix.ASCII.LLL,
Tag: &TagSpec{
Length: 2,
Enc: encoding.ASCII,
Sort: sort.Strings,
SkipUnknownTLVTags: true,
PrefUnknownTLV: prefix.ASCII.L,
},
Subfields: map[string]Field{
"01": NewString(&Spec{
Length: 9,
Description: "String Field",
Enc: encoding.ASCII,
Pref: prefix.ASCII.L,
}),
"02": NewString(&Spec{
Length: 9,
Description: "String Field",
Enc: encoding.ASCII,
Pref: prefix.ASCII.L,
}),
},
}
)

type CompositeTestData struct {
Expand Down Expand Up @@ -537,6 +564,24 @@ func TestTLVPacking(t *testing.T) {
0x9f, 0x2, 0x6, 0x0, 0x0, 0x0, 0x0, 0x5, 0x1, 0x9f, 0x37, 0x4, 0x9b, 0xad, 0xbc, 0xab})
require.EqualError(t, err, "failed to unpack subfield 9F36: field not defined in Spec")
})

t.Run("ASCII Unknown TLV with PrefUnknownTLV: prefix.ASCII.L: unpack correct deserialises bytes to the data struct skipping unexpected tags", func(t *testing.T) {
// The field's specification contains the tags 9A and 9F02
composite := NewComposite(tlvASCIILSpec)

_, err := composite.Unpack([]byte{0x30, 0x31, 0x35, 0x30, 0x31, 0x32, 0x33, 0x34, 0x39, 0x39, 0x31, 0x30, 0x30, 0x32, 0x33, 0x34, 0x35, 0x36})
require.NoError(t, err)

require.Len(t, composite.subfields, 2)

v, err := composite.subfields["01"].String()
require.NoError(t, err)
require.Equal(t, "34", v)

v, err = composite.subfields["02"].String()
require.NoError(t, err)
require.Equal(t, "456", v)
})
}

func TestCompositePacking(t *testing.T) {
Expand Down
4 changes: 3 additions & 1 deletion field/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
type TagSpec struct {
// Length is defined for subfields and subelements whose tag
// lengths are fixed and can be defined statically.
// This field should not be populated in conjunction with the TLV Tag
// This field should not be populated in conjunction with the BerTLV Tag
// encoder as their lengths are determined dynamically.
Length int
// Enc defines the encoder used to marshal and unmarshal
Expand All @@ -36,6 +36,8 @@ type TagSpec struct {
// By default, this flag is disabled and unexpected TLV tags will throw an error.
// This flag is only meant to be used in Composite fields with TLV encoding.
SkipUnknownTLVTags bool
// PrefUnknownTLV is used for skipping unknown TLV if it is not nil
PrefUnknownTLV prefix.Prefixer
}

// Spec defines the structure of a field.
Expand Down
4 changes: 2 additions & 2 deletions prefix/bertlv.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type berTLVPrefixer struct{}
// according to the rules defined above.
// NOTE: Because BER-TLV lengths are encoded dynamically, the maxLen method
// argument is ignored.
func (p *berTLVPrefixer) EncodeLength(maxLen, dataLen int) ([]byte, error) {
func (p *berTLVPrefixer) EncodeLength(_, dataLen int) ([]byte, error) {
buf := big.NewInt(int64(dataLen)).Bytes()
if dataLen <= 127 {
return buf, nil
Expand All @@ -45,7 +45,7 @@ func (p *berTLVPrefixer) EncodeLength(maxLen, dataLen int) ([]byte, error) {
// as well as the number bytes read to decode the length are returned.
// NOTE: Because BER-TLV lengths are decoded dynamically, the maxLen method
// argument is ignored.
func (p *berTLVPrefixer) DecodeLength(maxLen int, data []byte) (int, int, error) {
func (p *berTLVPrefixer) DecodeLength(_ int, data []byte) (int, int, error) {
r := bytes.NewReader(data)

firstByte, err := r.ReadByte()
Expand Down

0 comments on commit ba8b8b3

Please sign in to comment.