Imported Upstream version 1.1
aviau
8 years ago
1 | 1 | go: |
2 | 2 | - 1.2 |
3 | 3 | - 1.3 |
4 | - 1.4 | |
5 | - 1.5 | |
4 | 6 | - tip |
7 | go_import_path: gopkg.in/asn-ber.v1 | |
5 | 8 | install: |
6 | 9 | - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v |
7 | 10 | - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v |
8 | - go get code.google.com/p/go.tools/cmd/cover | |
11 | - go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover | |
9 | 12 | - go build -v ./... |
10 | 13 | script: |
11 | 14 | - go test -v -cover ./... |
15 | 15 | TODO: |
16 | 16 | Fix all encoding / decoding to conform to ASN1 BER spec |
17 | 17 | Implement Tests / Benchmarks |
18 | ||
19 | --- | |
20 | ||
21 | The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/) | |
22 | The design is licensed under the Creative Commons 3.0 Attributions license. | |
23 | Read this article for more details: http://blog.golang.org/gopher |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "bytes" |
4 | "errors" | |
4 | 5 | "fmt" |
5 | 6 | "io" |
6 | 7 | "os" |
8 | 9 | ) |
9 | 10 | |
10 | 11 | type Packet struct { |
11 | ClassType Class | |
12 | TagType Type | |
13 | Tag Tag | |
12 | Identifier | |
14 | 13 | Value interface{} |
15 | 14 | ByteValue []byte |
16 | 15 | Data *bytes.Buffer |
18 | 17 | Description string |
19 | 18 | } |
20 | 19 | |
21 | type Tag uint8 | |
20 | type Identifier struct { | |
21 | ClassType Class | |
22 | TagType Type | |
23 | Tag Tag | |
24 | } | |
25 | ||
26 | type Tag uint64 | |
22 | 27 | |
23 | 28 | const ( |
24 | 29 | TagEOC Tag = 0x00 |
51 | 56 | TagCharacterString Tag = 0x1d |
52 | 57 | TagBMPString Tag = 0x1e |
53 | 58 | TagBitmask Tag = 0x1f // xxx11111b |
59 | ||
60 | // HighTag indicates the start of a high-tag byte sequence | |
61 | HighTag Tag = 0x1f // xxx11111b | |
62 | // HighTagContinueBitmask indicates the high-tag byte sequence should continue | |
63 | HighTagContinueBitmask Tag = 0x80 // 10000000b | |
64 | // HighTagValueBitmask obtains the tag value from a high-tag byte sequence byte | |
65 | HighTagValueBitmask Tag = 0x7f // 01111111b | |
66 | ) | |
67 | ||
68 | const ( | |
69 | // LengthLongFormBitmask is the mask to apply to the length byte to see if a long-form byte sequence is used | |
70 | LengthLongFormBitmask = 0x80 | |
71 | // LengthValueBitmask is the mask to apply to the length byte to get the number of bytes in the long-form byte sequence | |
72 | LengthValueBitmask = 0x7f | |
73 | ||
74 | // LengthIndefinite is returned from readLength to indicate an indefinite length | |
75 | LengthIndefinite = -1 | |
54 | 76 | ) |
55 | 77 | |
56 | 78 | var tagMap = map[Tag]string{ |
171 | 193 | } |
172 | 194 | } |
173 | 195 | |
174 | func resizeBuffer(in []byte, new_size int) (out []byte) { | |
175 | out = make([]byte, new_size) | |
176 | ||
177 | copy(out, in) | |
178 | ||
179 | return | |
180 | } | |
181 | ||
196 | // ReadPacket reads a single Packet from the reader | |
182 | 197 | func ReadPacket(reader io.Reader) (*Packet, error) { |
183 | var header [2]byte | |
184 | buf := header[:] | |
185 | _, err := io.ReadFull(reader, buf) | |
186 | ||
198 | p, _, err := readPacket(reader) | |
187 | 199 | if err != nil { |
188 | 200 | return nil, err |
189 | 201 | } |
190 | ||
191 | idx := 2 | |
192 | var datalen int | |
193 | l := buf[1] | |
194 | ||
195 | if l&0x80 == 0 { | |
196 | // The length is encoded in the bottom 7 bits. | |
197 | datalen = int(l & 0x7f) | |
198 | if Debug { | |
199 | fmt.Printf("Read: datalen = %d len(buf) = %d\n ", l, len(buf)) | |
200 | ||
201 | for _, b := range buf { | |
202 | fmt.Printf("%02X ", b) | |
203 | } | |
204 | ||
205 | fmt.Printf("\n") | |
206 | } | |
207 | } else { | |
208 | // Bottom 7 bits give the number of length bytes to follow. | |
209 | numBytes := int(l & 0x7f) | |
210 | if numBytes == 0 { | |
211 | return nil, fmt.Errorf("invalid length found") | |
212 | } | |
213 | idx += numBytes | |
214 | buf = resizeBuffer(buf, 2+numBytes) | |
215 | _, err := io.ReadFull(reader, buf[2:]) | |
216 | ||
217 | if err != nil { | |
218 | return nil, err | |
219 | } | |
220 | datalen = 0 | |
221 | for i := 0; i < numBytes; i++ { | |
222 | b := buf[2+i] | |
223 | datalen <<= 8 | |
224 | datalen |= int(b) | |
225 | } | |
226 | ||
227 | if Debug { | |
228 | fmt.Printf("Read: datalen = %d numbytes=%d len(buf) = %d\n ", datalen, numBytes, len(buf)) | |
229 | ||
230 | for _, b := range buf { | |
231 | fmt.Printf("%02X ", b) | |
232 | } | |
233 | ||
234 | fmt.Printf("\n") | |
235 | } | |
236 | } | |
237 | ||
238 | buf = resizeBuffer(buf, idx+datalen) | |
239 | _, err = io.ReadFull(reader, buf[idx:]) | |
240 | ||
241 | if err != nil { | |
242 | return nil, err | |
243 | } | |
244 | ||
245 | if Debug { | |
246 | fmt.Printf("Read: len( buf ) = %d idx=%d datalen=%d idx+datalen=%d\n ", len(buf), idx, datalen, idx+datalen) | |
247 | ||
248 | for _, b := range buf { | |
249 | fmt.Printf("%02X ", b) | |
250 | } | |
251 | } | |
252 | ||
253 | p, _ := decodePacket(buf) | |
254 | ||
255 | 202 | return p, nil |
256 | 203 | } |
257 | 204 | |
305 | 252 | return |
306 | 253 | } |
307 | 254 | |
255 | // DecodePacket decodes the given bytes into a single Packet | |
256 | // If a decode error is encountered, nil is returned. | |
308 | 257 | func DecodePacket(data []byte) *Packet { |
309 | p, _ := decodePacket(data) | |
258 | p, _, _ := readPacket(bytes.NewBuffer(data)) | |
310 | 259 | |
311 | 260 | return p |
312 | 261 | } |
313 | 262 | |
314 | func decodePacket(data []byte) (*Packet, []byte) { | |
315 | if Debug { | |
316 | fmt.Printf("decodePacket: enter %d\n", len(data)) | |
317 | } | |
318 | ||
319 | p := new(Packet) | |
320 | ||
321 | p.ClassType = Class(data[0]) & ClassBitmask | |
322 | p.TagType = Type(data[0]) & TypeBitmask | |
323 | p.Tag = Tag(data[0]) & TagBitmask | |
324 | ||
325 | var datalen int | |
326 | l := data[1] | |
327 | datapos := 2 | |
328 | if l&0x80 == 0 { | |
329 | // The length is encoded in the bottom 7 bits. | |
330 | datalen = int(l & 0x7f) | |
331 | } else { | |
332 | // Bottom 7 bits give the number of length bytes to follow. | |
333 | numBytes := int(l & 0x7f) | |
334 | if numBytes == 0 { | |
335 | return nil, nil | |
336 | } | |
337 | datapos += numBytes | |
338 | datalen = 0 | |
339 | for i := 0; i < numBytes; i++ { | |
340 | b := data[2+i] | |
341 | datalen <<= 8 | |
342 | datalen |= int(b) | |
343 | } | |
263 | // DecodePacketErr decodes the given bytes into a single Packet | |
264 | // If a decode error is encountered, nil is returned | |
265 | func DecodePacketErr(data []byte) (*Packet, error) { | |
266 | p, _, err := readPacket(bytes.NewBuffer(data)) | |
267 | if err != nil { | |
268 | return nil, err | |
269 | } | |
270 | return p, nil | |
271 | } | |
272 | ||
273 | // readPacket reads a single Packet from the reader, returning the number of bytes read | |
274 | func readPacket(reader io.Reader) (*Packet, int, error) { | |
275 | identifier, length, read, err := readHeader(reader) | |
276 | if err != nil { | |
277 | return nil, read, err | |
278 | } | |
279 | ||
280 | p := &Packet{ | |
281 | Identifier: identifier, | |
344 | 282 | } |
345 | 283 | |
346 | 284 | p.Data = new(bytes.Buffer) |
347 | ||
348 | 285 | p.Children = make([]*Packet, 0, 2) |
349 | ||
350 | 286 | p.Value = nil |
351 | 287 | |
352 | value_data := data[datapos : datapos+datalen] | |
353 | ||
354 | 288 | if p.TagType == TypeConstructed { |
355 | for len(value_data) != 0 { | |
356 | var child *Packet | |
357 | ||
358 | child, value_data = decodePacket(value_data) | |
289 | // TODO: if universal, ensure tag type is allowed to be constructed | |
290 | ||
291 | // Track how much content we've read | |
292 | contentRead := 0 | |
293 | for { | |
294 | if length != LengthIndefinite { | |
295 | // End if we've read what we've been told to | |
296 | if contentRead == length { | |
297 | break | |
298 | } | |
299 | // Detect if a packet boundary didn't fall on the expected length | |
300 | if contentRead > length { | |
301 | return nil, read, fmt.Errorf("expected to read %d bytes, read %d", length, contentRead) | |
302 | } | |
303 | } | |
304 | ||
305 | // Read the next packet | |
306 | child, r, err := readPacket(reader) | |
307 | if err != nil { | |
308 | return nil, read, err | |
309 | } | |
310 | contentRead += r | |
311 | read += r | |
312 | ||
313 | // Test is this is the EOC marker for our packet | |
314 | if isEOCPacket(child) { | |
315 | if length == LengthIndefinite { | |
316 | break | |
317 | } | |
318 | return nil, read, errors.New("eoc child not allowed with definite length") | |
319 | } | |
320 | ||
321 | // Append and continue | |
359 | 322 | p.AppendChild(child) |
360 | 323 | } |
361 | } else if p.ClassType == ClassUniversal { | |
362 | p.Data.Write(data[datapos : datapos+datalen]) | |
363 | p.ByteValue = value_data | |
324 | return p, read, nil | |
325 | } | |
326 | ||
327 | if length == LengthIndefinite { | |
328 | return nil, read, errors.New("indefinite length used with primitive type") | |
329 | } | |
330 | ||
331 | // Read definite-length content | |
332 | content := make([]byte, length, length) | |
333 | if length > 0 { | |
334 | _, err := io.ReadFull(reader, content) | |
335 | if err != nil { | |
336 | if err == io.EOF { | |
337 | return nil, read, io.ErrUnexpectedEOF | |
338 | } | |
339 | return nil, read, err | |
340 | } | |
341 | read += length | |
342 | } | |
343 | ||
344 | if p.ClassType == ClassUniversal { | |
345 | p.Data.Write(content) | |
346 | p.ByteValue = content | |
364 | 347 | |
365 | 348 | switch p.Tag { |
366 | 349 | case TagEOC: |
367 | 350 | case TagBoolean: |
368 | val, _ := parseInt64(value_data) | |
351 | val, _ := parseInt64(content) | |
369 | 352 | |
370 | 353 | p.Value = val != 0 |
371 | 354 | case TagInteger: |
372 | p.Value, _ = parseInt64(value_data) | |
355 | p.Value, _ = parseInt64(content) | |
373 | 356 | case TagBitString: |
374 | 357 | case TagOctetString: |
375 | 358 | // the actual string encoding is not known here |
376 | // (e.g. for LDAP value_data is already an UTF8-encoded | |
359 | // (e.g. for LDAP content is already an UTF8-encoded | |
377 | 360 | // string). Return the data without further processing |
378 | p.Value = DecodeString(value_data) | |
361 | p.Value = DecodeString(content) | |
379 | 362 | case TagNULL: |
380 | 363 | case TagObjectIdentifier: |
381 | 364 | case TagObjectDescriptor: |
382 | 365 | case TagExternal: |
383 | 366 | case TagRealFloat: |
384 | 367 | case TagEnumerated: |
385 | p.Value, _ = parseInt64(value_data) | |
368 | p.Value, _ = parseInt64(content) | |
386 | 369 | case TagEmbeddedPDV: |
387 | 370 | case TagUTF8String: |
371 | p.Value = DecodeString(content) | |
388 | 372 | case TagRelativeOID: |
389 | 373 | case TagSequence: |
390 | 374 | case TagSet: |
391 | 375 | case TagNumericString: |
392 | 376 | case TagPrintableString: |
393 | p.Value = DecodeString(value_data) | |
377 | p.Value = DecodeString(content) | |
394 | 378 | case TagT61String: |
395 | 379 | case TagVideotexString: |
396 | 380 | case TagIA5String: |
404 | 388 | case TagBMPString: |
405 | 389 | } |
406 | 390 | } else { |
407 | p.Data.Write(data[datapos : datapos+datalen]) | |
408 | } | |
409 | ||
410 | return p, data[datapos+datalen:] | |
391 | p.Data.Write(content) | |
392 | } | |
393 | ||
394 | return p, read, nil | |
411 | 395 | } |
412 | 396 | |
413 | 397 | func (p *Packet) Bytes() []byte { |
414 | 398 | var out bytes.Buffer |
415 | 399 | |
416 | out.Write([]byte{byte(p.ClassType) | byte(p.TagType) | byte(p.Tag)}) | |
417 | packet_length := encodeInteger(int64(p.Data.Len())) | |
418 | ||
419 | if p.Data.Len() > 127 || len(packet_length) > 1 { | |
420 | out.Write([]byte{byte(len(packet_length) | 128)}) | |
421 | out.Write(packet_length) | |
422 | } else { | |
423 | out.Write(packet_length) | |
424 | } | |
425 | ||
400 | out.Write(encodeIdentifier(p.Identifier)) | |
401 | out.Write(encodeLength(p.Data.Len())) | |
426 | 402 | out.Write(p.Data.Bytes()) |
427 | 403 | |
428 | 404 | return out.Bytes() |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "bytes" |
4 | "math" | |
4 | 5 | |
5 | 6 | "io" |
6 | 7 | "testing" |
7 | 8 | ) |
8 | 9 | |
9 | 10 | func TestEncodeDecodeInteger(t *testing.T) { |
10 | for _, v := range []int64{0, 10, 128, 1024, -1, -100, -128, -1024} { | |
11 | for _, v := range []int64{0, 10, 128, 1024, math.MaxInt64, -1, -100, -128, -1024, math.MinInt64} { | |
11 | 12 | enc := encodeInteger(v) |
12 | 13 | dec, err := parseInt64(enc) |
13 | 14 | if err != nil { |
88 | 89 | |
89 | 90 | func TestSequenceAndAppendChild(t *testing.T) { |
90 | 91 | |
91 | p1 := NewString(ClassUniversal, TypePrimitive, TagOctetString, "HIC SVNT LEONES", "String") | |
92 | p2 := NewString(ClassUniversal, TypePrimitive, TagOctetString, "HIC SVNT DRACONES", "String") | |
93 | p3 := NewString(ClassUniversal, TypePrimitive, TagOctetString, "Terra Incognita", "String") | |
92 | values := []string{ | |
93 | "HIC SVNT LEONES", | |
94 | "Iñtërnâtiônàlizætiøn", | |
95 | "Terra Incognita", | |
96 | } | |
94 | 97 | |
95 | 98 | sequence := NewSequence("a sequence") |
96 | sequence.AppendChild(p1) | |
97 | sequence.AppendChild(p2) | |
98 | sequence.AppendChild(p3) | |
99 | for _, s := range values { | |
100 | sequence.AppendChild(NewString(ClassUniversal, TypePrimitive, TagOctetString, s, "String")) | |
101 | } | |
99 | 102 | |
100 | if len(sequence.Children) != 3 { | |
101 | t.Error("wrong length for children array should be three =>", len(sequence.Children)) | |
103 | if len(sequence.Children) != len(values) { | |
104 | t.Errorf("wrong length for children array should be %d, got %d", len(values), len(sequence.Children)) | |
102 | 105 | } |
103 | 106 | |
104 | 107 | encodedSequence := sequence.Bytes() |
105 | 108 | |
106 | 109 | decodedSequence := DecodePacket(encodedSequence) |
107 | if len(decodedSequence.Children) != 3 { | |
108 | t.Error("wrong length for children array should be three =>", len(decodedSequence.Children)) | |
110 | if len(decodedSequence.Children) != len(values) { | |
111 | t.Errorf("wrong length for children array should be %d => %d", len(values), len(decodedSequence.Children)) | |
109 | 112 | } |
110 | 113 | |
114 | for i, s := range values { | |
115 | if decodedSequence.Children[i].Value.(string) != s { | |
116 | t.Errorf("expected %d to be %q, got %q", i, s, decodedSequence.Children[i].Value.(string)) | |
117 | } | |
118 | } | |
111 | 119 | } |
112 | 120 | |
113 | 121 | func TestReadPacket(t *testing.T) { |
139 | 147 | {v: 256, e: []byte{0x02, 0x02, 0x01, 0x00}}, |
140 | 148 | {v: -128, e: []byte{0x02, 0x01, 0x80}}, |
141 | 149 | {v: -129, e: []byte{0x02, 0x02, 0xFF, 0x7F}}, |
150 | {v: math.MaxInt64, e: []byte{0x02, 0x08, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, | |
151 | {v: math.MinInt64, e: []byte{0x02, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, | |
142 | 152 | } |
143 | 153 | |
144 | 154 | for _, d := range data { |
0 | package ber | |
1 | ||
2 | func encodeUnsignedInteger(i uint64) []byte { | |
3 | n := uint64Length(i) | |
4 | out := make([]byte, n) | |
5 | ||
6 | var j int | |
7 | for ; n > 0; n-- { | |
8 | out[j] = (byte(i >> uint((n-1)*8))) | |
9 | j++ | |
10 | } | |
11 | ||
12 | return out | |
13 | } | |
14 | ||
15 | func uint64Length(i uint64) (numBytes int) { | |
16 | numBytes = 1 | |
17 | ||
18 | for i > 255 { | |
19 | numBytes++ | |
20 | i >>= 8 | |
21 | } | |
22 | ||
23 | return | |
24 | } |
0 | package ber | |
1 | ||
2 | import ( | |
3 | "errors" | |
4 | "io" | |
5 | ) | |
6 | ||
7 | func readHeader(reader io.Reader) (identifier Identifier, length int, read int, err error) { | |
8 | if i, c, err := readIdentifier(reader); err != nil { | |
9 | return Identifier{}, 0, read, err | |
10 | } else { | |
11 | identifier = i | |
12 | read += c | |
13 | } | |
14 | ||
15 | if l, c, err := readLength(reader); err != nil { | |
16 | return Identifier{}, 0, read, err | |
17 | } else { | |
18 | length = l | |
19 | read += c | |
20 | } | |
21 | ||
22 | // Validate length type with identifier (x.600, 8.1.3.2.a) | |
23 | if length == LengthIndefinite && identifier.TagType == TypePrimitive { | |
24 | return Identifier{}, 0, read, errors.New("indefinite length used with primitive type") | |
25 | } | |
26 | ||
27 | return identifier, length, read, nil | |
28 | } |
0 | package ber | |
1 | ||
2 | import ( | |
3 | "bytes" | |
4 | "io" | |
5 | "testing" | |
6 | ) | |
7 | ||
8 | func TestReadHeader(t *testing.T) { | |
9 | testcases := map[string]struct { | |
10 | Data []byte | |
11 | ExpectedIdentifier Identifier | |
12 | ExpectedLength int | |
13 | ExpectedBytesRead int | |
14 | ExpectedError string | |
15 | }{ | |
16 | "empty": { | |
17 | Data: []byte{}, | |
18 | ExpectedIdentifier: Identifier{}, | |
19 | ExpectedLength: 0, | |
20 | ExpectedBytesRead: 0, | |
21 | ExpectedError: io.ErrUnexpectedEOF.Error(), | |
22 | }, | |
23 | ||
24 | "valid short form": { | |
25 | Data: []byte{ | |
26 | byte(ClassUniversal) | byte(TypePrimitive) | byte(TagCharacterString), | |
27 | 127, | |
28 | }, | |
29 | ExpectedIdentifier: Identifier{ | |
30 | ClassType: ClassUniversal, | |
31 | TagType: TypePrimitive, | |
32 | Tag: TagCharacterString, | |
33 | }, | |
34 | ExpectedLength: 127, | |
35 | ExpectedBytesRead: 2, | |
36 | ExpectedError: "", | |
37 | }, | |
38 | ||
39 | "valid long form": { | |
40 | Data: []byte{ | |
41 | // 2-byte encoding of tag | |
42 | byte(ClassUniversal) | byte(TypePrimitive) | byte(HighTag), | |
43 | byte(TagCharacterString), | |
44 | ||
45 | // 2-byte encoding of length | |
46 | LengthLongFormBitmask | 1, | |
47 | 127, | |
48 | }, | |
49 | ExpectedIdentifier: Identifier{ | |
50 | ClassType: ClassUniversal, | |
51 | TagType: TypePrimitive, | |
52 | Tag: TagCharacterString, | |
53 | }, | |
54 | ExpectedLength: 127, | |
55 | ExpectedBytesRead: 4, | |
56 | ExpectedError: "", | |
57 | }, | |
58 | ||
59 | "valid indefinite length": { | |
60 | Data: []byte{ | |
61 | byte(ClassUniversal) | byte(TypeConstructed) | byte(TagCharacterString), | |
62 | LengthLongFormBitmask, | |
63 | }, | |
64 | ExpectedIdentifier: Identifier{ | |
65 | ClassType: ClassUniversal, | |
66 | TagType: TypeConstructed, | |
67 | Tag: TagCharacterString, | |
68 | }, | |
69 | ExpectedLength: LengthIndefinite, | |
70 | ExpectedBytesRead: 2, | |
71 | ExpectedError: "", | |
72 | }, | |
73 | ||
74 | "invalid indefinite length": { | |
75 | Data: []byte{ | |
76 | byte(ClassUniversal) | byte(TypePrimitive) | byte(TagCharacterString), | |
77 | LengthLongFormBitmask, | |
78 | }, | |
79 | ExpectedIdentifier: Identifier{}, | |
80 | ExpectedLength: 0, | |
81 | ExpectedBytesRead: 2, | |
82 | ExpectedError: "indefinite length used with primitive type", | |
83 | }, | |
84 | } | |
85 | ||
86 | for k, tc := range testcases { | |
87 | reader := bytes.NewBuffer(tc.Data) | |
88 | identifier, length, read, err := readHeader(reader) | |
89 | ||
90 | if err != nil { | |
91 | if tc.ExpectedError == "" { | |
92 | t.Errorf("%s: unexpected error: %v", k, err) | |
93 | } else if err.Error() != tc.ExpectedError { | |
94 | t.Errorf("%s: expected error %v, got %v", k, tc.ExpectedError, err) | |
95 | } | |
96 | } else if tc.ExpectedError != "" { | |
97 | t.Errorf("%s: expected error %v, got none", k, tc.ExpectedError) | |
98 | continue | |
99 | } | |
100 | ||
101 | if read != tc.ExpectedBytesRead { | |
102 | t.Errorf("%s: expected read %d, got %d", k, tc.ExpectedBytesRead, read) | |
103 | } | |
104 | ||
105 | if identifier.ClassType != tc.ExpectedIdentifier.ClassType { | |
106 | t.Errorf("%s: expected class type %d (%s), got %d (%s)", k, | |
107 | tc.ExpectedIdentifier.ClassType, | |
108 | ClassMap[tc.ExpectedIdentifier.ClassType], | |
109 | identifier.ClassType, | |
110 | ClassMap[identifier.ClassType], | |
111 | ) | |
112 | } | |
113 | if identifier.TagType != tc.ExpectedIdentifier.TagType { | |
114 | t.Errorf("%s: expected tag type %d (%s), got %d (%s)", k, | |
115 | tc.ExpectedIdentifier.TagType, | |
116 | TypeMap[tc.ExpectedIdentifier.TagType], | |
117 | identifier.TagType, | |
118 | TypeMap[identifier.TagType], | |
119 | ) | |
120 | } | |
121 | if identifier.Tag != tc.ExpectedIdentifier.Tag { | |
122 | t.Errorf("%s: expected tag %d (%s), got %d (%s)", k, | |
123 | tc.ExpectedIdentifier.Tag, | |
124 | tagMap[tc.ExpectedIdentifier.Tag], | |
125 | identifier.Tag, | |
126 | tagMap[identifier.Tag], | |
127 | ) | |
128 | } | |
129 | ||
130 | if length != tc.ExpectedLength { | |
131 | t.Errorf("%s: expected length %d, got %d", k, tc.ExpectedLength, length) | |
132 | } | |
133 | } | |
134 | } |
0 | package ber | |
1 | ||
2 | import ( | |
3 | "errors" | |
4 | "fmt" | |
5 | "io" | |
6 | "math" | |
7 | ) | |
8 | ||
9 | func readIdentifier(reader io.Reader) (Identifier, int, error) { | |
10 | identifier := Identifier{} | |
11 | read := 0 | |
12 | ||
13 | // identifier byte | |
14 | b, err := readByte(reader) | |
15 | if err != nil { | |
16 | if Debug { | |
17 | fmt.Printf("error reading identifier byte: %v\n", err) | |
18 | } | |
19 | return Identifier{}, read, err | |
20 | } | |
21 | read++ | |
22 | ||
23 | identifier.ClassType = Class(b) & ClassBitmask | |
24 | identifier.TagType = Type(b) & TypeBitmask | |
25 | ||
26 | if tag := Tag(b) & TagBitmask; tag != HighTag { | |
27 | // short-form tag | |
28 | identifier.Tag = tag | |
29 | return identifier, read, nil | |
30 | } | |
31 | ||
32 | // high-tag-number tag | |
33 | tagBytes := 0 | |
34 | for { | |
35 | b, err := readByte(reader) | |
36 | if err != nil { | |
37 | if Debug { | |
38 | fmt.Printf("error reading high-tag-number tag byte %d: %v\n", tagBytes, err) | |
39 | } | |
40 | return Identifier{}, read, err | |
41 | } | |
42 | tagBytes++ | |
43 | read++ | |
44 | ||
45 | // Lowest 7 bits get appended to the tag value (x.690, 8.1.2.4.2.b) | |
46 | identifier.Tag <<= 7 | |
47 | identifier.Tag |= Tag(b) & HighTagValueBitmask | |
48 | ||
49 | // First byte may not be all zeros (x.690, 8.1.2.4.2.c) | |
50 | if tagBytes == 1 && identifier.Tag == 0 { | |
51 | return Identifier{}, read, errors.New("invalid first high-tag-number tag byte") | |
52 | } | |
53 | // Overflow of int64 | |
54 | // TODO: support big int tags? | |
55 | if tagBytes > 9 { | |
56 | return Identifier{}, read, errors.New("high-tag-number tag overflow") | |
57 | } | |
58 | ||
59 | // Top bit of 0 means this is the last byte in the high-tag-number tag (x.690, 8.1.2.4.2.a) | |
60 | if Tag(b)&HighTagContinueBitmask == 0 { | |
61 | break | |
62 | } | |
63 | } | |
64 | ||
65 | return identifier, read, nil | |
66 | } | |
67 | ||
68 | func encodeIdentifier(identifier Identifier) []byte { | |
69 | b := []byte{0x0} | |
70 | b[0] |= byte(identifier.ClassType) | |
71 | b[0] |= byte(identifier.TagType) | |
72 | ||
73 | if identifier.Tag < HighTag { | |
74 | // Short-form | |
75 | b[0] |= byte(identifier.Tag) | |
76 | } else { | |
77 | // high-tag-number | |
78 | b[0] |= byte(HighTag) | |
79 | ||
80 | tag := identifier.Tag | |
81 | ||
82 | highBit := uint(63) | |
83 | for { | |
84 | if tag&(1<<highBit) != 0 { | |
85 | break | |
86 | } | |
87 | highBit-- | |
88 | } | |
89 | ||
90 | tagBytes := int(math.Ceil(float64(highBit) / 7.0)) | |
91 | for i := tagBytes - 1; i >= 0; i-- { | |
92 | offset := uint(i) * 7 | |
93 | mask := Tag(0x7f) << offset | |
94 | tagByte := (tag & mask) >> offset | |
95 | if i != 0 { | |
96 | tagByte |= 0x80 | |
97 | } | |
98 | b = append(b, byte(tagByte)) | |
99 | } | |
100 | } | |
101 | return b | |
102 | } |
0 | package ber | |
1 | ||
2 | import ( | |
3 | "bytes" | |
4 | "io" | |
5 | "math" | |
6 | "testing" | |
7 | ) | |
8 | ||
9 | func TestReadIdentifier(t *testing.T) { | |
10 | testcases := map[string]struct { | |
11 | Data []byte | |
12 | ||
13 | ExpectedIdentifier Identifier | |
14 | ExpectedBytesRead int | |
15 | ExpectedError string | |
16 | }{ | |
17 | "empty": { | |
18 | Data: []byte{}, | |
19 | ExpectedBytesRead: 0, | |
20 | ExpectedError: io.ErrUnexpectedEOF.Error(), | |
21 | }, | |
22 | ||
23 | "universal primitive eoc": { | |
24 | Data: []byte{byte(ClassUniversal) | byte(TypePrimitive) | byte(TagEOC)}, | |
25 | ExpectedIdentifier: Identifier{ | |
26 | ClassType: ClassUniversal, | |
27 | TagType: TypePrimitive, | |
28 | Tag: TagEOC, | |
29 | }, | |
30 | ExpectedBytesRead: 1, | |
31 | }, | |
32 | "universal primitive character string": { | |
33 | Data: []byte{byte(ClassUniversal) | byte(TypePrimitive) | byte(TagCharacterString)}, | |
34 | ExpectedIdentifier: Identifier{ | |
35 | ClassType: ClassUniversal, | |
36 | TagType: TypePrimitive, | |
37 | Tag: TagCharacterString, | |
38 | }, | |
39 | ExpectedBytesRead: 1, | |
40 | }, | |
41 | ||
42 | "universal constructed bit string": { | |
43 | Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(TagBitString)}, | |
44 | ExpectedIdentifier: Identifier{ | |
45 | ClassType: ClassUniversal, | |
46 | TagType: TypeConstructed, | |
47 | Tag: TagBitString, | |
48 | }, | |
49 | ExpectedBytesRead: 1, | |
50 | }, | |
51 | "universal constructed character string": { | |
52 | Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(TagCharacterString)}, | |
53 | ExpectedIdentifier: Identifier{ | |
54 | ClassType: ClassUniversal, | |
55 | TagType: TypeConstructed, | |
56 | Tag: TagCharacterString, | |
57 | }, | |
58 | ExpectedBytesRead: 1, | |
59 | }, | |
60 | ||
61 | "application constructed object descriptor": { | |
62 | Data: []byte{byte(ClassApplication) | byte(TypeConstructed) | byte(TagObjectDescriptor)}, | |
63 | ExpectedIdentifier: Identifier{ | |
64 | ClassType: ClassApplication, | |
65 | TagType: TypeConstructed, | |
66 | Tag: TagObjectDescriptor, | |
67 | }, | |
68 | ExpectedBytesRead: 1, | |
69 | }, | |
70 | "context constructed object descriptor": { | |
71 | Data: []byte{byte(ClassContext) | byte(TypeConstructed) | byte(TagObjectDescriptor)}, | |
72 | ExpectedIdentifier: Identifier{ | |
73 | ClassType: ClassContext, | |
74 | TagType: TypeConstructed, | |
75 | Tag: TagObjectDescriptor, | |
76 | }, | |
77 | ExpectedBytesRead: 1, | |
78 | }, | |
79 | "private constructed object descriptor": { | |
80 | Data: []byte{byte(ClassPrivate) | byte(TypeConstructed) | byte(TagObjectDescriptor)}, | |
81 | ExpectedIdentifier: Identifier{ | |
82 | ClassType: ClassPrivate, | |
83 | TagType: TypeConstructed, | |
84 | Tag: TagObjectDescriptor, | |
85 | }, | |
86 | ExpectedBytesRead: 1, | |
87 | }, | |
88 | ||
89 | "high-tag-number tag missing bytes": { | |
90 | Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag)}, | |
91 | ExpectedError: io.ErrUnexpectedEOF.Error(), | |
92 | ExpectedBytesRead: 1, | |
93 | }, | |
94 | "high-tag-number tag invalid first byte": { | |
95 | Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), 0x0}, | |
96 | ExpectedError: "invalid first high-tag-number tag byte", | |
97 | ExpectedBytesRead: 2, | |
98 | }, | |
99 | "high-tag-number tag invalid first byte with continue bit": { | |
100 | Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), byte(HighTagContinueBitmask)}, | |
101 | ExpectedError: "invalid first high-tag-number tag byte", | |
102 | ExpectedBytesRead: 2, | |
103 | }, | |
104 | "high-tag-number tag continuation missing bytes": { | |
105 | Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), byte(HighTagContinueBitmask | 0x1)}, | |
106 | ExpectedError: io.ErrUnexpectedEOF.Error(), | |
107 | ExpectedBytesRead: 2, | |
108 | }, | |
109 | "high-tag-number tag overflow": { | |
110 | Data: []byte{ | |
111 | byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), | |
112 | byte(HighTagContinueBitmask | 0x1), | |
113 | byte(HighTagContinueBitmask | 0x1), | |
114 | byte(HighTagContinueBitmask | 0x1), | |
115 | byte(HighTagContinueBitmask | 0x1), | |
116 | byte(HighTagContinueBitmask | 0x1), | |
117 | byte(HighTagContinueBitmask | 0x1), | |
118 | byte(HighTagContinueBitmask | 0x1), | |
119 | byte(HighTagContinueBitmask | 0x1), | |
120 | byte(HighTagContinueBitmask | 0x1), | |
121 | byte(0x1), | |
122 | }, | |
123 | ExpectedError: "high-tag-number tag overflow", | |
124 | ExpectedBytesRead: 11, | |
125 | }, | |
126 | "max high-tag-number tag": { | |
127 | Data: []byte{ | |
128 | byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), | |
129 | byte(HighTagContinueBitmask | 0x7f), | |
130 | byte(HighTagContinueBitmask | 0x7f), | |
131 | byte(HighTagContinueBitmask | 0x7f), | |
132 | byte(HighTagContinueBitmask | 0x7f), | |
133 | byte(HighTagContinueBitmask | 0x7f), | |
134 | byte(HighTagContinueBitmask | 0x7f), | |
135 | byte(HighTagContinueBitmask | 0x7f), | |
136 | byte(HighTagContinueBitmask | 0x7f), | |
137 | byte(0x7f), | |
138 | }, | |
139 | ExpectedIdentifier: Identifier{ | |
140 | ClassType: ClassUniversal, | |
141 | TagType: TypeConstructed, | |
142 | Tag: Tag(0x7FFFFFFFFFFFFFFF), // 01111111...(63)...11111b | |
143 | }, | |
144 | ExpectedBytesRead: 10, | |
145 | }, | |
146 | "high-tag-number encoding of low-tag value": { | |
147 | Data: []byte{ | |
148 | byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), | |
149 | byte(TagObjectDescriptor), | |
150 | }, | |
151 | ExpectedIdentifier: Identifier{ | |
152 | ClassType: ClassUniversal, | |
153 | TagType: TypeConstructed, | |
154 | Tag: TagObjectDescriptor, | |
155 | }, | |
156 | ExpectedBytesRead: 2, | |
157 | }, | |
158 | "max high-tag-number tag ignores extra data": { | |
159 | Data: []byte{ | |
160 | byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), | |
161 | byte(HighTagContinueBitmask | 0x7f), | |
162 | byte(HighTagContinueBitmask | 0x7f), | |
163 | byte(HighTagContinueBitmask | 0x7f), | |
164 | byte(HighTagContinueBitmask | 0x7f), | |
165 | byte(HighTagContinueBitmask | 0x7f), | |
166 | byte(HighTagContinueBitmask | 0x7f), | |
167 | byte(HighTagContinueBitmask | 0x7f), | |
168 | byte(HighTagContinueBitmask | 0x7f), | |
169 | byte(0x7f), | |
170 | byte(0x01), // extra data, shouldn't be read | |
171 | byte(0x02), // extra data, shouldn't be read | |
172 | byte(0x03), // extra data, shouldn't be read | |
173 | }, | |
174 | ExpectedIdentifier: Identifier{ | |
175 | ClassType: ClassUniversal, | |
176 | TagType: TypeConstructed, | |
177 | Tag: Tag(0x7FFFFFFFFFFFFFFF), // 01111111...(63)...11111b | |
178 | }, | |
179 | ExpectedBytesRead: 10, | |
180 | }, | |
181 | } | |
182 | ||
183 | for k, tc := range testcases { | |
184 | reader := bytes.NewBuffer(tc.Data) | |
185 | identifier, read, err := readIdentifier(reader) | |
186 | ||
187 | if err != nil { | |
188 | if tc.ExpectedError == "" { | |
189 | t.Errorf("%s: unexpected error: %v", k, err) | |
190 | } else if err.Error() != tc.ExpectedError { | |
191 | t.Errorf("%s: expected error %v, got %v", k, tc.ExpectedError, err) | |
192 | } | |
193 | } else if tc.ExpectedError != "" { | |
194 | t.Errorf("%s: expected error %v, got none", k, tc.ExpectedError) | |
195 | continue | |
196 | } | |
197 | ||
198 | if read != tc.ExpectedBytesRead { | |
199 | t.Errorf("%s: expected read %d, got %d", k, tc.ExpectedBytesRead, read) | |
200 | } | |
201 | ||
202 | if identifier.ClassType != tc.ExpectedIdentifier.ClassType { | |
203 | t.Errorf("%s: expected class type %d (%s), got %d (%s)", k, | |
204 | tc.ExpectedIdentifier.ClassType, | |
205 | ClassMap[tc.ExpectedIdentifier.ClassType], | |
206 | identifier.ClassType, | |
207 | ClassMap[identifier.ClassType], | |
208 | ) | |
209 | } | |
210 | if identifier.TagType != tc.ExpectedIdentifier.TagType { | |
211 | t.Errorf("%s: expected tag type %d (%s), got %d (%s)", k, | |
212 | tc.ExpectedIdentifier.TagType, | |
213 | TypeMap[tc.ExpectedIdentifier.TagType], | |
214 | identifier.TagType, | |
215 | TypeMap[identifier.TagType], | |
216 | ) | |
217 | } | |
218 | if identifier.Tag != tc.ExpectedIdentifier.Tag { | |
219 | t.Errorf("%s: expected tag %d (%s), got %d (%s)", k, | |
220 | tc.ExpectedIdentifier.Tag, | |
221 | tagMap[tc.ExpectedIdentifier.Tag], | |
222 | identifier.Tag, | |
223 | tagMap[identifier.Tag], | |
224 | ) | |
225 | } | |
226 | } | |
227 | } | |
228 | ||
229 | func TestEncodeIdentifier(t *testing.T) { | |
230 | testcases := map[string]struct { | |
231 | Identifier Identifier | |
232 | ExpectedBytes []byte | |
233 | }{ | |
234 | "universal primitive eoc": { | |
235 | Identifier: Identifier{ | |
236 | ClassType: ClassUniversal, | |
237 | TagType: TypePrimitive, | |
238 | Tag: TagEOC, | |
239 | }, | |
240 | ExpectedBytes: []byte{byte(ClassUniversal) | byte(TypePrimitive) | byte(TagEOC)}, | |
241 | }, | |
242 | "universal primitive character string": { | |
243 | Identifier: Identifier{ | |
244 | ClassType: ClassUniversal, | |
245 | TagType: TypePrimitive, | |
246 | Tag: TagCharacterString, | |
247 | }, | |
248 | ExpectedBytes: []byte{byte(ClassUniversal) | byte(TypePrimitive) | byte(TagCharacterString)}, | |
249 | }, | |
250 | ||
251 | "universal constructed bit string": { | |
252 | Identifier: Identifier{ | |
253 | ClassType: ClassUniversal, | |
254 | TagType: TypeConstructed, | |
255 | Tag: TagBitString, | |
256 | }, | |
257 | ExpectedBytes: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(TagBitString)}, | |
258 | }, | |
259 | "universal constructed character string": { | |
260 | Identifier: Identifier{ | |
261 | ClassType: ClassUniversal, | |
262 | TagType: TypeConstructed, | |
263 | Tag: TagCharacterString, | |
264 | }, | |
265 | ExpectedBytes: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(TagCharacterString)}, | |
266 | }, | |
267 | ||
268 | "application constructed object descriptor": { | |
269 | Identifier: Identifier{ | |
270 | ClassType: ClassApplication, | |
271 | TagType: TypeConstructed, | |
272 | Tag: TagObjectDescriptor, | |
273 | }, | |
274 | ExpectedBytes: []byte{byte(ClassApplication) | byte(TypeConstructed) | byte(TagObjectDescriptor)}, | |
275 | }, | |
276 | "context constructed object descriptor": { | |
277 | Identifier: Identifier{ | |
278 | ClassType: ClassContext, | |
279 | TagType: TypeConstructed, | |
280 | Tag: TagObjectDescriptor, | |
281 | }, | |
282 | ExpectedBytes: []byte{byte(ClassContext) | byte(TypeConstructed) | byte(TagObjectDescriptor)}, | |
283 | }, | |
284 | "private constructed object descriptor": { | |
285 | Identifier: Identifier{ | |
286 | ClassType: ClassPrivate, | |
287 | TagType: TypeConstructed, | |
288 | Tag: TagObjectDescriptor, | |
289 | }, | |
290 | ExpectedBytes: []byte{byte(ClassPrivate) | byte(TypeConstructed) | byte(TagObjectDescriptor)}, | |
291 | }, | |
292 | ||
293 | "max low-tag-number tag": { | |
294 | Identifier: Identifier{ | |
295 | ClassType: ClassUniversal, | |
296 | TagType: TypeConstructed, | |
297 | Tag: TagBMPString, | |
298 | }, | |
299 | ExpectedBytes: []byte{ | |
300 | byte(ClassUniversal) | byte(TypeConstructed) | byte(TagBMPString), | |
301 | }, | |
302 | }, | |
303 | ||
304 | "min high-tag-number tag": { | |
305 | Identifier: Identifier{ | |
306 | ClassType: ClassUniversal, | |
307 | TagType: TypeConstructed, | |
308 | Tag: TagBMPString + 1, | |
309 | }, | |
310 | ExpectedBytes: []byte{ | |
311 | byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), | |
312 | byte(TagBMPString + 1), | |
313 | }, | |
314 | }, | |
315 | ||
316 | "max high-tag-number tag": { | |
317 | Identifier: Identifier{ | |
318 | ClassType: ClassUniversal, | |
319 | TagType: TypeConstructed, | |
320 | Tag: Tag(math.MaxInt64), | |
321 | }, | |
322 | ExpectedBytes: []byte{ | |
323 | byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), | |
324 | byte(HighTagContinueBitmask | 0x7f), | |
325 | byte(HighTagContinueBitmask | 0x7f), | |
326 | byte(HighTagContinueBitmask | 0x7f), | |
327 | byte(HighTagContinueBitmask | 0x7f), | |
328 | byte(HighTagContinueBitmask | 0x7f), | |
329 | byte(HighTagContinueBitmask | 0x7f), | |
330 | byte(HighTagContinueBitmask | 0x7f), | |
331 | byte(HighTagContinueBitmask | 0x7f), | |
332 | byte(0x7f), | |
333 | }, | |
334 | }, | |
335 | } | |
336 | ||
337 | for k, tc := range testcases { | |
338 | b := encodeIdentifier(tc.Identifier) | |
339 | if bytes.Compare(tc.ExpectedBytes, b) != 0 { | |
340 | t.Errorf("%s: Expected\n\t%#v\ngot\n\t%#v", k, tc.ExpectedBytes, b) | |
341 | } | |
342 | } | |
343 | } |
0 | package ber | |
1 | ||
2 | import ( | |
3 | "errors" | |
4 | "fmt" | |
5 | "io" | |
6 | ) | |
7 | ||
8 | func readLength(reader io.Reader) (length int, read int, err error) { | |
9 | // length byte | |
10 | b, err := readByte(reader) | |
11 | if err != nil { | |
12 | if Debug { | |
13 | fmt.Printf("error reading length byte: %v\n", err) | |
14 | } | |
15 | return 0, 0, err | |
16 | } | |
17 | read++ | |
18 | ||
19 | switch { | |
20 | case b == 0xFF: | |
21 | // Invalid 0xFF (x.600, 8.1.3.5.c) | |
22 | return 0, read, errors.New("invalid length byte 0xff") | |
23 | ||
24 | case b == LengthLongFormBitmask: | |
25 | // Indefinite form, we have to decode packets until we encounter an EOC packet (x.600, 8.1.3.6) | |
26 | length = LengthIndefinite | |
27 | ||
28 | case b&LengthLongFormBitmask == 0: | |
29 | // Short definite form, extract the length from the bottom 7 bits (x.600, 8.1.3.4) | |
30 | length = int(b) & LengthValueBitmask | |
31 | ||
32 | case b&LengthLongFormBitmask != 0: | |
33 | // Long definite form, extract the number of length bytes to follow from the bottom 7 bits (x.600, 8.1.3.5.b) | |
34 | lengthBytes := int(b) & LengthValueBitmask | |
35 | // Protect against overflow | |
36 | // TODO: support big int length? | |
37 | if lengthBytes > 8 { | |
38 | return 0, read, errors.New("long-form length overflow") | |
39 | } | |
40 | for i := 0; i < lengthBytes; i++ { | |
41 | b, err = readByte(reader) | |
42 | if err != nil { | |
43 | if Debug { | |
44 | fmt.Printf("error reading long-form length byte %d: %v\n", i, err) | |
45 | } | |
46 | return 0, read, err | |
47 | } | |
48 | read++ | |
49 | ||
50 | // x.600, 8.1.3.5 | |
51 | length <<= 8 | |
52 | length |= int(b) | |
53 | } | |
54 | ||
55 | default: | |
56 | return 0, read, errors.New("invalid length byte") | |
57 | } | |
58 | ||
59 | return length, read, nil | |
60 | } | |
61 | ||
62 | func encodeLength(length int) []byte { | |
63 | length_bytes := encodeUnsignedInteger(uint64(length)) | |
64 | if length > 127 || len(length_bytes) > 1 { | |
65 | longFormBytes := []byte{(LengthLongFormBitmask | byte(len(length_bytes)))} | |
66 | longFormBytes = append(longFormBytes, length_bytes...) | |
67 | length_bytes = longFormBytes | |
68 | } | |
69 | return length_bytes | |
70 | } |
0 | package ber | |
1 | ||
2 | import ( | |
3 | "bytes" | |
4 | "io" | |
5 | "math" | |
6 | "testing" | |
7 | ) | |
8 | ||
9 | func TestReadLength(t *testing.T) { | |
10 | testcases := map[string]struct { | |
11 | Data []byte | |
12 | ||
13 | ExpectedLength int | |
14 | ExpectedBytesRead int | |
15 | ExpectedError string | |
16 | }{ | |
17 | "empty": { | |
18 | Data: []byte{}, | |
19 | ExpectedBytesRead: 0, | |
20 | ExpectedError: io.ErrUnexpectedEOF.Error(), | |
21 | }, | |
22 | "invalid first byte": { | |
23 | Data: []byte{0xFF}, | |
24 | ExpectedBytesRead: 1, | |
25 | ExpectedError: "invalid length byte 0xff", | |
26 | }, | |
27 | ||
28 | "indefinite form": { | |
29 | Data: []byte{LengthLongFormBitmask}, | |
30 | ExpectedLength: LengthIndefinite, | |
31 | ExpectedBytesRead: 1, | |
32 | }, | |
33 | ||
34 | "short-definite-form zero length": { | |
35 | Data: []byte{0}, | |
36 | ExpectedLength: 0, | |
37 | ExpectedBytesRead: 1, | |
38 | }, | |
39 | "short-definite-form length 1": { | |
40 | Data: []byte{1}, | |
41 | ExpectedLength: 1, | |
42 | ExpectedBytesRead: 1, | |
43 | }, | |
44 | "short-definite-form max length": { | |
45 | Data: []byte{127}, | |
46 | ExpectedLength: 127, | |
47 | ExpectedBytesRead: 1, | |
48 | }, | |
49 | ||
50 | "long-definite-form missing bytes": { | |
51 | Data: []byte{LengthLongFormBitmask | 1}, | |
52 | ExpectedBytesRead: 1, | |
53 | ExpectedError: io.ErrUnexpectedEOF.Error(), | |
54 | }, | |
55 | "long-definite-form overflow": { | |
56 | Data: []byte{LengthLongFormBitmask | 9}, | |
57 | ExpectedBytesRead: 1, | |
58 | ExpectedError: "long-form length overflow", | |
59 | }, | |
60 | "long-definite-form zero length": { | |
61 | Data: []byte{LengthLongFormBitmask | 1, 0x0}, | |
62 | ExpectedLength: 0, | |
63 | ExpectedBytesRead: 2, | |
64 | }, | |
65 | "long-definite-form length 127": { | |
66 | Data: []byte{LengthLongFormBitmask | 1, 127}, | |
67 | ExpectedLength: 127, | |
68 | ExpectedBytesRead: 2, | |
69 | }, | |
70 | "long-definite-form max length": { | |
71 | Data: []byte{ | |
72 | LengthLongFormBitmask | 8, | |
73 | 0x7F, | |
74 | 0xFF, | |
75 | 0xFF, | |
76 | 0xFF, | |
77 | 0xFF, | |
78 | 0xFF, | |
79 | 0xFF, | |
80 | 0xFF, | |
81 | }, | |
82 | ExpectedLength: math.MaxInt64, | |
83 | ExpectedBytesRead: 9, | |
84 | }, | |
85 | } | |
86 | ||
87 | for k, tc := range testcases { | |
88 | reader := bytes.NewBuffer(tc.Data) | |
89 | length, read, err := readLength(reader) | |
90 | ||
91 | if err != nil { | |
92 | if tc.ExpectedError == "" { | |
93 | t.Errorf("%s: unexpected error: %v", k, err) | |
94 | } else if err.Error() != tc.ExpectedError { | |
95 | t.Errorf("%s: expected error %v, got %v", k, tc.ExpectedError, err) | |
96 | } | |
97 | } else if tc.ExpectedError != "" { | |
98 | t.Errorf("%s: expected error %v, got none", k, tc.ExpectedError) | |
99 | continue | |
100 | } | |
101 | ||
102 | if read != tc.ExpectedBytesRead { | |
103 | t.Errorf("%s: expected read %d, got %d", k, tc.ExpectedBytesRead, read) | |
104 | } | |
105 | ||
106 | if length != tc.ExpectedLength { | |
107 | t.Errorf("%s: expected length %d, got %d", k, tc.ExpectedLength, length) | |
108 | } | |
109 | } | |
110 | } | |
111 | ||
112 | func TestEncodeLength(t *testing.T) { | |
113 | testcases := map[string]struct { | |
114 | Length int | |
115 | ExpectedBytes []byte | |
116 | }{ | |
117 | "0": { | |
118 | Length: 0, | |
119 | ExpectedBytes: []byte{0}, | |
120 | }, | |
121 | "1": { | |
122 | Length: 1, | |
123 | ExpectedBytes: []byte{1}, | |
124 | }, | |
125 | ||
126 | "max short-form length": { | |
127 | Length: 127, | |
128 | ExpectedBytes: []byte{127}, | |
129 | }, | |
130 | "min long-form length": { | |
131 | Length: 128, | |
132 | ExpectedBytes: []byte{LengthLongFormBitmask | 1, 128}, | |
133 | }, | |
134 | ||
135 | "max long-form length": { | |
136 | Length: math.MaxInt64, | |
137 | ExpectedBytes: []byte{ | |
138 | LengthLongFormBitmask | 8, | |
139 | 0x7F, | |
140 | 0xFF, | |
141 | 0xFF, | |
142 | 0xFF, | |
143 | 0xFF, | |
144 | 0xFF, | |
145 | 0xFF, | |
146 | 0xFF, | |
147 | }, | |
148 | }, | |
149 | } | |
150 | ||
151 | for k, tc := range testcases { | |
152 | b := encodeLength(tc.Length) | |
153 | if bytes.Compare(tc.ExpectedBytes, b) != 0 { | |
154 | t.Errorf("%s: Expected\n\t%#v\ngot\n\t%#v", k, tc.ExpectedBytes, b) | |
155 | } | |
156 | } | |
157 | } |
0 | package ber | |
1 | ||
2 | import ( | |
3 | "bytes" | |
4 | "io" | |
5 | "io/ioutil" | |
6 | "testing" | |
7 | ) | |
8 | ||
9 | var errEOF = io.ErrUnexpectedEOF.Error() | |
10 | ||
11 | // Tests from http://www.strozhevsky.com/free_docs/free_asn1_testsuite_descr.pdf | |
12 | // Source files and descriptions at http://www.strozhevsky.com/free_docs/TEST_SUITE.zip | |
13 | var testcases = []struct { | |
14 | // File contains the path to the BER-encoded file | |
15 | File string | |
16 | // Error indicates whether a decoding error is expected | |
17 | Error string | |
18 | // AbnormalEncoding indicates whether a normalized re-encoding is expected to differ from the original source | |
19 | AbnormalEncoding bool | |
20 | // IndefiniteEncoding indicates the source file used indefinite-length encoding, so the re-encoding is expected to differ (since the length is known) | |
21 | IndefiniteEncoding bool | |
22 | }{ | |
23 | // Common blocks | |
24 | {File: "tests/tc1.ber", Error: "high-tag-number tag overflow"}, | |
25 | {File: "tests/tc2.ber", Error: errEOF}, | |
26 | {File: "tests/tc3.ber", Error: errEOF}, | |
27 | {File: "tests/tc4.ber", Error: "invalid length byte 0xff"}, | |
28 | {File: "tests/tc5.ber", Error: "", AbnormalEncoding: true}, | |
29 | // Real numbers (some expected failures are disabled until support is added) | |
30 | {File: "tests/tc6.ber", Error: ""}, // Error: "REAL value +0 must be encoded with zero-length value block"}, | |
31 | {File: "tests/tc7.ber", Error: ""}, // Error: "REAL value -0 must be encoded as a special value"}, | |
32 | {File: "tests/tc8.ber", Error: ""}, | |
33 | {File: "tests/tc9.ber", Error: ""}, // Error: "Bits 6 and 5 of information octet for REAL are equal to 11" | |
34 | {File: "tests/tc10.ber", Error: ""}, | |
35 | {File: "tests/tc11.ber", Error: ""}, // Error: "Incorrect NR form" | |
36 | {File: "tests/tc12.ber", Error: ""}, // Error: "Encoding of "special value" not from ASN.1 standard" | |
37 | {File: "tests/tc13.ber", Error: errEOF}, | |
38 | {File: "tests/tc14.ber", Error: errEOF}, | |
39 | {File: "tests/tc15.ber", Error: ""}, // Error: "Too big value of exponent" | |
40 | {File: "tests/tc16.ber", Error: ""}, // Error: "Too big value of mantissa" | |
41 | {File: "tests/tc17.ber", Error: ""}, // Error: "Too big values for exponent and mantissa + using of "scaling factor" value" | |
42 | // Integers | |
43 | {File: "tests/tc18.ber", Error: ""}, | |
44 | {File: "tests/tc19.ber", Error: errEOF}, | |
45 | {File: "tests/tc20.ber", Error: ""}, | |
46 | // Object identifiers | |
47 | {File: "tests/tc21.ber", Error: ""}, | |
48 | {File: "tests/tc22.ber", Error: ""}, | |
49 | {File: "tests/tc23.ber", Error: errEOF}, | |
50 | {File: "tests/tc24.ber", Error: ""}, | |
51 | // Booleans | |
52 | {File: "tests/tc25.ber", Error: ""}, | |
53 | {File: "tests/tc26.ber", Error: ""}, | |
54 | {File: "tests/tc27.ber", Error: errEOF}, | |
55 | {File: "tests/tc28.ber", Error: ""}, | |
56 | {File: "tests/tc29.ber", Error: ""}, | |
57 | // Null | |
58 | {File: "tests/tc30.ber", Error: ""}, | |
59 | {File: "tests/tc31.ber", Error: errEOF}, | |
60 | {File: "tests/tc32.ber", Error: ""}, | |
61 | // Bitstring (some expected failures are disabled until support is added) | |
62 | {File: "tests/tc33.ber", Error: ""}, // Error: "Too big value for "unused bits"" | |
63 | {File: "tests/tc34.ber", Error: errEOF}, | |
64 | {File: "tests/tc35.ber", Error: "", IndefiniteEncoding: true}, // Error: "Using of different from BIT STRING types as internal types for constructive encoding" | |
65 | {File: "tests/tc36.ber", Error: "", IndefiniteEncoding: true}, // Error: "Using of "unused bits" in internal BIT STRINGs with constructive form of encoding" | |
66 | {File: "tests/tc37.ber", Error: ""}, | |
67 | {File: "tests/tc38.ber", Error: "", IndefiniteEncoding: true}, | |
68 | {File: "tests/tc39.ber", Error: ""}, | |
69 | {File: "tests/tc40.ber", Error: ""}, | |
70 | // Octet string (some expected failures are disabled until support is added) | |
71 | {File: "tests/tc41.ber", Error: "", IndefiniteEncoding: true}, // Error: "Using of different from OCTET STRING types as internal types for constructive encoding" | |
72 | {File: "tests/tc42.ber", Error: errEOF}, | |
73 | {File: "tests/tc43.ber", Error: errEOF}, | |
74 | {File: "tests/tc44.ber", Error: ""}, | |
75 | {File: "tests/tc45.ber", Error: ""}, | |
76 | // Bitstring | |
77 | {File: "tests/tc46.ber", Error: "indefinite length used with primitive type"}, | |
78 | {File: "tests/tc47.ber", Error: "eoc child not allowed with definite length"}, | |
79 | {File: "tests/tc48.ber", Error: "", IndefiniteEncoding: true}, // Error: "Using of more than 7 "unused bits" in BIT STRING with constrictive encoding form" | |
80 | } | |
81 | ||
82 | func TestSuiteDecodePacket(t *testing.T) { | |
83 | // Debug = true | |
84 | for _, tc := range testcases { | |
85 | file := tc.File | |
86 | ||
87 | dataIn, err := ioutil.ReadFile(file) | |
88 | if err != nil { | |
89 | t.Errorf("%s: %v", file, err) | |
90 | continue | |
91 | } | |
92 | ||
93 | // fmt.Printf("%s: decode %d\n", file, len(dataIn)) | |
94 | packet, err := DecodePacketErr(dataIn) | |
95 | if err != nil { | |
96 | if tc.Error == "" { | |
97 | t.Errorf("%s: unexpected error during DecodePacket: %v", file, err) | |
98 | } else if tc.Error != err.Error() { | |
99 | t.Errorf("%s: expected error %q during DecodePacket, got %q", file, tc.Error, err) | |
100 | } | |
101 | continue | |
102 | } | |
103 | if tc.Error != "" { | |
104 | t.Errorf("%s: expected error %q, got none", file, tc.Error) | |
105 | continue | |
106 | } | |
107 | ||
108 | dataOut := packet.Bytes() | |
109 | if tc.AbnormalEncoding || tc.IndefiniteEncoding { | |
110 | // Abnormal encodings and encodings that used indefinite length should re-encode differently | |
111 | if bytes.Equal(dataOut, dataIn) { | |
112 | t.Errorf("%s: data should have been re-encoded differently", file) | |
113 | } | |
114 | } else if !bytes.Equal(dataOut, dataIn) { | |
115 | // Make sure the serialized data matches the source | |
116 | t.Errorf("%s: data should be the same", file) | |
117 | } | |
118 | ||
119 | packet, err = DecodePacketErr(dataOut) | |
120 | if err != nil { | |
121 | t.Errorf("%s: unexpected error: %v", file, err) | |
122 | continue | |
123 | } | |
124 | ||
125 | // Make sure the re-serialized data matches our original serialization | |
126 | dataOut2 := packet.Bytes() | |
127 | if !bytes.Equal(dataOut, dataOut2) { | |
128 | t.Errorf("%s: data should be the same", file) | |
129 | } | |
130 | } | |
131 | } | |
132 | ||
133 | func TestSuiteReadPacket(t *testing.T) { | |
134 | for _, tc := range testcases { | |
135 | file := tc.File | |
136 | ||
137 | dataIn, err := ioutil.ReadFile(file) | |
138 | if err != nil { | |
139 | t.Errorf("%s: %v", file, err) | |
140 | continue | |
141 | } | |
142 | ||
143 | buffer := bytes.NewBuffer(dataIn) | |
144 | packet, err := ReadPacket(buffer) | |
145 | if err != nil { | |
146 | if tc.Error == "" { | |
147 | t.Errorf("%s: unexpected error during ReadPacket: %v", file, err) | |
148 | } else if tc.Error != err.Error() { | |
149 | t.Errorf("%s: expected error %q during ReadPacket, got %q", file, tc.Error, err) | |
150 | } | |
151 | continue | |
152 | } | |
153 | if tc.Error != "" { | |
154 | t.Errorf("%s: expected error %q, got none", file, tc.Error) | |
155 | continue | |
156 | } | |
157 | ||
158 | dataOut := packet.Bytes() | |
159 | if tc.AbnormalEncoding || tc.IndefiniteEncoding { | |
160 | // Abnormal encodings and encodings that used indefinite length should re-encode differently | |
161 | if bytes.Equal(dataOut, dataIn) { | |
162 | t.Errorf("%s: data should have been re-encoded differently", file) | |
163 | } | |
164 | } else if !bytes.Equal(dataOut, dataIn) { | |
165 | // Make sure the serialized data matches the source | |
166 | t.Errorf("%s: data should be the same", file) | |
167 | } | |
168 | ||
169 | packet, err = DecodePacketErr(dataOut) | |
170 | if err != nil { | |
171 | t.Errorf("%s: unexpected error: %v", file, err) | |
172 | continue | |
173 | } | |
174 | ||
175 | // Make sure the re-serialized data matches our original serialization | |
176 | dataOut2 := packet.Bytes() | |
177 | if !bytes.Equal(dataOut, dataOut2) { | |
178 | t.Errorf("%s: data should be the same", file) | |
179 | } | |
180 | } | |
181 | } |
0 | Ÿÿÿÿÿÿÿÿÿÿ@⏎ |
0 | ƒÿÿÿû⏎ |
0 | 015625⏎ |
0 | I⏎ |
Binary diff not shown
Binary diff not shown
0 | ƒ ÿÿÿÿÿÿÿû⏎ |
0 | €û⏎ |
0 | ¯ þÿÿÿÿÿÿÿÿ⏎ |
0 | ÿð⏎ |
0 | ⏎ |
0 | Ÿÿÿÿÿÿÿÿÿÿ⏎ |
Binary diff not shown
0 | €€Q€€⏎ |
0 | ÿÿÿÿÿÿÿÿÿÿ…⏎ |
0 | ÿÿÿÿÿ⏎ |
0 | Ξ`†H��O …ξεJ…δΏc‹Ϋ/⏎ |
Binary diff not shown
Binary diff not shown
0 | ⏎ |
0 | ÿ⏎ |
Binary diff not shown
0 | Ÿÿÿÿÿÿÿÿÿ⏎ |
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 | ⏎ |
0 | ⏎ |
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 | Ÿÿÿÿÿÿÿÿÿÿ⏎ |
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 | $⏎ |
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 | џяяяяяяяяЃ@⏎ |
0 | +0.E-5⏎ |
0 | -0.E-5⏎ |
Binary diff not shown
0 | ¼þ⏎ |
0 | package ber | |
1 | ||
2 | import "io" | |
3 | ||
4 | func readByte(reader io.Reader) (byte, error) { | |
5 | bytes := make([]byte, 1, 1) | |
6 | _, err := io.ReadFull(reader, bytes) | |
7 | if err != nil { | |
8 | if err == io.EOF { | |
9 | return 0, io.ErrUnexpectedEOF | |
10 | } | |
11 | return 0, err | |
12 | } | |
13 | return bytes[0], nil | |
14 | } | |
15 | ||
16 | func isEOCPacket(p *Packet) bool { | |
17 | return p != nil && | |
18 | p.Tag == TagEOC && | |
19 | p.ClassType == ClassUniversal && | |
20 | p.TagType == TypePrimitive && | |
21 | len(p.ByteValue) == 0 && | |
22 | len(p.Children) == 0 | |
23 | } |