| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 | 
							- package ber
 
- import (
 
- 	"bytes"
 
- 	"errors"
 
- 	"fmt"
 
- 	"io"
 
- 	"os"
 
- 	"reflect"
 
- )
 
- type Packet struct {
 
- 	Identifier
 
- 	Value       interface{}
 
- 	ByteValue   []byte
 
- 	Data        *bytes.Buffer
 
- 	Children    []*Packet
 
- 	Description string
 
- }
 
- type Identifier struct {
 
- 	ClassType Class
 
- 	TagType   Type
 
- 	Tag       Tag
 
- }
 
- type Tag uint64
 
- const (
 
- 	TagEOC              Tag = 0x00
 
- 	TagBoolean          Tag = 0x01
 
- 	TagInteger          Tag = 0x02
 
- 	TagBitString        Tag = 0x03
 
- 	TagOctetString      Tag = 0x04
 
- 	TagNULL             Tag = 0x05
 
- 	TagObjectIdentifier Tag = 0x06
 
- 	TagObjectDescriptor Tag = 0x07
 
- 	TagExternal         Tag = 0x08
 
- 	TagRealFloat        Tag = 0x09
 
- 	TagEnumerated       Tag = 0x0a
 
- 	TagEmbeddedPDV      Tag = 0x0b
 
- 	TagUTF8String       Tag = 0x0c
 
- 	TagRelativeOID      Tag = 0x0d
 
- 	TagSequence         Tag = 0x10
 
- 	TagSet              Tag = 0x11
 
- 	TagNumericString    Tag = 0x12
 
- 	TagPrintableString  Tag = 0x13
 
- 	TagT61String        Tag = 0x14
 
- 	TagVideotexString   Tag = 0x15
 
- 	TagIA5String        Tag = 0x16
 
- 	TagUTCTime          Tag = 0x17
 
- 	TagGeneralizedTime  Tag = 0x18
 
- 	TagGraphicString    Tag = 0x19
 
- 	TagVisibleString    Tag = 0x1a
 
- 	TagGeneralString    Tag = 0x1b
 
- 	TagUniversalString  Tag = 0x1c
 
- 	TagCharacterString  Tag = 0x1d
 
- 	TagBMPString        Tag = 0x1e
 
- 	TagBitmask          Tag = 0x1f // xxx11111b
 
- 	// HighTag indicates the start of a high-tag byte sequence
 
- 	HighTag Tag = 0x1f // xxx11111b
 
- 	// HighTagContinueBitmask indicates the high-tag byte sequence should continue
 
- 	HighTagContinueBitmask Tag = 0x80 // 10000000b
 
- 	// HighTagValueBitmask obtains the tag value from a high-tag byte sequence byte
 
- 	HighTagValueBitmask Tag = 0x7f // 01111111b
 
- )
 
- const (
 
- 	// LengthLongFormBitmask is the mask to apply to the length byte to see if a long-form byte sequence is used
 
- 	LengthLongFormBitmask = 0x80
 
- 	// LengthValueBitmask is the mask to apply to the length byte to get the number of bytes in the long-form byte sequence
 
- 	LengthValueBitmask = 0x7f
 
- 	// LengthIndefinite is returned from readLength to indicate an indefinite length
 
- 	LengthIndefinite = -1
 
- )
 
- var tagMap = map[Tag]string{
 
- 	TagEOC:              "EOC (End-of-Content)",
 
- 	TagBoolean:          "Boolean",
 
- 	TagInteger:          "Integer",
 
- 	TagBitString:        "Bit String",
 
- 	TagOctetString:      "Octet String",
 
- 	TagNULL:             "NULL",
 
- 	TagObjectIdentifier: "Object Identifier",
 
- 	TagObjectDescriptor: "Object Descriptor",
 
- 	TagExternal:         "External",
 
- 	TagRealFloat:        "Real (float)",
 
- 	TagEnumerated:       "Enumerated",
 
- 	TagEmbeddedPDV:      "Embedded PDV",
 
- 	TagUTF8String:       "UTF8 String",
 
- 	TagRelativeOID:      "Relative-OID",
 
- 	TagSequence:         "Sequence and Sequence of",
 
- 	TagSet:              "Set and Set OF",
 
- 	TagNumericString:    "Numeric String",
 
- 	TagPrintableString:  "Printable String",
 
- 	TagT61String:        "T61 String",
 
- 	TagVideotexString:   "Videotex String",
 
- 	TagIA5String:        "IA5 String",
 
- 	TagUTCTime:          "UTC Time",
 
- 	TagGeneralizedTime:  "Generalized Time",
 
- 	TagGraphicString:    "Graphic String",
 
- 	TagVisibleString:    "Visible String",
 
- 	TagGeneralString:    "General String",
 
- 	TagUniversalString:  "Universal String",
 
- 	TagCharacterString:  "Character String",
 
- 	TagBMPString:        "BMP String",
 
- }
 
