345 lines
10 KiB
Go
345 lines
10 KiB
Go
|
package ber
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"io"
|
||
|
"math"
|
||
|
"testing"
|
||
|
)
|
||
|
|
||
|
func TestReadIdentifier(t *testing.T) {
|
||
|
testcases := map[string]struct {
|
||
|
Data []byte
|
||
|
|
||
|
ExpectedIdentifier Identifier
|
||
|
ExpectedBytesRead int
|
||
|
ExpectedError string
|
||
|
}{
|
||
|
"empty": {
|
||
|
Data: []byte{},
|
||
|
ExpectedBytesRead: 0,
|
||
|
ExpectedError: io.ErrUnexpectedEOF.Error(),
|
||
|
},
|
||
|
|
||
|
"universal primitive eoc": {
|
||
|
Data: []byte{byte(ClassUniversal) | byte(TypePrimitive) | byte(TagEOC)},
|
||
|
ExpectedIdentifier: Identifier{
|
||
|
ClassType: ClassUniversal,
|
||
|
TagType: TypePrimitive,
|
||
|
Tag: TagEOC,
|
||
|
},
|
||
|
ExpectedBytesRead: 1,
|
||
|
},
|
||
|
"universal primitive character string": {
|
||
|
Data: []byte{byte(ClassUniversal) | byte(TypePrimitive) | byte(TagCharacterString)},
|
||
|
ExpectedIdentifier: Identifier{
|
||
|
ClassType: ClassUniversal,
|
||
|
TagType: TypePrimitive,
|
||
|
Tag: TagCharacterString,
|
||
|
},
|
||
|
ExpectedBytesRead: 1,
|
||
|
},
|
||
|
|
||
|
"universal constructed bit string": {
|
||
|
Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(TagBitString)},
|
||
|
ExpectedIdentifier: Identifier{
|
||
|
ClassType: ClassUniversal,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: TagBitString,
|
||
|
},
|
||
|
ExpectedBytesRead: 1,
|
||
|
},
|
||
|
"universal constructed character string": {
|
||
|
Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(TagCharacterString)},
|
||
|
ExpectedIdentifier: Identifier{
|
||
|
ClassType: ClassUniversal,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: TagCharacterString,
|
||
|
},
|
||
|
ExpectedBytesRead: 1,
|
||
|
},
|
||
|
|
||
|
"application constructed object descriptor": {
|
||
|
Data: []byte{byte(ClassApplication) | byte(TypeConstructed) | byte(TagObjectDescriptor)},
|
||
|
ExpectedIdentifier: Identifier{
|
||
|
ClassType: ClassApplication,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: TagObjectDescriptor,
|
||
|
},
|
||
|
ExpectedBytesRead: 1,
|
||
|
},
|
||
|
"context constructed object descriptor": {
|
||
|
Data: []byte{byte(ClassContext) | byte(TypeConstructed) | byte(TagObjectDescriptor)},
|
||
|
ExpectedIdentifier: Identifier{
|
||
|
ClassType: ClassContext,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: TagObjectDescriptor,
|
||
|
},
|
||
|
ExpectedBytesRead: 1,
|
||
|
},
|
||
|
"private constructed object descriptor": {
|
||
|
Data: []byte{byte(ClassPrivate) | byte(TypeConstructed) | byte(TagObjectDescriptor)},
|
||
|
ExpectedIdentifier: Identifier{
|
||
|
ClassType: ClassPrivate,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: TagObjectDescriptor,
|
||
|
},
|
||
|
ExpectedBytesRead: 1,
|
||
|
},
|
||
|
|
||
|
"high-tag-number tag missing bytes": {
|
||
|
Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag)},
|
||
|
ExpectedError: io.ErrUnexpectedEOF.Error(),
|
||
|
ExpectedBytesRead: 1,
|
||
|
},
|
||
|
"high-tag-number tag invalid first byte": {
|
||
|
Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), 0x0},
|
||
|
ExpectedError: "invalid first high-tag-number tag byte",
|
||
|
ExpectedBytesRead: 2,
|
||
|
},
|
||
|
"high-tag-number tag invalid first byte with continue bit": {
|
||
|
Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), byte(HighTagContinueBitmask)},
|
||
|
ExpectedError: "invalid first high-tag-number tag byte",
|
||
|
ExpectedBytesRead: 2,
|
||
|
},
|
||
|
"high-tag-number tag continuation missing bytes": {
|
||
|
Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), byte(HighTagContinueBitmask | 0x1)},
|
||
|
ExpectedError: io.ErrUnexpectedEOF.Error(),
|
||
|
ExpectedBytesRead: 2,
|
||
|
},
|
||
|
"high-tag-number tag overflow": {
|
||
|
Data: []byte{
|
||
|
byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag),
|
||
|
byte(HighTagContinueBitmask | 0x1),
|
||
|
byte(HighTagContinueBitmask | 0x1),
|
||
|
byte(HighTagContinueBitmask | 0x1),
|
||
|
byte(HighTagContinueBitmask | 0x1),
|
||
|
byte(HighTagContinueBitmask | 0x1),
|
||
|
byte(HighTagContinueBitmask | 0x1),
|
||
|
byte(HighTagContinueBitmask | 0x1),
|
||
|
byte(HighTagContinueBitmask | 0x1),
|
||
|
byte(HighTagContinueBitmask | 0x1),
|
||
|
byte(0x1),
|
||
|
},
|
||
|
ExpectedError: "high-tag-number tag overflow",
|
||
|
ExpectedBytesRead: 11,
|
||
|
},
|
||
|
"max high-tag-number tag": {
|
||
|
Data: []byte{
|
||
|
byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(0x7f),
|
||
|
},
|
||
|
ExpectedIdentifier: Identifier{
|
||
|
ClassType: ClassUniversal,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: Tag(0x7FFFFFFFFFFFFFFF), // 01111111...(63)...11111b
|
||
|
},
|
||
|
ExpectedBytesRead: 10,
|
||
|
},
|
||
|
"high-tag-number encoding of low-tag value": {
|
||
|
Data: []byte{
|
||
|
byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag),
|
||
|
byte(TagObjectDescriptor),
|
||
|
},
|
||
|
ExpectedIdentifier: Identifier{
|
||
|
ClassType: ClassUniversal,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: TagObjectDescriptor,
|
||
|
},
|
||
|
ExpectedBytesRead: 2,
|
||
|
},
|
||
|
"max high-tag-number tag ignores extra data": {
|
||
|
Data: []byte{
|
||
|
byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(0x7f),
|
||
|
byte(0x01), // extra data, shouldn't be read
|
||
|
byte(0x02), // extra data, shouldn't be read
|
||
|
byte(0x03), // extra data, shouldn't be read
|
||
|
},
|
||
|
ExpectedIdentifier: Identifier{
|
||
|
ClassType: ClassUniversal,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: Tag(0x7FFFFFFFFFFFFFFF), // 01111111...(63)...11111b
|
||
|
},
|
||
|
ExpectedBytesRead: 10,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for k, tc := range testcases {
|
||
|
reader := bytes.NewBuffer(tc.Data)
|
||
|
identifier, read, err := readIdentifier(reader)
|
||
|
|
||
|
if err != nil {
|
||
|
if tc.ExpectedError == "" {
|
||
|
t.Errorf("%s: unexpected error: %v", k, err)
|
||
|
} else if err.Error() != tc.ExpectedError {
|
||
|
t.Errorf("%s: expected error %v, got %v", k, tc.ExpectedError, err)
|
||
|
}
|
||
|
} else if tc.ExpectedError != "" {
|
||
|
t.Errorf("%s: expected error %v, got none", k, tc.ExpectedError)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if read != tc.ExpectedBytesRead {
|
||
|
t.Errorf("%s: expected read %d, got %d", k, tc.ExpectedBytesRead, read)
|
||
|
}
|
||
|
|
||
|
if identifier.ClassType != tc.ExpectedIdentifier.ClassType {
|
||
|
t.Errorf("%s: expected class type %d (%s), got %d (%s)", k,
|
||
|
tc.ExpectedIdentifier.ClassType,
|
||
|
ClassMap[tc.ExpectedIdentifier.ClassType],
|
||
|
identifier.ClassType,
|
||
|
ClassMap[identifier.ClassType],
|
||
|
)
|
||
|
}
|
||
|
if identifier.TagType != tc.ExpectedIdentifier.TagType {
|
||
|
t.Errorf("%s: expected tag type %d (%s), got %d (%s)", k,
|
||
|
tc.ExpectedIdentifier.TagType,
|
||
|
TypeMap[tc.ExpectedIdentifier.TagType],
|
||
|
identifier.TagType,
|
||
|
TypeMap[identifier.TagType],
|
||
|
)
|
||
|
}
|
||
|
if identifier.Tag != tc.ExpectedIdentifier.Tag {
|
||
|
t.Errorf("%s: expected tag %d (%s), got %d (%s)", k,
|
||
|
tc.ExpectedIdentifier.Tag,
|
||
|
tagMap[tc.ExpectedIdentifier.Tag],
|
||
|
identifier.Tag,
|
||
|
tagMap[identifier.Tag],
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestEncodeIdentifier(t *testing.T) {
|
||
|
testcases := map[string]struct {
|
||
|
Identifier Identifier
|
||
|
ExpectedBytes []byte
|
||
|
}{
|
||
|
"universal primitive eoc": {
|
||
|
Identifier: Identifier{
|
||
|
ClassType: ClassUniversal,
|
||
|
TagType: TypePrimitive,
|
||
|
Tag: TagEOC,
|
||
|
},
|
||
|
ExpectedBytes: []byte{byte(ClassUniversal) | byte(TypePrimitive) | byte(TagEOC)},
|
||
|
},
|
||
|
"universal primitive character string": {
|
||
|
Identifier: Identifier{
|
||
|
ClassType: ClassUniversal,
|
||
|
TagType: TypePrimitive,
|
||
|
Tag: TagCharacterString,
|
||
|
},
|
||
|
ExpectedBytes: []byte{byte(ClassUniversal) | byte(TypePrimitive) | byte(TagCharacterString)},
|
||
|
},
|
||
|
|
||
|
"universal constructed bit string": {
|
||
|
Identifier: Identifier{
|
||
|
ClassType: ClassUniversal,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: TagBitString,
|
||
|
},
|
||
|
ExpectedBytes: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(TagBitString)},
|
||
|
},
|
||
|
"universal constructed character string": {
|
||
|
Identifier: Identifier{
|
||
|
ClassType: ClassUniversal,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: TagCharacterString,
|
||
|
},
|
||
|
ExpectedBytes: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(TagCharacterString)},
|
||
|
},
|
||
|
|
||
|
"application constructed object descriptor": {
|
||
|
Identifier: Identifier{
|
||
|
ClassType: ClassApplication,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: TagObjectDescriptor,
|
||
|
},
|
||
|
ExpectedBytes: []byte{byte(ClassApplication) | byte(TypeConstructed) | byte(TagObjectDescriptor)},
|
||
|
},
|
||
|
"context constructed object descriptor": {
|
||
|
Identifier: Identifier{
|
||
|
ClassType: ClassContext,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: TagObjectDescriptor,
|
||
|
},
|
||
|
ExpectedBytes: []byte{byte(ClassContext) | byte(TypeConstructed) | byte(TagObjectDescriptor)},
|
||
|
},
|
||
|
"private constructed object descriptor": {
|
||
|
Identifier: Identifier{
|
||
|
ClassType: ClassPrivate,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: TagObjectDescriptor,
|
||
|
},
|
||
|
ExpectedBytes: []byte{byte(ClassPrivate) | byte(TypeConstructed) | byte(TagObjectDescriptor)},
|
||
|
},
|
||
|
|
||
|
"max low-tag-number tag": {
|
||
|
Identifier: Identifier{
|
||
|
ClassType: ClassUniversal,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: TagBMPString,
|
||
|
},
|
||
|
ExpectedBytes: []byte{
|
||
|
byte(ClassUniversal) | byte(TypeConstructed) | byte(TagBMPString),
|
||
|
},
|
||
|
},
|
||
|
|
||
|
"min high-tag-number tag": {
|
||
|
Identifier: Identifier{
|
||
|
ClassType: ClassUniversal,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: TagBMPString + 1,
|
||
|
},
|
||
|
ExpectedBytes: []byte{
|
||
|
byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag),
|
||
|
byte(TagBMPString + 1),
|
||
|
},
|
||
|
},
|
||
|
|
||
|
"max high-tag-number tag": {
|
||
|
Identifier: Identifier{
|
||
|
ClassType: ClassUniversal,
|
||
|
TagType: TypeConstructed,
|
||
|
Tag: Tag(math.MaxInt64),
|
||
|
},
|
||
|
ExpectedBytes: []byte{
|
||
|
byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(HighTagContinueBitmask | 0x7f),
|
||
|
byte(0x7f),
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for k, tc := range testcases {
|
||
|
b := encodeIdentifier(tc.Identifier)
|
||
|
if bytes.Compare(tc.ExpectedBytes, b) != 0 {
|
||
|
t.Errorf("%s: Expected\n\t%#v\ngot\n\t%#v", k, tc.ExpectedBytes, b)
|
||
|
}
|
||
|
}
|
||
|
}
|