New upstream version 1.5.1
Alexandre Viau
3 years ago
0 | 0 | language: go |
1 | ||
1 | 2 | go: |
2 | - 1.2 | |
3 | - 1.3 | |
4 | - 1.4 | |
5 | - 1.5 | |
6 | - 1.6 | |
7 | - 1.7 | |
8 | - 1.8 | |
9 | - tip | |
10 | go_import_path: gopkg.in/asn-ber.v1 | |
11 | install: | |
12 | - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v | |
13 | - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v | |
14 | - go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover | |
15 | - go build -v ./... | |
3 | - 1.2.x | |
4 | - 1.6.x | |
5 | - 1.9.x | |
6 | - 1.10.x | |
7 | - 1.11.x | |
8 | - 1.12.x | |
9 | - 1.14.x | |
10 | - tip | |
11 | ||
12 | os: | |
13 | - linux | |
14 | ||
15 | arch: | |
16 | - amd64 | |
17 | ||
18 | dist: xenial | |
19 | ||
20 | env: | |
21 | - GOARCH=amd64 | |
22 | ||
23 | jobs: | |
24 | include: | |
25 | - os: windows | |
26 | go: 1.14.x | |
27 | - os: osx | |
28 | go: 1.14.x | |
29 | - os: linux | |
30 | go: 1.14.x | |
31 | arch: arm64 | |
32 | - os: linux | |
33 | go: 1.14.x | |
34 | env: | |
35 | - GOARCH=386 | |
36 | ||
16 | 37 | script: |
17 | - go test -v -cover ./... | |
38 | - go test -v -cover ./... || go test -v ./... |
4 | 4 | "errors" |
5 | 5 | "fmt" |
6 | 6 | "io" |
7 | "math" | |
7 | 8 | "os" |
8 | 9 | "reflect" |
10 | "time" | |
11 | "unicode/utf8" | |
9 | 12 | ) |
13 | ||
14 | // MaxPacketLengthBytes specifies the maximum allowed packet size when calling ReadPacket or DecodePacket. Set to 0 for | |
15 | // no limit. | |
16 | var MaxPacketLengthBytes int64 = math.MaxInt32 | |
10 | 17 | |
11 | 18 | type Packet struct { |
12 | 19 | Identifier |
137 | 144 | TypeConstructed: "Constructed", |
138 | 145 | } |
139 | 146 | |
140 | var Debug bool = false | |
147 | var Debug = false | |
141 | 148 | |
142 | 149 | func PrintBytes(out io.Writer, buf []byte, indent string) { |
143 | data_lines := make([]string, (len(buf)/30)+1) | |
144 | num_lines := make([]string, (len(buf)/30)+1) | |
150 | dataLines := make([]string, (len(buf)/30)+1) | |
151 | numLines := make([]string, (len(buf)/30)+1) | |
145 | 152 | |
146 | 153 | for i, b := range buf { |
147 | data_lines[i/30] += fmt.Sprintf("%02x ", b) | |
148 | num_lines[i/30] += fmt.Sprintf("%02d ", (i+1)%100) | |
149 | } | |
150 | ||
151 | for i := 0; i < len(data_lines); i++ { | |
152 | out.Write([]byte(indent + data_lines[i] + "\n")) | |
153 | out.Write([]byte(indent + num_lines[i] + "\n\n")) | |
154 | } | |
154 | dataLines[i/30] += fmt.Sprintf("%02x ", b) | |
155 | numLines[i/30] += fmt.Sprintf("%02d ", (i+1)%100) | |
156 | } | |
157 | ||
158 | for i := 0; i < len(dataLines); i++ { | |
159 | _, _ = out.Write([]byte(indent + dataLines[i] + "\n")) | |
160 | _, _ = out.Write([]byte(indent + numLines[i] + "\n\n")) | |
161 | } | |
162 | } | |
163 | ||
164 | func WritePacket(out io.Writer, p *Packet) { | |
165 | printPacket(out, p, 0, false) | |
155 | 166 | } |
156 | 167 | |
157 | 168 | func PrintPacket(p *Packet) { |
159 | 170 | } |
160 | 171 | |
161 | 172 | func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) { |
162 | indent_str := "" | |
163 | ||
164 | for len(indent_str) != indent { | |
165 | indent_str += " " | |
166 | } | |
167 | ||
168 | class_str := ClassMap[p.ClassType] | |
169 | ||
170 | tagtype_str := TypeMap[p.TagType] | |
171 | ||
172 | tag_str := fmt.Sprintf("0x%02X", p.Tag) | |
173 | indentStr := "" | |
174 | ||
175 | for len(indentStr) != indent { | |
176 | indentStr += " " | |
177 | } | |
178 | ||
179 | classStr := ClassMap[p.ClassType] | |
180 | ||
181 | tagTypeStr := TypeMap[p.TagType] | |
182 | ||
183 | tagStr := fmt.Sprintf("0x%02X", p.Tag) | |
173 | 184 | |
174 | 185 | if p.ClassType == ClassUniversal { |
175 | tag_str = tagMap[p.Tag] | |
186 | tagStr = tagMap[p.Tag] | |
176 | 187 | } |
177 | 188 | |
178 | 189 | value := fmt.Sprint(p.Value) |
182 | 193 | description = p.Description + ": " |
183 | 194 | } |
184 | 195 | |
185 | fmt.Fprintf(out, "%s%s(%s, %s, %s) Len=%d %q\n", indent_str, description, class_str, tagtype_str, tag_str, p.Data.Len(), value) | |
196 | _, _ = fmt.Fprintf(out, "%s%s(%s, %s, %s) Len=%d %q\n", indentStr, description, classStr, tagTypeStr, tagStr, p.Data.Len(), value) | |
186 | 197 | |
187 | 198 | if printBytes { |
188 | PrintBytes(out, p.Bytes(), indent_str) | |
199 | PrintBytes(out, p.Bytes(), indentStr) | |
189 | 200 | } |
190 | 201 | |
191 | 202 | for _, child := range p.Children { |
193 | 204 | } |
194 | 205 | } |
195 | 206 | |
196 | // ReadPacket reads a single Packet from the reader | |
207 | // ReadPacket reads a single Packet from the reader. | |
197 | 208 | func ReadPacket(reader io.Reader) (*Packet, error) { |
198 | 209 | p, _, err := readPacket(reader) |
199 | 210 | if err != nil { |
206 | 217 | return string(data) |
207 | 218 | } |
208 | 219 | |
209 | func parseInt64(bytes []byte) (ret int64, err error) { | |
220 | func ParseInt64(bytes []byte) (ret int64, err error) { | |
210 | 221 | if len(bytes) > 8 { |
211 | 222 | // We'll overflow an int64 in this case. |
212 | 223 | err = fmt.Errorf("integer too large") |
229 | 240 | |
230 | 241 | var j int |
231 | 242 | for ; n > 0; n-- { |
232 | out[j] = (byte(i >> uint((n-1)*8))) | |
243 | out[j] = byte(i >> uint((n-1)*8)) | |
233 | 244 | j++ |
234 | 245 | } |
235 | 246 | |
261 | 272 | } |
262 | 273 | |
263 | 274 | // DecodePacketErr decodes the given bytes into a single Packet |
264 | // If a decode error is encountered, nil is returned | |
275 | // If a decode error is encountered, nil is returned. | |
265 | 276 | func DecodePacketErr(data []byte) (*Packet, error) { |
266 | 277 | p, _, err := readPacket(bytes.NewBuffer(data)) |
267 | 278 | if err != nil { |
270 | 281 | return p, nil |
271 | 282 | } |
272 | 283 | |
273 | // readPacket reads a single Packet from the reader, returning the number of bytes read | |
284 | // readPacket reads a single Packet from the reader, returning the number of bytes read. | |
274 | 285 | func readPacket(reader io.Reader) (*Packet, int, error) { |
275 | 286 | identifier, length, read, err := readHeader(reader) |
276 | 287 | if err != nil { |
329 | 340 | } |
330 | 341 | |
331 | 342 | // Read definite-length content |
332 | content := make([]byte, length, length) | |
343 | if MaxPacketLengthBytes > 0 && int64(length) > MaxPacketLengthBytes { | |
344 | return nil, read, fmt.Errorf("length %d greater than maximum %d", length, MaxPacketLengthBytes) | |
345 | } | |
346 | content := make([]byte, length) | |
333 | 347 | if length > 0 { |
334 | 348 | _, err := io.ReadFull(reader, content) |
335 | 349 | if err != nil { |
348 | 362 | switch p.Tag { |
349 | 363 | case TagEOC: |
350 | 364 | case TagBoolean: |
351 | val, _ := parseInt64(content) | |
365 | val, _ := ParseInt64(content) | |
352 | 366 | |
353 | 367 | p.Value = val != 0 |
354 | 368 | case TagInteger: |
355 | p.Value, _ = parseInt64(content) | |
369 | p.Value, _ = ParseInt64(content) | |
356 | 370 | case TagBitString: |
357 | 371 | case TagOctetString: |
358 | 372 | // the actual string encoding is not known here |
364 | 378 | case TagObjectDescriptor: |
365 | 379 | case TagExternal: |
366 | 380 | case TagRealFloat: |
381 | p.Value, err = ParseReal(content) | |
367 | 382 | case TagEnumerated: |
368 | p.Value, _ = parseInt64(content) | |
383 | p.Value, _ = ParseInt64(content) | |
369 | 384 | case TagEmbeddedPDV: |
370 | 385 | case TagUTF8String: |
371 | p.Value = DecodeString(content) | |
386 | val := DecodeString(content) | |
387 | if !utf8.Valid([]byte(val)) { | |
388 | err = errors.New("invalid UTF-8 string") | |
389 | } else { | |
390 | p.Value = val | |
391 | } | |
372 | 392 | case TagRelativeOID: |
373 | 393 | case TagSequence: |
374 | 394 | case TagSet: |
375 | 395 | case TagNumericString: |
376 | 396 | case TagPrintableString: |
377 | p.Value = DecodeString(content) | |
397 | val := DecodeString(content) | |
398 | if err = isPrintableString(val); err == nil { | |
399 | p.Value = val | |
400 | } | |
378 | 401 | case TagT61String: |
379 | 402 | case TagVideotexString: |
380 | 403 | case TagIA5String: |
404 | val := DecodeString(content) | |
405 | for i, c := range val { | |
406 | if c >= 0x7F { | |
407 | err = fmt.Errorf("invalid character for IA5String at pos %d: %c", i, c) | |
408 | break | |
409 | } | |
410 | } | |
411 | if err == nil { | |
412 | p.Value = val | |
413 | } | |
381 | 414 | case TagUTCTime: |
382 | 415 | case TagGeneralizedTime: |
416 | p.Value, err = ParseGeneralizedTime(content) | |
383 | 417 | case TagGraphicString: |
384 | 418 | case TagVisibleString: |
385 | 419 | case TagGeneralString: |
391 | 425 | p.Data.Write(content) |
392 | 426 | } |
393 | 427 | |
394 | return p, read, nil | |
428 | return p, read, err | |
429 | } | |
430 | ||
431 | func isPrintableString(val string) error { | |
432 | for i, c := range val { | |
433 | switch { | |
434 | case c >= 'a' && c <= 'z': | |
435 | case c >= 'A' && c <= 'Z': | |
436 | case c >= '0' && c <= '9': | |
437 | default: | |
438 | switch c { | |
439 | case '\'', '(', ')', '+', ',', '-', '.', '=', '/', ':', '?', ' ': | |
440 | default: | |
441 | return fmt.Errorf("invalid character in position %d", i) | |
442 | } | |
443 | } | |
444 | } | |
445 | return nil | |
395 | 446 | } |
396 | 447 | |
397 | 448 | func (p *Packet) Bytes() []byte { |
409 | 460 | p.Children = append(p.Children, child) |
410 | 461 | } |
411 | 462 | |
412 | func Encode(ClassType Class, TagType Type, Tag Tag, Value interface{}, Description string) *Packet { | |
463 | func Encode(classType Class, tagType Type, tag Tag, value interface{}, description string) *Packet { | |
413 | 464 | p := new(Packet) |
414 | 465 | |
415 | p.ClassType = ClassType | |
416 | p.TagType = TagType | |
417 | p.Tag = Tag | |
466 | p.ClassType = classType | |
467 | p.TagType = tagType | |
468 | p.Tag = tag | |
418 | 469 | p.Data = new(bytes.Buffer) |
419 | 470 | |
420 | 471 | p.Children = make([]*Packet, 0, 2) |
421 | 472 | |
422 | p.Value = Value | |
423 | p.Description = Description | |
424 | ||
425 | if Value != nil { | |
426 | v := reflect.ValueOf(Value) | |
427 | ||
428 | if ClassType == ClassUniversal { | |
429 | switch Tag { | |
473 | p.Value = value | |
474 | p.Description = description | |
475 | ||
476 | if value != nil { | |
477 | v := reflect.ValueOf(value) | |
478 | ||
479 | if classType == ClassUniversal { | |
480 | switch tag { | |
430 | 481 | case TagOctetString: |
431 | 482 | sv, ok := v.Interface().(string) |
432 | 483 | |
433 | 484 | if ok { |
434 | 485 | p.Data.Write([]byte(sv)) |
435 | 486 | } |
487 | case TagEnumerated: | |
488 | bv, ok := v.Interface().([]byte) | |
489 | if ok { | |
490 | p.Data.Write(bv) | |
491 | } | |
492 | case TagEmbeddedPDV: | |
493 | bv, ok := v.Interface().([]byte) | |
494 | if ok { | |
495 | p.Data.Write(bv) | |
496 | } | |
497 | } | |
498 | } else if classType == ClassContext { | |
499 | switch tag { | |
500 | case TagEnumerated: | |
501 | bv, ok := v.Interface().([]byte) | |
502 | if ok { | |
503 | p.Data.Write(bv) | |
504 | } | |
505 | case TagEmbeddedPDV: | |
506 | bv, ok := v.Interface().([]byte) | |
507 | if ok { | |
508 | p.Data.Write(bv) | |
509 | } | |
436 | 510 | } |
437 | 511 | } |
438 | 512 | } |
439 | ||
440 | return p | |
441 | } | |
442 | ||
443 | func NewSequence(Description string) *Packet { | |
444 | return Encode(ClassUniversal, TypeConstructed, TagSequence, nil, Description) | |
445 | } | |
446 | ||
447 | func NewBoolean(ClassType Class, TagType Type, Tag Tag, Value bool, Description string) *Packet { | |
513 | return p | |
514 | } | |
515 | ||
516 | func NewSequence(description string) *Packet { | |
517 | return Encode(ClassUniversal, TypeConstructed, TagSequence, nil, description) | |
518 | } | |
519 | ||
520 | func NewBoolean(classType Class, tagType Type, tag Tag, value bool, description string) *Packet { | |
448 | 521 | intValue := int64(0) |
449 | 522 | |
450 | if Value { | |
523 | if value { | |
451 | 524 | intValue = 1 |
452 | 525 | } |
453 | 526 | |
454 | p := Encode(ClassType, TagType, Tag, nil, Description) | |
455 | ||
456 | p.Value = Value | |
527 | p := Encode(classType, tagType, tag, nil, description) | |
528 | ||
529 | p.Value = value | |
457 | 530 | p.Data.Write(encodeInteger(intValue)) |
458 | 531 | |
459 | 532 | return p |
460 | 533 | } |
461 | 534 | |
462 | func NewInteger(ClassType Class, TagType Type, Tag Tag, Value interface{}, Description string) *Packet { | |
463 | p := Encode(ClassType, TagType, Tag, nil, Description) | |
464 | ||
465 | p.Value = Value | |
466 | switch v := Value.(type) { | |
535 | // NewLDAPBoolean returns a RFC 4511-compliant Boolean packet. | |
536 | func NewLDAPBoolean(classType Class, tagType Type, tag Tag, value bool, description string) *Packet { | |
537 | intValue := int64(0) | |
538 | ||
539 | if value { | |
540 | intValue = 255 | |
541 | } | |
542 | ||
543 | p := Encode(classType, tagType, tag, nil, description) | |
544 | ||
545 | p.Value = value | |
546 | p.Data.Write(encodeInteger(intValue)) | |
547 | ||
548 | return p | |
549 | } | |
550 | ||
551 | func NewInteger(classType Class, tagType Type, tag Tag, value interface{}, description string) *Packet { | |
552 | p := Encode(classType, tagType, tag, nil, description) | |
553 | ||
554 | p.Value = value | |
555 | switch v := value.(type) { | |
467 | 556 | case int: |
468 | 557 | p.Data.Write(encodeInteger(int64(v))) |
469 | 558 | case uint: |
493 | 582 | return p |
494 | 583 | } |
495 | 584 | |
496 | func NewString(ClassType Class, TagType Type, Tag Tag, Value, Description string) *Packet { | |
497 | p := Encode(ClassType, TagType, Tag, nil, Description) | |
498 | ||
499 | p.Value = Value | |
500 | p.Data.Write([]byte(Value)) | |
501 | ||
502 | return p | |
503 | } | |
585 | func NewString(classType Class, tagType Type, tag Tag, value, description string) *Packet { | |
586 | p := Encode(classType, tagType, tag, nil, description) | |
587 | ||
588 | p.Value = value | |
589 | p.Data.Write([]byte(value)) | |
590 | ||
591 | return p | |
592 | } | |
593 | ||
594 | func NewGeneralizedTime(classType Class, tagType Type, tag Tag, value time.Time, description string) *Packet { | |
595 | p := Encode(classType, tagType, tag, nil, description) | |
596 | var s string | |
597 | if value.Nanosecond() != 0 { | |
598 | s = value.Format(`20060102150405.000000000Z`) | |
599 | } else { | |
600 | s = value.Format(`20060102150405Z`) | |
601 | } | |
602 | p.Value = s | |
603 | p.Data.Write([]byte(s)) | |
604 | return p | |
605 | } | |
606 | ||
607 | func NewReal(classType Class, tagType Type, tag Tag, value interface{}, description string) *Packet { | |
608 | p := Encode(classType, tagType, tag, nil, description) | |
609 | ||
610 | switch v := value.(type) { | |
611 | case float64: | |
612 | p.Data.Write(encodeFloat(v)) | |
613 | case float32: | |
614 | p.Data.Write(encodeFloat(float64(v))) | |
615 | default: | |
616 | panic(fmt.Sprintf("Invalid type %T, expected float{64|32}", v)) | |
617 | } | |
618 | return p | |
619 | } |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "bytes" |
4 | "io" | |
4 | 5 | "math" |
5 | ||
6 | "io" | |
7 | 6 | "testing" |
8 | 7 | ) |
9 | 8 | |
10 | 9 | func TestEncodeDecodeInteger(t *testing.T) { |
11 | 10 | for _, v := range []int64{0, 10, 128, 1024, math.MaxInt64, -1, -100, -128, -1024, math.MinInt64} { |
12 | 11 | enc := encodeInteger(v) |
13 | dec, err := parseInt64(enc) | |
12 | dec, err := ParseInt64(enc) | |
14 | 13 | if err != nil { |
15 | 14 | t.Fatalf("Error decoding %d : %s", v, err) |
16 | 15 | } |
17 | 16 | if v != dec { |
18 | t.Error("TestEncodeDecodeInteger failed for %d (got %d)", v, dec) | |
17 | t.Errorf("TestEncodeDecodeInteger failed for %d (got %d)", v, dec) | |
19 | 18 | } |
20 | ||
21 | 19 | } |
22 | 20 | } |
23 | 21 | |
24 | 22 | func TestBoolean(t *testing.T) { |
25 | var value bool = true | |
26 | ||
27 | packet := NewBoolean(ClassUniversal, TypePrimitive, TagBoolean, value, "first Packet, True") | |
23 | packet := NewBoolean(ClassUniversal, TypePrimitive, TagBoolean, true, "first Packet, True") | |
28 | 24 | |
29 | 25 | newBoolean, ok := packet.Value.(bool) |
30 | if !ok || newBoolean != value { | |
26 | if !ok || newBoolean != true { | |
31 | 27 | t.Error("error during creating packet") |
32 | 28 | } |
33 | 29 | |
36 | 32 | newPacket := DecodePacket(encodedPacket) |
37 | 33 | |
38 | 34 | newBoolean, ok = newPacket.Value.(bool) |
39 | if !ok || newBoolean != value { | |
35 | if !ok || newBoolean != true { | |
40 | 36 | t.Error("error during decoding packet") |
41 | 37 | } |
38 | } | |
42 | 39 | |
40 | func TestLDAPBoolean(t *testing.T) { | |
41 | packet := NewLDAPBoolean(ClassUniversal, TypePrimitive, TagBoolean, true, "first Packet, True") | |
42 | ||
43 | newBoolean, ok := packet.Value.(bool) | |
44 | if !ok || newBoolean != true { | |
45 | t.Error("error during creating packet") | |
46 | } | |
47 | ||
48 | encodedPacket := packet.Bytes() | |
49 | ||
50 | newPacket := DecodePacket(encodedPacket) | |
51 | ||
52 | newBoolean, ok = newPacket.Value.(bool) | |
53 | if !ok || newBoolean != true { | |
54 | t.Error("error during decoding packet") | |
55 | } | |
43 | 56 | } |
44 | 57 | |
45 | 58 | func TestInteger(t *testing.T) { |
60 | 73 | |
61 | 74 | { |
62 | 75 | newInteger, ok := newPacket.Value.(int64) |
63 | if !ok || int64(newInteger) != value { | |
76 | if !ok || newInteger != value { | |
64 | 77 | t.Error("error decoding packet") |
65 | 78 | } |
66 | 79 | } |
67 | 80 | } |
68 | 81 | |
69 | 82 | func TestString(t *testing.T) { |
70 | var value string = "Hic sunt dracones" | |
83 | var value = "Hic sunt dracones" | |
71 | 84 | |
72 | 85 | packet := NewString(ClassUniversal, TypePrimitive, TagOctetString, value, "String") |
73 | 86 | |
84 | 97 | if !ok || newValue != value { |
85 | 98 | t.Error("error during decoding packet") |
86 | 99 | } |
87 | ||
88 | 100 | } |
89 | 101 | |
90 | 102 | func TestSequenceAndAppendChild(t *testing.T) { |
91 | ||
92 | 103 | values := []string{ |
93 | 104 | "HIC SVNT LEONES", |
94 | 105 | "Iñtërnâtiônàlizætiøn", |
120 | 131 | |
121 | 132 | func TestReadPacket(t *testing.T) { |
122 | 133 | packet := NewString(ClassUniversal, TypePrimitive, TagOctetString, "Ad impossibilia nemo tenetur", "string") |
123 | var buffer io.ReadWriter | |
124 | buffer = new(bytes.Buffer) | |
134 | var buffer io.ReadWriter = new(bytes.Buffer) | |
125 | 135 | |
126 | buffer.Write(packet.Bytes()) | |
136 | if _, err := buffer.Write(packet.Bytes()); err != nil { | |
137 | t.Error("error writing packet", err) | |
138 | } | |
127 | 139 | |
128 | 140 | newPacket, err := ReadPacket(buffer) |
129 | 141 | if err != nil { |
152 | 164 | } |
153 | 165 | |
154 | 166 | for _, d := range data { |
155 | if b := NewInteger(ClassUniversal, TypePrimitive, TagInteger, int64(d.v), "").Bytes(); !bytes.Equal(d.e, b) { | |
167 | if b := NewInteger(ClassUniversal, TypePrimitive, TagInteger, d.v, "").Bytes(); !bytes.Equal(d.e, b) { | |
156 | 168 | t.Errorf("Wrong binary generated for %d : got % X, expected % X", d.v, b, d.e) |
157 | 169 | } |
158 | 170 | } |
5 | 5 | |
6 | 6 | var j int |
7 | 7 | for ; n > 0; n-- { |
8 | out[j] = (byte(i >> uint((n-1)*8))) | |
8 | out[j] = byte(i >> uint((n-1)*8)) | |
9 | 9 | j++ |
10 | 10 | } |
11 | 11 |
0 | package ber | |
1 | ||
2 | import ( | |
3 | "bytes" | |
4 | "errors" | |
5 | "fmt" | |
6 | "strconv" | |
7 | "time" | |
8 | ) | |
9 | ||
10 | // ErrInvalidTimeFormat is returned when the generalizedTime string was not correct. | |
11 | var ErrInvalidTimeFormat = errors.New("invalid time format") | |
12 | ||
13 | var zeroTime = time.Time{} | |
14 | ||
15 | // ParseGeneralizedTime parses a string value and if it conforms to | |
16 | // GeneralizedTime[^0] format, will return a time.Time for that value. | |
17 | // | |
18 | // [^0]: https://www.itu.int/rec/T-REC-X.690-201508-I/en Section 11.7 | |
19 | func ParseGeneralizedTime(v []byte) (time.Time, error) { | |
20 | var format string | |
21 | var fract time.Duration | |
22 | ||
23 | str := []byte(DecodeString(v)) | |
24 | tzIndex := bytes.IndexAny(str, "Z+-") | |
25 | if tzIndex < 0 { | |
26 | return zeroTime, ErrInvalidTimeFormat | |
27 | } | |
28 | ||
29 | dot := bytes.IndexAny(str, ".,") | |
30 | switch dot { | |
31 | case -1: | |
32 | switch tzIndex { | |
33 | case 10: | |
34 | format = `2006010215Z` | |
35 | case 12: | |
36 | format = `200601021504Z` | |
37 | case 14: | |
38 | format = `20060102150405Z` | |
39 | default: | |
40 | return zeroTime, ErrInvalidTimeFormat | |
41 | } | |
42 | ||
43 | case 10, 12: | |
44 | if tzIndex < dot { | |
45 | return zeroTime, ErrInvalidTimeFormat | |
46 | } | |
47 | // a "," is also allowed, but would not be parsed by time.Parse(): | |
48 | str[dot] = '.' | |
49 | ||
50 | // If <minute> is omitted, then <fraction> represents a fraction of an | |
51 | // hour; otherwise, if <second> and <leap-second> are omitted, then | |
52 | // <fraction> represents a fraction of a minute; otherwise, <fraction> | |
53 | // represents a fraction of a second. | |
54 | ||
55 | // parse as float from dot to timezone | |
56 | f, err := strconv.ParseFloat(string(str[dot:tzIndex]), 64) | |
57 | if err != nil { | |
58 | return zeroTime, fmt.Errorf("failed to parse float: %s", err) | |
59 | } | |
60 | // ...and strip that part | |
61 | str = append(str[:dot], str[tzIndex:]...) | |
62 | tzIndex = dot | |
63 | ||
64 | if dot == 10 { | |
65 | fract = time.Duration(int64(f * float64(time.Hour))) | |
66 | format = `2006010215Z` | |
67 | } else { | |
68 | fract = time.Duration(int64(f * float64(time.Minute))) | |
69 | format = `200601021504Z` | |
70 | } | |
71 | ||
72 | case 14: | |
73 | if tzIndex < dot { | |
74 | return zeroTime, ErrInvalidTimeFormat | |
75 | } | |
76 | str[dot] = '.' | |
77 | // no need for fractional seconds, time.Parse() handles that | |
78 | format = `20060102150405Z` | |
79 | ||
80 | default: | |
81 | return zeroTime, ErrInvalidTimeFormat | |
82 | } | |
83 | ||
84 | l := len(str) | |
85 | switch l - tzIndex { | |
86 | case 1: | |
87 | if str[l-1] != 'Z' { | |
88 | return zeroTime, ErrInvalidTimeFormat | |
89 | } | |
90 | case 3: | |
91 | format += `0700` | |
92 | str = append(str, []byte("00")...) | |
93 | case 5: | |
94 | format += `0700` | |
95 | default: | |
96 | return zeroTime, ErrInvalidTimeFormat | |
97 | } | |
98 | ||
99 | t, err := time.Parse(format, string(str)) | |
100 | if err != nil { | |
101 | return zeroTime, fmt.Errorf("%s: %s", ErrInvalidTimeFormat, err) | |
102 | } | |
103 | return t.Add(fract), nil | |
104 | } |
0 | package ber | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | "time" | |
5 | ) | |
6 | ||
7 | func TestParseGeneralizedTime(t *testing.T) { | |
8 | for _, test := range []struct { | |
9 | str string | |
10 | wanted time.Time | |
11 | err error | |
12 | }{ | |
13 | { | |
14 | "20170222190527Z", | |
15 | time.Date(2017, time.Month(2), 22, 19, 05, 27, 0, time.UTC), | |
16 | nil, | |
17 | }, | |
18 | { | |
19 | "201702221905Z", | |
20 | time.Date(2017, time.Month(2), 22, 19, 05, 0, 0, time.UTC), | |
21 | nil, | |
22 | }, | |
23 | { | |
24 | "2017022219Z", | |
25 | time.Date(2017, time.Month(2), 22, 19, 0, 0, 0, time.UTC), | |
26 | nil, | |
27 | }, | |
28 | { | |
29 | "2017022219.25Z", | |
30 | time.Date(2017, time.Month(2), 22, 19, 15, 0, 0, time.UTC), | |
31 | nil, | |
32 | }, | |
33 | { | |
34 | "201702221905.25Z", | |
35 | time.Date(2017, time.Month(2), 22, 19, 5, 15, 0, time.UTC), | |
36 | nil, | |
37 | }, | |
38 | { | |
39 | "20170222190525-0100", | |
40 | time.Date(2017, time.Month(2), 22, 19, 5, 25, 0, time.FixedZone("", -3600)), | |
41 | nil, | |
42 | }, | |
43 | { | |
44 | "20170222190525+0100", | |
45 | time.Date(2017, time.Month(2), 22, 19, 5, 25, 0, time.FixedZone("", 3600)), | |
46 | nil, | |
47 | }, | |
48 | { | |
49 | "20170222190525+01", | |
50 | time.Date(2017, time.Month(2), 22, 19, 5, 25, 0, time.FixedZone("", 3600)), | |
51 | nil, | |
52 | }, | |
53 | { | |
54 | "20170222190527.123Z", | |
55 | time.Date(2017, time.Month(2), 22, 19, 05, 27, 123*1000*1000, time.UTC), | |
56 | nil, | |
57 | }, | |
58 | { | |
59 | "20170222190527,123Z", | |
60 | time.Date(2017, time.Month(2), 22, 19, 05, 27, 123*1000*1000, time.UTC), | |
61 | nil, | |
62 | }, | |
63 | { | |
64 | "2017022219-0100", | |
65 | time.Date(2017, time.Month(2), 22, 19, 0, 0, 0, time.FixedZone("", -3600)), | |
66 | nil, | |
67 | }, | |
68 | } { | |
69 | genTime, err := ParseGeneralizedTime([]byte(test.str)) | |
70 | if err != nil { | |
71 | if test.err != nil { | |
72 | if err != test.err { | |
73 | t.Errorf("unexpected error in %s: %s", test.str, err) | |
74 | } | |
75 | } else { | |
76 | t.Errorf("test %s failed with error: %s", test.str, err) | |
77 | } | |
78 | } else { | |
79 | if !genTime.Equal(test.wanted) { | |
80 | t.Errorf("test got unexpected result: wanted=%s, got=%s", test.wanted, genTime) | |
81 | } | |
82 | } | |
83 | } | |
84 | } |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "errors" |
4 | "fmt" | |
4 | 5 | "io" |
5 | 6 | ) |
6 | 7 | |
7 | 8 | func readHeader(reader io.Reader) (identifier Identifier, length int, read int, err error) { |
8 | if i, c, err := readIdentifier(reader); err != nil { | |
9 | var ( | |
10 | c, l int | |
11 | i Identifier | |
12 | ) | |
13 | ||
14 | if i, c, err = readIdentifier(reader); err != nil { | |
9 | 15 | return Identifier{}, 0, read, err |
10 | } else { | |
11 | identifier = i | |
12 | read += c | |
13 | 16 | } |
17 | identifier = i | |
18 | read += c | |
14 | 19 | |
15 | if l, c, err := readLength(reader); err != nil { | |
20 | if l, c, err = readLength(reader); err != nil { | |
16 | 21 | return Identifier{}, 0, read, err |
17 | } else { | |
18 | length = l | |
19 | read += c | |
20 | 22 | } |
23 | length = l | |
24 | read += c | |
21 | 25 | |
22 | 26 | // Validate length type with identifier (x.600, 8.1.3.2.a) |
23 | 27 | if length == LengthIndefinite && identifier.TagType == TypePrimitive { |
24 | 28 | return Identifier{}, 0, read, errors.New("indefinite length used with primitive type") |
25 | 29 | } |
26 | 30 | |
31 | if length < LengthIndefinite { | |
32 | err = fmt.Errorf("length cannot be less than %d", LengthIndefinite) | |
33 | return | |
34 | } | |
35 | ||
27 | 36 | return identifier, length, read, nil |
28 | 37 | } |
6 | 6 | ) |
7 | 7 | |
8 | 8 | func TestReadHeader(t *testing.T) { |
9 | testcases := map[string]struct { | |
9 | testCases := map[string]struct { | |
10 | 10 | Data []byte |
11 | 11 | ExpectedIdentifier Identifier |
12 | 12 | ExpectedLength int |
83 | 83 | }, |
84 | 84 | } |
85 | 85 | |
86 | for k, tc := range testcases { | |
86 | for k, tc := range testCases { | |
87 | 87 | reader := bytes.NewBuffer(tc.Data) |
88 | 88 | identifier, length, read, err := readHeader(reader) |
89 | 89 |
3 | 3 | "errors" |
4 | 4 | "fmt" |
5 | 5 | "io" |
6 | "math" | |
7 | 6 | ) |
8 | 7 | |
9 | 8 | func readIdentifier(reader io.Reader) (Identifier, int, error) { |
79 | 78 | |
80 | 79 | tag := identifier.Tag |
81 | 80 | |
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 | } | |
81 | b = append(b, encodeHighTag(tag)...) | |
100 | 82 | } |
101 | 83 | return b |
102 | 84 | } |
85 | ||
86 | func encodeHighTag(tag Tag) []byte { | |
87 | // set cap=4 to hopefully avoid additional allocations | |
88 | b := make([]byte, 0, 4) | |
89 | for tag != 0 { | |
90 | // t := last 7 bits of tag (HighTagValueBitmask = 0x7F) | |
91 | t := tag & HighTagValueBitmask | |
92 | ||
93 | // right shift tag 7 to remove what was just pulled off | |
94 | tag >>= 7 | |
95 | ||
96 | // if b already has entries this entry needs a continuation bit (0x80) | |
97 | if len(b) != 0 { | |
98 | t |= HighTagContinueBitmask | |
99 | } | |
100 | ||
101 | b = append(b, byte(t)) | |
102 | } | |
103 | // reverse | |
104 | // since bits were pulled off 'tag' small to high the byte slice is in reverse order. | |
105 | // example: tag = 0xFF results in {0x7F, 0x01 + 0x80 (continuation bit)} | |
106 | // this needs to be reversed into 0x81 0x7F | |
107 | for i, j := 0, len(b)-1; i < len(b)/2; i++ { | |
108 | b[i], b[j-i] = b[j-i], b[i] | |
109 | } | |
110 | return b | |
111 | } |
7 | 7 | ) |
8 | 8 | |
9 | 9 | func TestReadIdentifier(t *testing.T) { |
10 | testcases := map[string]struct { | |
10 | testCases := map[string]struct { | |
11 | 11 | Data []byte |
12 | 12 | |
13 | 13 | ExpectedIdentifier Identifier |
180 | 180 | }, |
181 | 181 | } |
182 | 182 | |
183 | for k, tc := range testcases { | |
183 | for k, tc := range testCases { | |
184 | 184 | reader := bytes.NewBuffer(tc.Data) |
185 | 185 | identifier, read, err := readIdentifier(reader) |
186 | 186 | |
227 | 227 | } |
228 | 228 | |
229 | 229 | func TestEncodeIdentifier(t *testing.T) { |
230 | testcases := map[string]struct { | |
230 | testCases := map[string]struct { | |
231 | 231 | Identifier Identifier |
232 | 232 | ExpectedBytes []byte |
233 | 233 | }{ |
334 | 334 | }, |
335 | 335 | } |
336 | 336 | |
337 | for k, tc := range testcases { | |
337 | for k, tc := range testCases { | |
338 | 338 | b := encodeIdentifier(tc.Identifier) |
339 | if bytes.Compare(tc.ExpectedBytes, b) != 0 { | |
339 | if !bytes.Equal(tc.ExpectedBytes, b) { | |
340 | 340 | t.Errorf("%s: Expected\n\t%#v\ngot\n\t%#v", k, tc.ExpectedBytes, b) |
341 | 341 | } |
342 | 342 | } |
343 | 343 | } |
344 | ||
345 | func TestEncodeHighTag(t *testing.T) { | |
346 | cases := []struct { | |
347 | tag Tag | |
348 | want []byte | |
349 | }{ | |
350 | {134, []byte{0x80 + 0x01, 0x06}}, | |
351 | {123456, []byte{0x80 + 0x07, 0x80 + 0x44, 0x40}}, | |
352 | {0xFF, []byte{0x81, 0x7F}}, | |
353 | } | |
354 | ||
355 | for _, c := range cases { | |
356 | got := encodeHighTag(c.tag) | |
357 | ||
358 | if !bytes.Equal(c.want, got) { | |
359 | t.Errorf("tag: %d want: %#v got: %#v", c.tag, c.want, got) | |
360 | } | |
361 | } | |
362 | } |
70 | 70 | } |
71 | 71 | |
72 | 72 | func encodeLength(length int) []byte { |
73 | length_bytes := encodeUnsignedInteger(uint64(length)) | |
74 | if length > 127 || len(length_bytes) > 1 { | |
75 | longFormBytes := []byte{(LengthLongFormBitmask | byte(len(length_bytes)))} | |
76 | longFormBytes = append(longFormBytes, length_bytes...) | |
77 | length_bytes = longFormBytes | |
73 | lengthBytes := encodeUnsignedInteger(uint64(length)) | |
74 | if length > 127 || len(lengthBytes) > 1 { | |
75 | longFormBytes := []byte{LengthLongFormBitmask | byte(len(lengthBytes))} | |
76 | longFormBytes = append(longFormBytes, lengthBytes...) | |
77 | lengthBytes = longFormBytes | |
78 | 78 | } |
79 | return length_bytes | |
79 | return lengthBytes | |
80 | 80 | } |
7 | 7 | ) |
8 | 8 | |
9 | 9 | func TestReadLength(t *testing.T) { |
10 | testcases := map[string]struct { | |
10 | testCases := map[string]struct { | |
11 | 11 | Data []byte |
12 | 12 | |
13 | 13 | ExpectedLength int64 |
96 | 96 | }, |
97 | 97 | } |
98 | 98 | |
99 | for k, tc := range testcases { | |
99 | for k, tc := range testCases { | |
100 | 100 | // Skip tests requiring 64-bit integers on platforms that don't support them |
101 | 101 | if tc.ExpectedLength != int64(int(tc.ExpectedLength)) { |
102 | 102 | continue |
127 | 127 | } |
128 | 128 | |
129 | 129 | func TestEncodeLength(t *testing.T) { |
130 | testcases := map[string]struct { | |
130 | testCases := map[string]struct { | |
131 | 131 | Length int64 |
132 | 132 | ExpectedBytes []byte |
133 | 133 | }{ |
176 | 176 | }, |
177 | 177 | } |
178 | 178 | |
179 | for k, tc := range testcases { | |
179 | for k, tc := range testCases { | |
180 | 180 | // Skip tests requiring 64-bit integers on platforms that don't support them |
181 | 181 | if tc.Length != int64(int(tc.Length)) { |
182 | 182 | continue |
183 | 183 | } |
184 | 184 | |
185 | 185 | b := encodeLength(int(tc.Length)) |
186 | if bytes.Compare(tc.ExpectedBytes, b) != 0 { | |
186 | if !bytes.Equal(tc.ExpectedBytes, b) { | |
187 | 187 | t.Errorf("%s: Expected\n\t%#v\ngot\n\t%#v", k, tc.ExpectedBytes, b) |
188 | 188 | } |
189 | 189 | } |
0 | package ber | |
1 | ||
2 | import ( | |
3 | "bytes" | |
4 | "errors" | |
5 | "fmt" | |
6 | "math" | |
7 | "strconv" | |
8 | "strings" | |
9 | ) | |
10 | ||
11 | func encodeFloat(v float64) []byte { | |
12 | switch { | |
13 | case math.IsInf(v, 1): | |
14 | return []byte{0x40} | |
15 | case math.IsInf(v, -1): | |
16 | return []byte{0x41} | |
17 | case math.IsNaN(v): | |
18 | return []byte{0x42} | |
19 | case v == 0.0: | |
20 | if math.Signbit(v) { | |
21 | return []byte{0x43} | |
22 | } | |
23 | return []byte{} | |
24 | default: | |
25 | // we take the easy part ;-) | |
26 | value := []byte(strconv.FormatFloat(v, 'G', -1, 64)) | |
27 | var ret []byte | |
28 | if bytes.Contains(value, []byte{'E'}) { | |
29 | ret = []byte{0x03} | |
30 | } else { | |
31 | ret = []byte{0x02} | |
32 | } | |
33 | ret = append(ret, value...) | |
34 | return ret | |
35 | } | |
36 | } | |
37 | ||
38 | func ParseReal(v []byte) (val float64, err error) { | |
39 | if len(v) == 0 { | |
40 | return 0.0, nil | |
41 | } | |
42 | switch { | |
43 | case v[0]&0x80 == 0x80: | |
44 | val, err = parseBinaryFloat(v) | |
45 | case v[0]&0xC0 == 0x40: | |
46 | val, err = parseSpecialFloat(v) | |
47 | case v[0]&0xC0 == 0x0: | |
48 | val, err = parseDecimalFloat(v) | |
49 | default: | |
50 | return 0.0, fmt.Errorf("invalid info block") | |
51 | } | |
52 | if err != nil { | |
53 | return 0.0, err | |
54 | } | |
55 | ||
56 | if val == 0.0 && !math.Signbit(val) { | |
57 | return 0.0, errors.New("REAL value +0 must be encoded with zero-length value block") | |
58 | } | |
59 | return val, nil | |
60 | } | |
61 | ||
62 | func parseBinaryFloat(v []byte) (float64, error) { | |
63 | var info byte | |
64 | var buf []byte | |
65 | ||
66 | info, v = v[0], v[1:] | |
67 | ||
68 | var base int | |
69 | switch info & 0x30 { | |
70 | case 0x00: | |
71 | base = 2 | |
72 | case 0x10: | |
73 | base = 8 | |
74 | case 0x20: | |
75 | base = 16 | |
76 | case 0x30: | |
77 | return 0.0, errors.New("bits 6 and 5 of information octet for REAL are equal to 11") | |
78 | } | |
79 | ||
80 | scale := uint((info & 0x0c) >> 2) | |
81 | ||
82 | var expLen int | |
83 | switch info & 0x03 { | |
84 | case 0x00: | |
85 | expLen = 1 | |
86 | case 0x01: | |
87 | expLen = 2 | |
88 | case 0x02: | |
89 | expLen = 3 | |
90 | case 0x03: | |
91 | expLen = int(v[0]) | |
92 | if expLen > 8 { | |
93 | return 0.0, errors.New("too big value of exponent") | |
94 | } | |
95 | v = v[1:] | |
96 | } | |
97 | buf, v = v[:expLen], v[expLen:] | |
98 | exponent, err := ParseInt64(buf) | |
99 | if err != nil { | |
100 | return 0.0, err | |
101 | } | |
102 | ||
103 | if len(v) > 8 { | |
104 | return 0.0, errors.New("too big value of mantissa") | |
105 | } | |
106 | ||
107 | mant, err := ParseInt64(v) | |
108 | if err != nil { | |
109 | return 0.0, err | |
110 | } | |
111 | mantissa := mant << scale | |
112 | ||
113 | if info&0x40 == 0x40 { | |
114 | mantissa = -mantissa | |
115 | } | |
116 | ||
117 | return float64(mantissa) * math.Pow(float64(base), float64(exponent)), nil | |
118 | } | |
119 | ||
120 | func parseDecimalFloat(v []byte) (val float64, err error) { | |
121 | switch v[0] & 0x3F { | |
122 | case 0x01: // NR form 1 | |
123 | var iVal int64 | |
124 | iVal, err = strconv.ParseInt(strings.TrimLeft(string(v[1:]), " "), 10, 64) | |
125 | val = float64(iVal) | |
126 | case 0x02, 0x03: // NR form 2, 3 | |
127 | val, err = strconv.ParseFloat(strings.Replace(strings.TrimLeft(string(v[1:]), " "), ",", ".", -1), 64) | |
128 | default: | |
129 | err = errors.New("incorrect NR form") | |
130 | } | |
131 | if err != nil { | |
132 | return 0.0, err | |
133 | } | |
134 | ||
135 | if val == 0.0 && math.Signbit(val) { | |
136 | return 0.0, errors.New("REAL value -0 must be encoded as a special value") | |
137 | } | |
138 | return val, nil | |
139 | } | |
140 | ||
141 | func parseSpecialFloat(v []byte) (float64, error) { | |
142 | if len(v) != 1 { | |
143 | return 0.0, errors.New(`encoding of "special value" must not contain exponent and mantissa`) | |
144 | } | |
145 | switch v[0] { | |
146 | case 0x40: | |
147 | return math.Inf(1), nil | |
148 | case 0x41: | |
149 | return math.Inf(-1), nil | |
150 | case 0x42: | |
151 | return math.NaN(), nil | |
152 | case 0x43: | |
153 | return math.Copysign(0, -1), nil | |
154 | } | |
155 | return 0.0, errors.New(`encoding of "special value" not from ASN.1 standard`) | |
156 | } |
0 | package ber | |
1 | ||
2 | import ( | |
3 | "math" | |
4 | "testing" | |
5 | ) | |
6 | ||
7 | var negativeZero = math.Copysign(0, -1) | |
8 | ||
9 | func TestRealEncoding(t *testing.T) { | |
10 | for _, value := range []float64{ | |
11 | 0.15625, | |
12 | -0.15625, | |
13 | math.Inf(1), | |
14 | math.Inf(-1), | |
15 | math.NaN(), | |
16 | negativeZero, | |
17 | 0.0, | |
18 | } { | |
19 | enc := encodeFloat(value) | |
20 | dec, err := ParseReal(enc) | |
21 | if err != nil { | |
22 | t.Errorf("Failed to decode %f (%v): %s", value, enc, err) | |
23 | } | |
24 | if dec != value { | |
25 | if !(math.IsNaN(dec) && math.IsNaN(value)) { | |
26 | t.Errorf("decoded value != orig: %f <=> %f", value, dec) | |
27 | } | |
28 | } | |
29 | } | |
30 | } |
0 | package ber | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ) | |
5 | ||
6 | func TestIA5String(t *testing.T) { | |
7 | for _, test := range []struct { | |
8 | value string | |
9 | expectedErr string | |
10 | }{ | |
11 | {"asdfgh", ""}, | |
12 | {"asdfgå", "invalid character for IA5String at pos 5: å"}, | |
13 | } { | |
14 | pkt := NewString(ClassUniversal, TypePrimitive, TagIA5String, test.value, test.value) | |
15 | dec, err := DecodePacketErr(pkt.Bytes()) | |
16 | if err == nil { | |
17 | if test.expectedErr != "" { | |
18 | t.Errorf("got unexpected error for `%s`: %s", test.value, err) | |
19 | } | |
20 | if dec.Value.(string) != test.value { | |
21 | t.Errorf("did not get back original value: %s <=> %s", dec.Value.(string), test.value) | |
22 | } | |
23 | } else if err.Error() != test.expectedErr { | |
24 | t.Errorf("got unexpected error for `%s`: %s", test.value, err) | |
25 | } | |
26 | } | |
27 | } | |
28 | ||
29 | func TestPrintableString(t *testing.T) { | |
30 | for _, test := range []struct { | |
31 | value string | |
32 | expectedErr string | |
33 | }{ | |
34 | {"asdfgh", ""}, | |
35 | {"asdfgå", "invalid character in position 5"}, | |
36 | } { | |
37 | pkt := NewString(ClassUniversal, TypePrimitive, TagPrintableString, test.value, test.value) | |
38 | dec, err := DecodePacketErr(pkt.Bytes()) | |
39 | if err == nil { | |
40 | if test.expectedErr != "" { | |
41 | t.Errorf("got unexpected error for `%s`: %s", test.value, err) | |
42 | } | |
43 | if dec.Value.(string) != test.value { | |
44 | t.Errorf("did not get back original value: %s <=> %s", dec.Value.(string), test.value) | |
45 | } | |
46 | } else if err.Error() != test.expectedErr { | |
47 | t.Errorf("got unexpected error for `%s`: %s", test.value, err) | |
48 | } | |
49 | } | |
50 | } | |
51 | ||
52 | func TestUTF8String(t *testing.T) { | |
53 | for _, test := range []struct { | |
54 | value string | |
55 | expectedErr string | |
56 | }{ | |
57 | {"åäöüß", ""}, | |
58 | {"asdfg\xFF", "invalid UTF-8 string"}, | |
59 | } { | |
60 | pkt := NewString(ClassUniversal, TypePrimitive, TagUTF8String, test.value, test.value) | |
61 | dec, err := DecodePacketErr(pkt.Bytes()) | |
62 | if err == nil { | |
63 | if test.expectedErr != "" { | |
64 | t.Errorf("got unexpected error for `%s`: %s", test.value, err) | |
65 | } | |
66 | if dec.Value.(string) != test.value { | |
67 | t.Errorf("did not get back original value: %s <=> %s", dec.Value.(string), test.value) | |
68 | } | |
69 | } else if err.Error() != test.expectedErr { | |
70 | t.Errorf("got unexpected error for `%s`: %s", test.value, err) | |
71 | } | |
72 | } | |
73 | } |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "bytes" |
4 | "fmt" | |
4 | 5 | "io" |
5 | 6 | "io/ioutil" |
7 | "math" | |
6 | 8 | "testing" |
7 | 9 | ) |
8 | 10 | |
10 | 12 | |
11 | 13 | // Tests from http://www.strozhevsky.com/free_docs/free_asn1_testsuite_descr.pdf |
12 | 14 | // Source files and descriptions at http://www.strozhevsky.com/free_docs/TEST_SUITE.zip |
13 | var testcases = []struct { | |
15 | var testCases = []struct { | |
14 | 16 | // File contains the path to the BER-encoded file |
15 | 17 | File string |
16 | 18 | // Error indicates whether a decoding error is expected |
27 | 29 | {File: "tests/tc4.ber", Error: "invalid length byte 0xff"}, |
28 | 30 | {File: "tests/tc5.ber", Error: "", AbnormalEncoding: true}, |
29 | 31 | // 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" | |
32 | {File: "tests/tc6.ber", Error: "REAL value +0 must be encoded with zero-length value block"}, | |
33 | {File: "tests/tc7.ber", Error: "REAL value -0 must be encoded as a special value"}, | |
34 | {File: "tests/tc8.ber", Error: `encoding of "special value" must not contain exponent and mantissa`}, | |
35 | {File: "tests/tc9.ber", Error: "bits 6 and 5 of information octet for REAL are equal to 11"}, | |
34 | 36 | {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/tc11.ber", Error: "incorrect NR form"}, | |
38 | {File: "tests/tc12.ber", Error: `encoding of "special value" not from ASN.1 standard`}, | |
37 | 39 | {File: "tests/tc13.ber", Error: errEOF}, |
38 | 40 | {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" | |
41 | {File: "tests/tc15.ber", Error: "too big value of exponent"}, | |
42 | {File: "tests/tc16.ber", Error: "too big value of mantissa"}, | |
43 | {File: "tests/tc17.ber", Error: "too big value of exponent"}, // Error: "Too big values for exponent and mantissa + using of "scaling factor" value" | |
42 | 44 | // Integers |
43 | 45 | {File: "tests/tc18.ber", Error: ""}, |
44 | 46 | {File: "tests/tc19.ber", Error: errEOF}, |
58 | 60 | {File: "tests/tc30.ber", Error: ""}, |
59 | 61 | {File: "tests/tc31.ber", Error: errEOF}, |
60 | 62 | {File: "tests/tc32.ber", Error: ""}, |
61 | // Bitstring (some expected failures are disabled until support is added) | |
63 | // Bit string (some expected failures are disabled until support is added) | |
62 | 64 | {File: "tests/tc33.ber", Error: ""}, // Error: "Too big value for "unused bits"" |
63 | 65 | {File: "tests/tc34.ber", Error: errEOF}, |
64 | 66 | {File: "tests/tc35.ber", Error: "", IndefiniteEncoding: true}, // Error: "Using of different from BIT STRING types as internal types for constructive encoding" |
73 | 75 | {File: "tests/tc43.ber", Error: errEOF}, |
74 | 76 | {File: "tests/tc44.ber", Error: ""}, |
75 | 77 | {File: "tests/tc45.ber", Error: ""}, |
76 | // Bitstring | |
78 | // Bit string | |
77 | 79 | {File: "tests/tc46.ber", Error: "indefinite length used with primitive type"}, |
78 | 80 | {File: "tests/tc47.ber", Error: "eoc child not allowed with definite length"}, |
79 | 81 | {File: "tests/tc48.ber", Error: "", IndefiniteEncoding: true}, // Error: "Using of more than 7 "unused bits" in BIT STRING with constrictive encoding form" |
82 | {File: "tests/tc49.ber", Error: ""}, | |
83 | {File: "tests/tc50.ber", Error: is64bit("length cannot be less than -1", "long-form length overflow")}, | |
84 | {File: "tests/tc51.ber", Error: is64bit(fmt.Sprintf("length 206966894640 greater than maximum %v", MaxPacketLengthBytes), "long-form length overflow")}, | |
85 | } | |
86 | ||
87 | func is64bit(a, b string) string { | |
88 | maxInt64 := int64(math.MaxInt64) | |
89 | length := int(maxInt64) | |
90 | if int64(length) != maxInt64 { | |
91 | return b | |
92 | } | |
93 | return a | |
80 | 94 | } |
81 | 95 | |
82 | 96 | func TestSuiteDecodePacket(t *testing.T) { |
83 | 97 | // Debug = true |
84 | for _, tc := range testcases { | |
98 | for _, tc := range testCases { | |
85 | 99 | file := tc.File |
86 | 100 | |
87 | 101 | dataIn, err := ioutil.ReadFile(file) |
113 | 127 | } |
114 | 128 | } else if !bytes.Equal(dataOut, dataIn) { |
115 | 129 | // Make sure the serialized data matches the source |
116 | t.Errorf("%s: data should be the same", file) | |
130 | t.Errorf("%s: data should be the same\nwant: %#v\ngot: %#v", file, dataIn, dataOut) | |
117 | 131 | } |
118 | 132 | |
119 | 133 | packet, err = DecodePacketErr(dataOut) |
125 | 139 | // Make sure the re-serialized data matches our original serialization |
126 | 140 | dataOut2 := packet.Bytes() |
127 | 141 | if !bytes.Equal(dataOut, dataOut2) { |
128 | t.Errorf("%s: data should be the same", file) | |
142 | t.Errorf("%s: data should be the same\nwant: %#v\ngot: %#v", file, dataOut, dataOut2) | |
129 | 143 | } |
130 | 144 | } |
131 | 145 | } |
132 | 146 | |
133 | 147 | func TestSuiteReadPacket(t *testing.T) { |
134 | for _, tc := range testcases { | |
148 | for _, tc := range testCases { | |
135 | 149 | file := tc.File |
136 | 150 | |
137 | 151 | dataIn, err := ioutil.ReadFile(file) |
163 | 177 | } |
164 | 178 | } else if !bytes.Equal(dataOut, dataIn) { |
165 | 179 | // Make sure the serialized data matches the source |
166 | t.Errorf("%s: data should be the same", file) | |
180 | t.Errorf("%s: data should be the same\nwant: %#v\ngot: %#v", file, dataIn, dataOut) | |
167 | 181 | } |
168 | 182 | |
169 | 183 | packet, err = DecodePacketErr(dataOut) |
175 | 189 | // Make sure the re-serialized data matches our original serialization |
176 | 190 | dataOut2 := packet.Bytes() |
177 | 191 | if !bytes.Equal(dataOut, dataOut2) { |
178 | t.Errorf("%s: data should be the same", file) | |
192 | t.Errorf("%s: data should be the same\nwant: %#v\ngot: %#v", file, dataOut, dataOut2) | |
179 | 193 | } |
180 | 194 | } |
181 | 195 | } |
Binary diff not shown
0 | ˆ›0000000⏎ |
0 | …00000⏎ |