- type Class uint8
 
- const (
 
- 	ClassUniversal   Class = 0   // 00xxxxxxb
 
- 	ClassApplication Class = 64  // 01xxxxxxb
 
- 	ClassContext     Class = 128 // 10xxxxxxb
 
- 	ClassPrivate     Class = 192 // 11xxxxxxb
 
- 	ClassBitmask     Class = 192 // 11xxxxxxb
 
- )
 
- var ClassMap = map[Class]string{
 
- 	ClassUniversal:   "Universal",
 
- 	ClassApplication: "Application",
 
- 	ClassContext:     "Context",
 
- 	ClassPrivate:     "Private",
 
- }
 
- type Type uint8
 
- const (
 
- 	TypePrimitive   Type = 0  // xx0xxxxxb
 
- 	TypeConstructed Type = 32 // xx1xxxxxb
 
- 	TypeBitmask     Type = 32 // xx1xxxxxb
 
- )
 
- var TypeMap = map[Type]string{
 
- 	TypePrimitive:   "Primitive",
 
- 	TypeConstructed: "Constructed",
 
- }
 
- var Debug bool = false
 
- func PrintBytes(out io.Writer, buf []byte, indent string) {
 
- 	data_lines := make([]string, (len(buf)/30)+1)
 
- 	num_lines := make([]string, (len(buf)/30)+1)
 
- 	for i, b := range buf {
 
- 		data_lines[i/30] += fmt.Sprintf("%02x ", b)
 
- 		num_lines[i/30] += fmt.Sprintf("%02d ", (i+1)%100)
 
- 	}
 
- 	for i := 0; i < len(data_lines); i++ {
 
- 		out.Write([]byte(indent + data_lines[i] + "\n"))
 
- 		out.Write([]byte(indent + num_lines[i] + "\n\n"))
 
- 	}
 
- }
 
- func PrintPacket(p *Packet) {
 
- 	printPacket(os.Stdout, p, 0, false)
 
- }
 
- func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
 
- 	indent_str := ""
 
- 	for len(indent_str) != indent {
 
- 		indent_str += " "
 
- 	}
 
- 	class_str := ClassMap[p.ClassType]
 
- 	tagtype_str := TypeMap[p.TagType]
 
- 	tag_str := fmt.Sprintf("0x%02X", p.Tag)
 
- 	if p.ClassType == ClassUniversal {
 
- 		tag_str = tagMap[p.Tag]
 
- 	}
 
- 	value := fmt.Sprint(p.Value)
 
- 	description := ""
 
- 	if p.Description != "" {
 
- 		description = p.Description + ": "
 
- 	}
 
- 	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)
 
- 	if printBytes {
 
- 		PrintBytes(out, p.Bytes(), indent_str)
 
- 	}
 
- 	for _, child := range p.Children {
 
- 		printPacket(out, child, indent+1, printBytes)
 
- 	}
 
- }
 
- // ReadPacket reads a single Packet from the reader
 
- func ReadPacket(reader io.Reader) (*Packet, error) {
 
- 	p, _, err := readPacket(reader)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	return p, nil
 
- }
 
- func DecodeString(data []byte) string {
 
- 	return string(data)
 
- }
 
- func parseInt64(bytes []byte) (ret int64, err error) {
 
- 	if len(bytes) > 8 {
 
- 		// We'll overflow an int64 in this case.
 
- 		err = fmt.Errorf("integer too large")
 
- 		return
 
- 	}
 
- 	for bytesRead := 0; bytesRead < len(bytes); bytesRead++ {
 
- 		ret <<= 8
 
- 		ret |= int64(bytes[bytesRead])
 
- 	}
 
- 	// Shift up and down in order to sign extend the result.
 
- 	ret <<= 64 - uint8(len(bytes))*8
 
- 	ret >>= 64 - uint8(len(bytes))*8
 
- 	return
 
- }
 
- func encodeInteger(i int64) []byte {
 
- 	n := int64Length(i)
 
- 	out := make([]byte, n)
 
- 	var j int
 
- 	for ; n > 0; n-- {
 
- 		out[j] = (byte(i >> uint((n-1)*8)))
 
- 		j++
 
- 	}
 
- 	return out
 
- }
 
