diff options
author | Quentin Dufour <quentin@deuxfleurs.fr> | 2021-07-07 01:49:33 +0200 |
---|---|---|
committer | Quentin Dufour <quentin@deuxfleurs.fr> | 2021-09-16 13:09:26 +0200 |
commit | 563fc272a36c8be317fbe95c8308ca2dfa29c3aa (patch) | |
tree | 0b6f9a6a15516e7234fc928ecbebbd32d3154074 /goldap/asn1.go | |
parent | aa912b5ceb24cb8772709171ea9589b0771bbe54 (diff) | |
download | bottin-563fc272a36c8be317fbe95c8308ca2dfa29c3aa.tar.gz bottin-563fc272a36c8be317fbe95c8308ca2dfa29c3aa.zip |
Vendor goldap, fix ASN.1 BER integer and length encoding
- Add tests for goldap to prevent regressions
- Disable reconnection for our functional tests
Diffstat (limited to 'goldap/asn1.go')
-rw-r--r-- | goldap/asn1.go | 761 |
1 files changed, 761 insertions, 0 deletions
diff --git a/goldap/asn1.go b/goldap/asn1.go new file mode 100644 index 0000000..3e7dc76 --- /dev/null +++ b/goldap/asn1.go @@ -0,0 +1,761 @@ +package message + +// Below code is largely inspired from the standard golang library encoding/asn +// If put BEGIN / END tags in the comments to give the original library name +import ( + // "errors" + "fmt" + "math/big" + // "strconv" + // "time" +) + +// +// BEGIN: encoding/asn1/common.go +// + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +const ( + tagBoolean = 1 + tagInteger = 2 + // tagBitString = 3 + tagOctetString = 4 + // tagOID = 6 + tagEnum = 10 + // tagUTF8String = 12 + tagSequence = 16 + tagSet = 17 + // tagPrintableString = 19 + // tagT61String = 20 + // tagIA5String = 22 + // tagUTCTime = 23 + // tagGeneralizedTime = 24 + tagGeneralString = 27 +) + +var tagNames = map[int]string{ + tagBoolean: "BOOLEAN", + tagInteger: "INTEGER", + tagOctetString: "OCTET STRING", + tagEnum: "ENUM", + tagSequence: "SEQUENCE", + tagSet: "SET", +} + +const ( + classUniversal = 0 + classApplication = 1 + classContextSpecific = 2 + // classPrivate = 3 +) + +var classNames = map[int]string{ + classUniversal: "UNIVERSAL", + classApplication: "APPLICATION", + classContextSpecific: "CONTEXT SPECIFIC", +} + +const ( + isCompound = true + isNotCompound = false +) + +var compoundNames = map[bool]string{ + isCompound: "COMPOUND", + isNotCompound: "NOT COMPOUND", +} + +type TagAndLength struct { + Class, Tag, Length int + IsCompound bool +} + +// +// END: encoding/asn1/common.go +// + +func (t *TagAndLength) Expect(class int, tag int, isCompound bool) (err error) { + err = t.ExpectClass(class) + if err != nil { + return LdapError{fmt.Sprintf("Expect: %s.", err)} + } + err = t.ExpectTag(tag) + if err != nil { + return LdapError{fmt.Sprintf("Expect: %s.", err)} + } + err = t.ExpectCompound(isCompound) + if err != nil { + return LdapError{fmt.Sprintf("Expect: %s.", err)} + } + return +} +func (t *TagAndLength) ExpectClass(class int) (err error) { + if class != t.Class { + err = SyntaxError{fmt.Sprintf("ExpectClass: wrong tag class: got %d (%s), expected %d (%s)", t.Class, classNames[t.Class], class, classNames[class])} + } + return +} +func (t *TagAndLength) ExpectTag(tag int) (err error) { + if tag != t.Tag { + err = SyntaxError{fmt.Sprintf("ExpectTag: wrong tag value: got %d (%s), expected %d (%s)", t.Tag, tagNames[t.Tag], tag, tagNames[tag])} + } + return +} +func (t *TagAndLength) ExpectCompound(isCompound bool) (err error) { + if isCompound != t.IsCompound { + err = SyntaxError{fmt.Sprintf("ExpectCompound: wrong tag compound: got %t (%s), expected %t (%s)", t.IsCompound, compoundNames[t.IsCompound], isCompound, compoundNames[isCompound])} + } + return +} + +func ParseTagAndLength(bytes []byte, initOffset int) (ret TagAndLength, offset int, err error) { + ret, offset, err = parseTagAndLength(bytes, initOffset) + return +} + +// +// BEGIN encoding/asn1/asn1.go +// + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package asn1 implements parsing of DER-encoded ASN.1 data structures, +// as defined in ITU-T Rec X.690. +// +// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' +// http://luca.ntop.org/Teaching/Appunti/asn1.html. +// package asn1 + +// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc +// are different encoding formats for those objects. Here, we'll be dealing +// with DER, the Distinguished Encoding Rules. DER is used in X.509 because +// it's fast to parse and, unlike BER, has a unique encoding for every object. +// When calculating hashes over objects, it's important that the resulting +// bytes be the same at both ends and DER removes this margin of error. +// +// ASN.1 is very complex and this package doesn't attempt to implement +// everything by any means. + +//import ( +// "fmt" +// "math/big" +// "reflect" +// "strconv" +// "time" +//) + +// A StructuralError suggests that the ASN.1 data is valid, but the Go type +// which is receiving it doesn't match. +type StructuralError struct { + Msg string +} + +func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg } + +// A SyntaxError suggests that the ASN.1 data is invalid. +type SyntaxError struct { + Msg string +} + +func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg } + +// We start by dealing with each of the primitive types in turn. + +// BOOLEAN + +func parseBool(bytes []byte) (ret bool, err error) { + if len(bytes) > 1 { + err = SyntaxError{"invalid boolean: should be encoded on one byte only"} + return + } else if len(bytes) == 0 { + err = SyntaxError{"invalid boolean: no data to read"} + } + + // DER demands that "If the encoding represents the boolean value TRUE, + // its single contents octet shall have all eight bits set to one." + // Thus only 0 and 255 are valid encoded values. + switch bytes[0] { + case 0: + ret = false + case 0xff: + ret = true + default: + err = SyntaxError{"invalid boolean: should be 0x00 of 0xFF"} + } + + return +} + +func sizeBool(b bool) int { + return 1 +} + +func writeBool(bytes *Bytes, b bool) int { + if b == false { + return bytes.writeBytes([]byte{0x00}) + } else { + return bytes.writeBytes([]byte{0xff}) + } +} + +// INTEGER + +// parseInt64 treats the given bytes as a big-endian, signed integer and +// returns the result. +func parseInt64(bytes []byte) (ret int64, err error) { + if len(bytes) > 8 { + // We'll overflow an int64 in this case. + err = StructuralError{"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 sizeInt64(i int64) (size int) { + n := 1 + + for i > 127 { + n++ + i >>= 8 + } + + for i < -128 { + n++ + i >>= 8 + } + + return n +} + +func writeInt64(bytes *Bytes, i int64) (size int) { + n := sizeInt64(i) + buf := [8]byte{} + + for j := 0; j < n; j++ { + b := i >> uint((n-1-j)*8) + buf[j] = byte(b) + } + bytes.writeBytes(buf[:n]) + + return n +} + +// parseInt treats the given bytes as a big-endian, signed integer and returns +// the result. +func parseInt32(bytes []byte) (int32, error) { + ret64, err := parseInt64(bytes) + if err != nil { + return 0, err + } + if ret64 != int64(int32(ret64)) { + return 0, StructuralError{"integer too large"} + } + return int32(ret64), nil +} + +func sizeInt32(i int32) int { + return sizeInt64(int64(i)) +} + +func writeInt32(bytes *Bytes, i int32) int { + return writeInt64(bytes, int64(i)) +} + +var bigOne = big.NewInt(1) + +// // parseBigInt treats the given bytes as a big-endian, signed integer and returns +// // the result. +// func parseBigInt(bytes []byte) *big.Int { +// ret := new(big.Int) +// if len(bytes) > 0 && bytes[0]&0x80 == 0x80 { +// // This is a negative number. +// notBytes := make([]byte, len(bytes)) +// for i := range notBytes { +// notBytes[i] = ^bytes[i] +// } +// ret.SetBytes(notBytes) +// ret.Add(ret, bigOne) +// ret.Neg(ret) +// return ret +// } +// ret.SetBytes(bytes) +// return ret +// } + +// // BIT STRING + +// // BitString is the structure to use when you want an ASN.1 BIT STRING type. A +// // bit string is padded up to the nearest byte in memory and the number of +// // valid bits is recorded. Padding bits will be zero. +// type BitString struct { +// Bytes []byte // bits packed into bytes. +// BitLength int // length in bits. +// } + +// // At returns the bit at the given index. If the index is out of range it +// // returns false. +// func (b BitString) At(i int) int { +// if i < 0 || i >= b.BitLength { +// return 0 +// } +// x := i / 8 +// y := 7 - uint(i%8) +// return int(b.Bytes[x]>>y) & 1 +// } + +// // RightAlign returns a slice where the padding bits are at the beginning. The +// // slice may share memory with the BitString. +// func (b BitString) RightAlign() []byte { +// shift := uint(8 - (b.BitLength % 8)) +// if shift == 8 || len(b.Bytes) == 0 { +// return b.Bytes +// } + +// a := make([]byte, len(b.Bytes)) +// a[0] = b.Bytes[0] >> shift +// for i := 1; i < len(b.Bytes); i++ { +// a[i] = b.Bytes[i-1] << (8 - shift) +// a[i] |= b.Bytes[i] >> shift +// } + +// return a +// } + +// // parseBitString parses an ASN.1 bit string from the given byte slice and returns it. +// func parseBitString(bytes []byte) (ret BitString, err error) { +// if len(bytes) == 0 { +// err = SyntaxError{"zero length BIT STRING"} +// return +// } +// paddingBits := int(bytes[0]) +// if paddingBits > 7 || +// len(bytes) == 1 && paddingBits > 0 || +// bytes[len(bytes)-1]&((1<<bytes[0])-1) != 0 { +// err = SyntaxError{"invalid padding bits in BIT STRING"} +// return +// } +// ret.BitLength = (len(bytes)-1)*8 - paddingBits +// ret.Bytes = bytes[1:] +// return +// } + +// OBJECT IDENTIFIER + +// An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER. +// type ObjectIdentifier []int + +// // Equal reports whether oi and other represent the same identifier. +// func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool { +// if len(oi) != len(other) { +// return false +// } +// for i := 0; i < len(oi); i++ { +// if oi[i] != other[i] { +// return false +// } +// } + +// return true +// } + +// func (oi ObjectIdentifier) String() string { +// var s string + +// for i, v := range oi { +// if i > 0 { +// s += "." +// } +// s += strconv.Itoa(v) +// } + +// return s +// } + +// // parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and +// // returns it. An object identifier is a sequence of variable length integers +// // that are assigned in a hierarchy. +// func parseObjectIdentifier(bytes []byte) (s []int, err error) { +// if len(bytes) == 0 { +// err = SyntaxError{"zero length OBJECT IDENTIFIER"} +// return +// } + +// // In the worst case, we get two elements from the first byte (which is +// // encoded differently) and then every varint is a single byte long. +// s = make([]int, len(bytes)+1) + +// // The first varint is 40*value1 + value2: +// // According to this packing, value1 can take the values 0, 1 and 2 only. +// // When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2, +// // then there are no restrictions on value2. +// v, offset, err := parseBase128Int(bytes, 0) +// if err != nil { +// return +// } +// if v < 80 { +// s[0] = v / 40 +// s[1] = v % 40 +// } else { +// s[0] = 2 +// s[1] = v - 80 +// } + +// i := 2 +// for ; offset < len(bytes); i++ { +// v, offset, err = parseBase128Int(bytes, offset) +// if err != nil { +// return +// } +// s[i] = v +// } +// s = s[0:i] +// return +// } + +// ENUMERATED + +// An Enumerated is represented as a plain int. +type Enumerated int + +// FLAG + +// A Flag accepts any data and is set to true if present. +type Flag bool + +// parseBase128Int parses a base-128 encoded int from the given offset in the +// given byte slice. It returns the value and the new offset. +func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) { + offset = initOffset + for shifted := 0; offset < len(bytes); shifted++ { + if shifted > 4 { + err = StructuralError{"base 128 integer too large"} + return + } + ret <<= 7 + b := bytes[offset] + ret |= int(b & 0x7f) + offset++ + if b&0x80 == 0 { + return + } + } + err = SyntaxError{"truncated base 128 integer"} + return +} + +func sizeBase128Int(value int) (size int) { + for i := value; i > 0; i >>= 7 { + size++ + } + return +} + +// Write start as the end of the slice and goes back +// We assume we have enough size +func writeBase128Int(bytes *Bytes, value int) (size int) { + for ; value > 0 || size == 0; value >>= 7 { // Write at least one byte even if the value is 0 + // Get the 7 lowest bits + b := byte(value) & 0x7f + if value < 128 { + b |= 0x80 + } + bytes.writeBytes([]byte{b}) + size++ + } + return +} + +// // UTCTime + +// func parseUTCTime(bytes []byte) (ret time.Time, err error) { +// s := string(bytes) +// ret, err = time.Parse("0601021504Z0700", s) +// if err != nil { +// ret, err = time.Parse("060102150405Z0700", s) +// } +// if err == nil && ret.Year() >= 2050 { +// // UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 +// ret = ret.AddDate(-100, 0, 0) +// } + +// return +// } + +// // parseGeneralizedTime parses the GeneralizedTime from the given byte slice +// // and returns the resulting time. +// func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) { +// return time.Parse("20060102150405Z0700", string(bytes)) +// } + +// // PrintableString + +// // parsePrintableString parses a ASN.1 PrintableString from the given byte +// // array and returns it. +// func parsePrintableString(bytes []byte) (ret string, err error) { +// for _, b := range bytes { +// if !isPrintable(b) { +// err = SyntaxError{"PrintableString contains invalid character"} +// return +// } +// } +// ret = string(bytes) +// return +// } + +// // isPrintable returns true iff the given b is in the ASN.1 PrintableString set. +// func isPrintable(b byte) bool { +// return 'a' <= b && b <= 'z' || +// 'A' <= b && b <= 'Z' || +// '0' <= b && b <= '9' || +// '\'' <= b && b <= ')' || +// '+' <= b && b <= '/' || +// b == ' ' || +// b == ':' || +// b == '=' || +// b == '?' || +// // This is technically not allowed in a PrintableString. +// // However, x509 certificates with wildcard strings don't +// // always use the correct string type so we permit it. +// b == '*' +// } + +// // IA5String + +// // parseIA5String parses a ASN.1 IA5String (ASCII string) from the given +// // byte slice and returns it. +// func parseIA5String(bytes []byte) (ret string, err error) { +// for _, b := range bytes { +// if b >= 0x80 { +// err = SyntaxError{"IA5String contains invalid character"} +// return +// } +// } +// ret = string(bytes) +// return +// } + +// // T61String + +// // parseT61String parses a ASN.1 T61String (8-bit clean string) from the given +// // byte slice and returns it. +// func parseT61String(bytes []byte) (ret string, err error) { +// return string(bytes), nil +// } + +// UTF8String + +// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte +// array and returns it. +// func parseUTF8String(bytes []byte) (ret string, err error) { +// return string(bytes), nil +// } +// func sizeUTF8String(s string) int { +// return len(s) +// } +// func writeUTF8String(bytes *Bytes, s string) int { +// return bytes.writeString(s) +// } + +// Octet string +func parseOctetString(bytes []byte) (ret []byte, err error) { + return bytes, nil +} +func sizeOctetString(s []byte) int { + return len(s) +} +func writeOctetString(bytes *Bytes, s []byte) int { + return bytes.writeBytes(s) +} + +// A RawValue represents an undecoded ASN.1 object. +type RawValue struct { + Class, Tag int + IsCompound bool + Bytes []byte + FullBytes []byte // includes the tag and length +} + +// RawContent is used to signal that the undecoded, DER data needs to be +// preserved for a struct. To use it, the first field of the struct must have +// this type. It's an error for any of the other fields to have this type. +type RawContent []byte + +// Tagging + +// parseTagAndLength parses an ASN.1 tag and length pair from the given offset +// into a byte slice. It returns the parsed data and the new offset. SET and +// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we +// don't distinguish between ordered and unordered objects in this code. +func parseTagAndLength(bytes []byte, initOffset int) (ret TagAndLength, offset int, err error) { + offset = initOffset + b := bytes[offset] + offset++ + ret.Class = int(b >> 6) + ret.IsCompound = b&0x20 == 0x20 + ret.Tag = int(b & 0x1f) + + // If the bottom five bits are set, then the tag number is actually base 128 + // encoded afterwards + if ret.Tag == 0x1f { + ret.Tag, offset, err = parseBase128Int(bytes, offset) + if err != nil { + return + } + } + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + if b&0x80 == 0 { + // The length is encoded in the bottom 7 bits. + ret.Length = int(b & 0x7f) + } else { + // Bottom 7 bits give the number of length bytes to follow. + numBytes := int(b & 0x7f) + if numBytes == 0 { + err = SyntaxError{"indefinite length found (not DER)"} + return + } + ret.Length = 0 + for i := 0; i < numBytes; i++ { + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + if ret.Length >= 1<<23 { + // We can't shift ret.length up without + // overflowing. + err = StructuralError{"length too large"} + return + } + ret.Length <<= 8 + ret.Length |= int(b) + if ret.Length == 0 { + // DER requires that lengths be minimal. + err = StructuralError{"superfluous leading zeros in length"} + return + } + } + } + + return +} + +// func writeTagAndLength(out *forkableWriter, t tagAndLength) (err error) { +// b := uint8(t.class) << 6 +// if t.isCompound { +// b |= 0x20 +// } +// if t.tag >= 31 { +// b |= 0x1f +// err = out.WriteByte(b) +// if err != nil { +// return +// } +// err = marshalBase128Int(out, int64(t.tag)) +// if err != nil { +// return +// } +// } else { +// b |= uint8(t.tag) +// err = out.WriteByte(b) +// if err != nil { +// return +// } +// } + +// if t.length >= 128 { +// l := lengthLength(t.length) +// err = out.WriteByte(0x80 | byte(l)) +// if err != nil { +// return +// } +// err = marshalLength(out, t.length) +// if err != nil { +// return +// } +// } else { +// err = out.WriteByte(byte(t.length)) +// if err != nil { +// return +// } +// } + +// return nil +// } + +func sizeTagAndLength(tag int, length int) (size int) { + // Compute the size of the tag + size = 1 + if tag >= 31 { + // Long-form identifier if the tag is greater than 30 + // http://en.wikipedia.org/wiki/X.690#Identifier_tags_greater_than_30 + size += sizeBase128Int(tag) + } + // Compute the size of the length using the definite form + // http://en.wikipedia.org/wiki/X.690#The_definite_form + size += 1 + if length >= 128 { + size += 1 + for length > 255 { + size++ + length >>= 8 + } + } + return +} + +func writeTagAndLength(bytes *Bytes, t TagAndLength) (size int) { + // We are writing backward, so write the length bytes first + if t.Length < 0 { + panic("Can't have a negative length") + + } else if t.Length >= 128 { + //lengthBytes := writeInt64(bytes, int64(t.Length)) + lengthBytes := 0 + val := t.Length + for val > 0 { + lengthBytes++ + bytes.writeBytes([]byte{byte(val & 0xff)}) + val >>= 8 + } + bytes.writeBytes([]byte{byte(0x80 | byte(lengthBytes))}) + size += lengthBytes + 1 + + } else if t.Length < 128 { + size += bytes.writeBytes([]byte{byte(t.Length)}) + } + // Then write the tag + b := uint8(t.Class) << 6 + if t.IsCompound { + b |= 0x20 + } + if t.Tag >= 31 { + b |= 0x1f + size += writeBase128Int(bytes, t.Tag) + } else { + b |= uint8(t.Tag) + } + size += bytes.writeBytes([]byte{byte(b)}) + return +} + +// +// END encoding/asn1/asn1.go +// |