- func int64Length(i int64) (numBytes int) {
 
- 	numBytes = 1
 
- 	for i > 127 {
 
- 		numBytes++
 
- 		i >>= 8
 
- 	}
 
- 	for i < -128 {
 
- 		numBytes++
 
- 		i >>= 8
 
- 	}
 
- 	return
 
- }
 
- // DecodePacket decodes the given bytes into a single Packet
 
- // If a decode error is encountered, nil is returned.
 
- func DecodePacket(data []byte) *Packet {
 
- 	p, _, _ := readPacket(bytes.NewBuffer(data))
 
- 	return p
 
- }
 
- // DecodePacketErr decodes the given bytes into a single Packet
 
- // If a decode error is encountered, nil is returned
 
- func DecodePacketErr(data []byte) (*Packet, error) {
 
- 	p, _, err := readPacket(bytes.NewBuffer(data))
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	return p, nil
 
- }
 
- // readPacket reads a single Packet from the reader, returning the number of bytes read
 
- func readPacket(reader io.Reader) (*Packet, int, error) {
 
- 	identifier, length, read, err := readHeader(reader)
 
- 	if err != nil {
 
- 		return nil, read, err
 
- 	}
 
- 	p := &Packet{
 
- 		Identifier: identifier,
 
- 	}
 
- 	p.Data = new(bytes.Buffer)
 
- 	p.Children = make([]*Packet, 0, 2)
 
- 	p.Value = nil
 
- 	if p.TagType == TypeConstructed {
 
- 		// TODO: if universal, ensure tag type is allowed to be constructed
 
- 		// Track how much content we've read
 
- 		contentRead := 0
 
- 		for {
 
- 			if length != LengthIndefinite {
 
- 				// End if we've read what we've been told to
 
- 				if contentRead == length {
 
- 					break
 
- 				}
 
- 				// Detect if a packet boundary didn't fall on the expected length
 
- 				if contentRead > length {
 
- 					return nil, read, fmt.Errorf("expected to read %d bytes, read %d", length, contentRead)
 
- 				}
 
- 			}
 
- 			// Read the next packet
 
- 			child, r, err := readPacket(reader)
 
- 			if err != nil {
 
- 				return nil, read, err
 
- 			}
 
- 			contentRead += r
 
- 			read += r
 
- 			// Test is this is the EOC marker for our packet
 
- 			if isEOCPacket(child) {
 
- 				if length == LengthIndefinite {
 
- 					break
 
- 				}
 
- 				return nil, read, errors.New("eoc child not allowed with definite length")
 
- 			}
 
- 			// Append and continue
 
- 			p.AppendChild(child)
 
- 		}
 
- 		return p, read, nil
 
- 	}
 
- 	if length == LengthIndefinite {
 
- 		return nil, read, errors.New("indefinite length used with primitive type")
 
- 	}
 
- 	// Read definite-length content
 
- 	content := make([]byte, length, length)
 
- 	if length > 0 {
 
- 		_, err := io.ReadFull(reader, content)
 
- 		if err != nil {
 
- 			if err == io.EOF {
 
- 				return nil, read, io.ErrUnexpectedEOF
 
- 			}
 
- 			return nil, read, err
 
- 		}
 
- 		read += length
 
- 	}
 
- 	if p.ClassType == ClassUniversal {
 
- 		p.Data.Write(content)
 
- 		p.ByteValue = content
 
- 		switch p.Tag {
 
- 		case TagEOC:
 
- 		case TagBoolean:
 
- 			val, _ := parseInt64(content)
 
- 			p.Value = val != 0
 
- 		case TagInteger:
 
- 			p.Value, _ = parseInt64(content)
 
- 		case TagBitString:
 
- 		case TagOctetString:
 
- 			// the actual string encoding is not known here
 
- 			// (e.g. for LDAP content is already an UTF8-encoded
 
- 			// string). Return the data without further processing
 
- 			p.Value = DecodeString(content)
 
- 		case TagNULL:
 
- 		case TagObjectIdentifier:
 
- 		case TagObjectDescriptor:
 
- 		case TagExternal:
 
- 		case TagRealFloat:
 
- 		case TagEnumerated:
 
- 			p.Value, _ = parseInt64(content)
 
- 		case TagEmbeddedPDV:
 
- 		case TagUTF8String:
 
- 			p.Value = DecodeString(content)
 
- 		case TagRelativeOID:
 
- 		case TagSequence:
 
- 		case TagSet:
 
- 		case TagNumericString:
 
- 		case TagPrintableString:
 
- 			p.Value = DecodeString(content)
 
- 		case TagT61String:
 
- 		case TagVideotexString:
 
- 		case TagIA5String:
 
- 		case TagUTCTime:
 
- 		case TagGeneralizedTime:
 
- 		case TagGraphicString:
 
- 		case TagVisibleString:
 
- 		case TagGeneralString:
 
- 		case TagUniversalString:
 
- 		case TagCharacterString:
 
- 		case TagBMPString:
 
- 		}
 
- 	} else {
 
- 		p.Data.Write(content)
 
- 	}
 
- 	return p, read, nil
 
- }
 
- func (p *Packet) Bytes() []byte {
 
- 	var out bytes.Buffer
 
- 	out.Write(encodeIdentifier(p.Identifier))
 
- 	out.Write(encodeLength(p.Data.Len()))
 
- 	out.Write(p.Data.Bytes())
 
- 	return out.Bytes()
 
- }
 
- func (p *Packet) AppendChild(child *Packet) {
 
- 	p.Data.Write(child.Bytes())
 
- 	p.Children = append(p.Children, child)
 
- }
 
- func Encode(ClassType Class, TagType Type, Tag Tag, Value interface{}, Description string) *Packet {
 
- 	p := new(Packet)
 
- 	p.ClassType = ClassType
 
- 	p.TagType = TagType
 
- 	p.Tag = Tag
 
- 	p.Data = new(bytes.Buffer)
 
- 	p.Children = make([]*Packet, 0, 2)
 
- 	p.Value = Value
 
- 	p.Description = Description
 
- 	if Value != nil {
 
- 		v := reflect.ValueOf(Value)
 
- 		if ClassType == ClassUniversal {
 
- 			switch Tag {
 
- 			case TagOctetString:
 
- 				sv, ok := v.Interface().(string)
 
- 				if ok {
 
- 					p.Data.Write([]byte(sv))
 
- 				}
 
- 			}
 
- 		}
 
- 	}
 
- 	return p
 
- }
 
- func NewSequence(Description string) *Packet {
 
- 	return Encode(ClassUniversal, TypeConstructed, TagSequence, nil, Description)
 
- }
 
- func NewBoolean(ClassType Class, TagType Type, Tag Tag, Value bool, Description string) *Packet {
 
- 	intValue := int64(0)
 
- 	if Value {
 
- 		intValue = 1
 
- 	}
 
- 	p := Encode(ClassType, TagType, Tag, nil, Description)
 
- 	p.Value = Value
 
- 	p.Data.Write(encodeInteger(intValue))
 
- 	return p
 
- }
 
- func NewInteger(ClassType Class, TagType Type, Tag Tag, Value interface{}, Description string) *Packet {
 
- 	p := Encode(ClassType, TagType, Tag, nil, Description)
 
- 	p.Value = Value
 
- 	switch v := Value.(type) {
 
- 	case int:
 
- 		p.Data.Write(encodeInteger(int64(v)))
 
- 	case uint:
 
- 		p.Data.Write(encodeInteger(int64(v)))
 
- 	case int64:
 
- 		p.Data.Write(encodeInteger(v))
 
- 	case uint64:
 
- 		// TODO : check range or add encodeUInt...
 
- 		p.Data.Write(encodeInteger(int64(v)))
 
- 	case int32:
 
- 		p.Data.Write(encodeInteger(int64(v)))
 
- 	case uint32:
 
- 		p.Data.Write(encodeInteger(int64(v)))
 
- 	case int16:
 
- 		p.Data.Write(encodeInteger(int64(v)))
 
- 	case uint16:
 
- 		p.Data.Write(encodeInteger(int64(v)))
 
- 	case int8:
 
- 		p.Data.Write(encodeInteger(int64(v)))
 
- 	case uint8:
 
- 		p.Data.Write(encodeInteger(int64(v)))
 
- 	default:
 
- 		// TODO : add support for big.Int ?
 
- 		panic(fmt.Sprintf("Invalid type %T, expected {u|}int{64|32|16|8}", v))
 
- 	}
 
- 	return p
 
- }
 
- func NewString(ClassType Class, TagType Type, Tag Tag, Value, Description string) *Packet {
 
- 	p := Encode(ClassType, TagType, Tag, nil, Description)
 
- 	p.Value = Value
 
- 	p.Data.Write([]byte(Value))
 
- 	return p
 
- }
 
 
  |