summaryrefslogtreecommitdiffstats
path: root/src/encoding/asn1
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:16:40 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:16:40 +0000
commit47ab3d4a42e9ab51c465c4322d2ec233f6324e6b (patch)
treea61a0ffd83f4a3def4b36e5c8e99630c559aa723 /src/encoding/asn1
parentInitial commit. (diff)
downloadgolang-1.18-47ab3d4a42e9ab51c465c4322d2ec233f6324e6b.tar.xz
golang-1.18-47ab3d4a42e9ab51c465c4322d2ec233f6324e6b.zip
Adding upstream version 1.18.10.upstream/1.18.10upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/encoding/asn1')
-rw-r--r--src/encoding/asn1/asn1.go1122
-rw-r--r--src/encoding/asn1/asn1_test.go1166
-rw-r--r--src/encoding/asn1/common.go185
-rw-r--r--src/encoding/asn1/marshal.go747
-rw-r--r--src/encoding/asn1/marshal_test.go406
5 files changed, 3626 insertions, 0 deletions
diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go
new file mode 100644
index 0000000..cad1d7b
--- /dev/null
+++ b/src/encoding/asn1/asn1.go
@@ -0,0 +1,1122 @@
+// 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 (
+ "errors"
+ "fmt"
+ "math"
+ "math/big"
+ "reflect"
+ "strconv"
+ "time"
+ "unicode/utf16"
+ "unicode/utf8"
+)
+
+// 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"}
+ return
+ }
+
+ // 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"}
+ }
+
+ return
+}
+
+// INTEGER
+
+// checkInteger returns nil if the given bytes are a valid DER-encoded
+// INTEGER and an error otherwise.
+func checkInteger(bytes []byte) error {
+ if len(bytes) == 0 {
+ return StructuralError{"empty integer"}
+ }
+ if len(bytes) == 1 {
+ return nil
+ }
+ if (bytes[0] == 0 && bytes[1]&0x80 == 0) || (bytes[0] == 0xff && bytes[1]&0x80 == 0x80) {
+ return StructuralError{"integer not minimally-encoded"}
+ }
+ return nil
+}
+
+// parseInt64 treats the given bytes as a big-endian, signed integer and
+// returns the result.
+func parseInt64(bytes []byte) (ret int64, err error) {
+ err = checkInteger(bytes)
+ if err != nil {
+ return
+ }
+ 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
+}
+
+// parseInt treats the given bytes as a big-endian, signed integer and returns
+// the result.
+func parseInt32(bytes []byte) (int32, error) {
+ if err := checkInteger(bytes); err != nil {
+ return 0, err
+ }
+ 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
+}
+
+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, error) {
+ if err := checkInteger(bytes); err != nil {
+ return nil, err
+ }
+ 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, nil
+ }
+ ret.SetBytes(bytes)
+ return ret, nil
+}
+
+// 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
+}
+
+// NULL
+
+// NullRawValue is a RawValue with its Tag set to the ASN.1 NULL type tag (5).
+var NullRawValue = RawValue{Tag: TagNull}
+
+// NullBytes contains bytes representing the DER-encoded ASN.1 NULL type.
+var NullBytes = []byte{TagNull, 0}
+
+// 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 ObjectIdentifier, 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
+ var ret64 int64
+ for shifted := 0; offset < len(bytes); shifted++ {
+ // 5 * 7 bits per byte == 35 bits of data
+ // Thus the representation is either non-minimal or too large for an int32
+ if shifted == 5 {
+ err = StructuralError{"base 128 integer too large"}
+ return
+ }
+ ret64 <<= 7
+ b := bytes[offset]
+ // integers should be minimally encoded, so the leading octet should
+ // never be 0x80
+ if shifted == 0 && b == 0x80 {
+ err = SyntaxError{"integer is not minimally encoded"}
+ return
+ }
+ ret64 |= int64(b & 0x7f)
+ offset++
+ if b&0x80 == 0 {
+ ret = int(ret64)
+ // Ensure that the returned value fits in an int on all platforms
+ if ret64 > math.MaxInt32 {
+ err = StructuralError{"base 128 integer too large"}
+ }
+ return
+ }
+ }
+ err = SyntaxError{"truncated base 128 integer"}
+ return
+}
+
+// UTCTime
+
+func parseUTCTime(bytes []byte) (ret time.Time, err error) {
+ s := string(bytes)
+
+ formatStr := "0601021504Z0700"
+ ret, err = time.Parse(formatStr, s)
+ if err != nil {
+ formatStr = "060102150405Z0700"
+ ret, err = time.Parse(formatStr, s)
+ }
+ if err != nil {
+ return
+ }
+
+ if serialized := ret.Format(formatStr); serialized != s {
+ err = fmt.Errorf("asn1: time did not serialize back to the original value and may be invalid: given %q, but serialized as %q", s, serialized)
+ return
+ }
+
+ if 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) {
+ const formatStr = "20060102150405Z0700"
+ s := string(bytes)
+
+ if ret, err = time.Parse(formatStr, s); err != nil {
+ return
+ }
+
+ if serialized := ret.Format(formatStr); serialized != s {
+ err = fmt.Errorf("asn1: time did not serialize back to the original value and may be invalid: given %q, but serialized as %q", s, serialized)
+ }
+
+ return
+}
+
+// NumericString
+
+// parseNumericString parses an ASN.1 NumericString from the given byte array
+// and returns it.
+func parseNumericString(bytes []byte) (ret string, err error) {
+ for _, b := range bytes {
+ if !isNumeric(b) {
+ return "", SyntaxError{"NumericString contains invalid character"}
+ }
+ }
+ return string(bytes), nil
+}
+
+// isNumeric reports whether the given b is in the ASN.1 NumericString set.
+func isNumeric(b byte) bool {
+ return '0' <= b && b <= '9' ||
+ b == ' '
+}
+
+// PrintableString
+
+// parsePrintableString parses an 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, allowAsterisk, allowAmpersand) {
+ err = SyntaxError{"PrintableString contains invalid character"}
+ return
+ }
+ }
+ ret = string(bytes)
+ return
+}
+
+type asteriskFlag bool
+type ampersandFlag bool
+
+const (
+ allowAsterisk asteriskFlag = true
+ rejectAsterisk asteriskFlag = false
+
+ allowAmpersand ampersandFlag = true
+ rejectAmpersand ampersandFlag = false
+)
+
+// isPrintable reports whether the given b is in the ASN.1 PrintableString set.
+// If asterisk is allowAsterisk then '*' is also allowed, reflecting existing
+// practice. If ampersand is allowAmpersand then '&' is allowed as well.
+func isPrintable(b byte, asterisk asteriskFlag, ampersand ampersandFlag) 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.
+ (bool(asterisk) && b == '*') ||
+ // This is not technically allowed either. However, not
+ // only is it relatively common, but there are also a
+ // handful of CA certificates that contain it. At least
+ // one of which will not expire until 2027.
+ (bool(ampersand) && b == '&')
+}
+
+// IA5String
+
+// parseIA5String parses an 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 >= utf8.RuneSelf {
+ err = SyntaxError{"IA5String contains invalid character"}
+ return
+ }
+ }
+ ret = string(bytes)
+ return
+}
+
+// T61String
+
+// parseT61String parses an 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 an ASN.1 UTF8String (raw UTF-8) from the given byte
+// array and returns it.
+func parseUTF8String(bytes []byte) (ret string, err error) {
+ if !utf8.Valid(bytes) {
+ return "", errors.New("asn1: invalid UTF-8 string")
+ }
+ return string(bytes), nil
+}
+
+// BMPString
+
+// parseBMPString parses an ASN.1 BMPString (Basic Multilingual Plane of
+// ISO/IEC/ITU 10646-1) from the given byte slice and returns it.
+func parseBMPString(bmpString []byte) (string, error) {
+ if len(bmpString)%2 != 0 {
+ return "", errors.New("pkcs12: odd-length BMP string")
+ }
+
+ // Strip terminator if present.
+ if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 {
+ bmpString = bmpString[:l-2]
+ }
+
+ s := make([]uint16, 0, len(bmpString)/2)
+ for len(bmpString) > 0 {
+ s = append(s, uint16(bmpString[0])<<8+uint16(bmpString[1]))
+ bmpString = bmpString[2:]
+ }
+
+ return string(utf16.Decode(s)), nil
+}
+
+// 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
+ // parseTagAndLength should not be called without at least a single
+ // byte to read. Thus this check is for robustness:
+ if offset >= len(bytes) {
+ err = errors.New("asn1: internal error in parseTagAndLength")
+ return
+ }
+ 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
+ }
+ // Tags should be encoded in minimal form.
+ if ret.tag < 0x1f {
+ err = SyntaxError{"non-minimal tag"}
+ 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
+ }
+ }
+ // Short lengths must be encoded in short form.
+ if ret.length < 0x80 {
+ err = StructuralError{"non-minimal length"}
+ return
+ }
+ }
+
+ return
+}
+
+// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse
+// a number of ASN.1 values from the given byte slice and returns them as a
+// slice of Go values of the given type.
+func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err error) {
+ matchAny, expectedTag, compoundType, ok := getUniversalType(elemType)
+ if !ok {
+ err = StructuralError{"unknown Go type for slice"}
+ return
+ }
+
+ // First we iterate over the input and count the number of elements,
+ // checking that the types are correct in each case.
+ numElements := 0
+ for offset := 0; offset < len(bytes); {
+ var t tagAndLength
+ t, offset, err = parseTagAndLength(bytes, offset)
+ if err != nil {
+ return
+ }
+ switch t.tag {
+ case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString, TagBMPString:
+ // We pretend that various other string types are
+ // PRINTABLE STRINGs so that a sequence of them can be
+ // parsed into a []string.
+ t.tag = TagPrintableString
+ case TagGeneralizedTime, TagUTCTime:
+ // Likewise, both time types are treated the same.
+ t.tag = TagUTCTime
+ }
+
+ if !matchAny && (t.class != ClassUniversal || t.isCompound != compoundType || t.tag != expectedTag) {
+ err = StructuralError{"sequence tag mismatch"}
+ return
+ }
+ if invalidLength(offset, t.length, len(bytes)) {
+ err = SyntaxError{"truncated sequence"}
+ return
+ }
+ offset += t.length
+ numElements++
+ }
+ ret = reflect.MakeSlice(sliceType, numElements, numElements)
+ params := fieldParameters{}
+ offset := 0
+ for i := 0; i < numElements; i++ {
+ offset, err = parseField(ret.Index(i), bytes, offset, params)
+ if err != nil {
+ return
+ }
+ }
+ return
+}
+
+var (
+ bitStringType = reflect.TypeOf(BitString{})
+ objectIdentifierType = reflect.TypeOf(ObjectIdentifier{})
+ enumeratedType = reflect.TypeOf(Enumerated(0))
+ flagType = reflect.TypeOf(Flag(false))
+ timeType = reflect.TypeOf(time.Time{})
+ rawValueType = reflect.TypeOf(RawValue{})
+ rawContentsType = reflect.TypeOf(RawContent(nil))
+ bigIntType = reflect.TypeOf(new(big.Int))
+)
+
+// invalidLength reports whether offset + length > sliceLength, or if the
+// addition would overflow.
+func invalidLength(offset, length, sliceLength int) bool {
+ return offset+length < offset || offset+length > sliceLength
+}
+
+// parseField is the main parsing function. Given a byte slice and an offset
+// into the array, it will try to parse a suitable ASN.1 value out and store it
+// in the given Value.
+func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err error) {
+ offset = initOffset
+ fieldType := v.Type()
+
+ // If we have run out of data, it may be that there are optional elements at the end.
+ if offset == len(bytes) {
+ if !setDefaultValue(v, params) {
+ err = SyntaxError{"sequence truncated"}
+ }
+ return
+ }
+
+ // Deal with the ANY type.
+ if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 {
+ var t tagAndLength
+ t, offset, err = parseTagAndLength(bytes, offset)
+ if err != nil {
+ return
+ }
+ if invalidLength(offset, t.length, len(bytes)) {
+ err = SyntaxError{"data truncated"}
+ return
+ }
+ var result any
+ if !t.isCompound && t.class == ClassUniversal {
+ innerBytes := bytes[offset : offset+t.length]
+ switch t.tag {
+ case TagPrintableString:
+ result, err = parsePrintableString(innerBytes)
+ case TagNumericString:
+ result, err = parseNumericString(innerBytes)
+ case TagIA5String:
+ result, err = parseIA5String(innerBytes)
+ case TagT61String:
+ result, err = parseT61String(innerBytes)
+ case TagUTF8String:
+ result, err = parseUTF8String(innerBytes)
+ case TagInteger:
+ result, err = parseInt64(innerBytes)
+ case TagBitString:
+ result, err = parseBitString(innerBytes)
+ case TagOID:
+ result, err = parseObjectIdentifier(innerBytes)
+ case TagUTCTime:
+ result, err = parseUTCTime(innerBytes)
+ case TagGeneralizedTime:
+ result, err = parseGeneralizedTime(innerBytes)
+ case TagOctetString:
+ result = innerBytes
+ case TagBMPString:
+ result, err = parseBMPString(innerBytes)
+ default:
+ // If we don't know how to handle the type, we just leave Value as nil.
+ }
+ }
+ offset += t.length
+ if err != nil {
+ return
+ }
+ if result != nil {
+ v.Set(reflect.ValueOf(result))
+ }
+ return
+ }
+
+ t, offset, err := parseTagAndLength(bytes, offset)
+ if err != nil {
+ return
+ }
+ if params.explicit {
+ expectedClass := ClassContextSpecific
+ if params.application {
+ expectedClass = ClassApplication
+ }
+ if offset == len(bytes) {
+ err = StructuralError{"explicit tag has no child"}
+ return
+ }
+ if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) {
+ if fieldType == rawValueType {
+ // The inner element should not be parsed for RawValues.
+ } else if t.length > 0 {
+ t, offset, err = parseTagAndLength(bytes, offset)
+ if err != nil {
+ return
+ }
+ } else {
+ if fieldType != flagType {
+ err = StructuralError{"zero length explicit tag was not an asn1.Flag"}
+ return
+ }
+ v.SetBool(true)
+ return
+ }
+ } else {
+ // The tags didn't match, it might be an optional element.
+ ok := setDefaultValue(v, params)
+ if ok {
+ offset = initOffset
+ } else {
+ err = StructuralError{"explicitly tagged member didn't match"}
+ }
+ return
+ }
+ }
+
+ matchAny, universalTag, compoundType, ok1 := getUniversalType(fieldType)
+ if !ok1 {
+ err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)}
+ return
+ }
+
+ // Special case for strings: all the ASN.1 string types map to the Go
+ // type string. getUniversalType returns the tag for PrintableString
+ // when it sees a string, so if we see a different string type on the
+ // wire, we change the universal type to match.
+ if universalTag == TagPrintableString {
+ if t.class == ClassUniversal {
+ switch t.tag {
+ case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString, TagBMPString:
+ universalTag = t.tag
+ }
+ } else if params.stringType != 0 {
+ universalTag = params.stringType
+ }
+ }
+
+ // Special case for time: UTCTime and GeneralizedTime both map to the
+ // Go type time.Time.
+ if universalTag == TagUTCTime && t.tag == TagGeneralizedTime && t.class == ClassUniversal {
+ universalTag = TagGeneralizedTime
+ }
+
+ if params.set {
+ universalTag = TagSet
+ }
+
+ matchAnyClassAndTag := matchAny
+ expectedClass := ClassUniversal
+ expectedTag := universalTag
+
+ if !params.explicit && params.tag != nil {
+ expectedClass = ClassContextSpecific
+ expectedTag = *params.tag
+ matchAnyClassAndTag = false
+ }
+
+ if !params.explicit && params.application && params.tag != nil {
+ expectedClass = ClassApplication
+ expectedTag = *params.tag
+ matchAnyClassAndTag = false
+ }
+
+ if !params.explicit && params.private && params.tag != nil {
+ expectedClass = ClassPrivate
+ expectedTag = *params.tag
+ matchAnyClassAndTag = false
+ }
+
+ // We have unwrapped any explicit tagging at this point.
+ if !matchAnyClassAndTag && (t.class != expectedClass || t.tag != expectedTag) ||
+ (!matchAny && t.isCompound != compoundType) {
+ // Tags don't match. Again, it could be an optional element.
+ ok := setDefaultValue(v, params)
+ if ok {
+ offset = initOffset
+ } else {
+ err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)}
+ }
+ return
+ }
+ if invalidLength(offset, t.length, len(bytes)) {
+ err = SyntaxError{"data truncated"}
+ return
+ }
+ innerBytes := bytes[offset : offset+t.length]
+ offset += t.length
+
+ // We deal with the structures defined in this package first.
+ switch v := v.Addr().Interface().(type) {
+ case *RawValue:
+ *v = RawValue{t.class, t.tag, t.isCompound, innerBytes, bytes[initOffset:offset]}
+ return
+ case *ObjectIdentifier:
+ *v, err = parseObjectIdentifier(innerBytes)
+ return
+ case *BitString:
+ *v, err = parseBitString(innerBytes)
+ return
+ case *time.Time:
+ if universalTag == TagUTCTime {
+ *v, err = parseUTCTime(innerBytes)
+ return
+ }
+ *v, err = parseGeneralizedTime(innerBytes)
+ return
+ case *Enumerated:
+ parsedInt, err1 := parseInt32(innerBytes)
+ if err1 == nil {
+ *v = Enumerated(parsedInt)
+ }
+ err = err1
+ return
+ case *Flag:
+ *v = true
+ return
+ case **big.Int:
+ parsedInt, err1 := parseBigInt(innerBytes)
+ if err1 == nil {
+ *v = parsedInt
+ }
+ err = err1
+ return
+ }
+ switch val := v; val.Kind() {
+ case reflect.Bool:
+ parsedBool, err1 := parseBool(innerBytes)
+ if err1 == nil {
+ val.SetBool(parsedBool)
+ }
+ err = err1
+ return
+ case reflect.Int, reflect.Int32, reflect.Int64:
+ if val.Type().Size() == 4 {
+ parsedInt, err1 := parseInt32(innerBytes)
+ if err1 == nil {
+ val.SetInt(int64(parsedInt))
+ }
+ err = err1
+ } else {
+ parsedInt, err1 := parseInt64(innerBytes)
+ if err1 == nil {
+ val.SetInt(parsedInt)
+ }
+ err = err1
+ }
+ return
+ // TODO(dfc) Add support for the remaining integer types
+ case reflect.Struct:
+ structType := fieldType
+
+ for i := 0; i < structType.NumField(); i++ {
+ if !structType.Field(i).IsExported() {
+ err = StructuralError{"struct contains unexported fields"}
+ return
+ }
+ }
+
+ if structType.NumField() > 0 &&
+ structType.Field(0).Type == rawContentsType {
+ bytes := bytes[initOffset:offset]
+ val.Field(0).Set(reflect.ValueOf(RawContent(bytes)))
+ }
+
+ innerOffset := 0
+ for i := 0; i < structType.NumField(); i++ {
+ field := structType.Field(i)
+ if i == 0 && field.Type == rawContentsType {
+ continue
+ }
+ innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag.Get("asn1")))
+ if err != nil {
+ return
+ }
+ }
+ // We allow extra bytes at the end of the SEQUENCE because
+ // adding elements to the end has been used in X.509 as the
+ // version numbers have increased.
+ return
+ case reflect.Slice:
+ sliceType := fieldType
+ if sliceType.Elem().Kind() == reflect.Uint8 {
+ val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes)))
+ reflect.Copy(val, reflect.ValueOf(innerBytes))
+ return
+ }
+ newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem())
+ if err1 == nil {
+ val.Set(newSlice)
+ }
+ err = err1
+ return
+ case reflect.String:
+ var v string
+ switch universalTag {
+ case TagPrintableString:
+ v, err = parsePrintableString(innerBytes)
+ case TagNumericString:
+ v, err = parseNumericString(innerBytes)
+ case TagIA5String:
+ v, err = parseIA5String(innerBytes)
+ case TagT61String:
+ v, err = parseT61String(innerBytes)
+ case TagUTF8String:
+ v, err = parseUTF8String(innerBytes)
+ case TagGeneralString:
+ // GeneralString is specified in ISO-2022/ECMA-35,
+ // A brief review suggests that it includes structures
+ // that allow the encoding to change midstring and
+ // such. We give up and pass it as an 8-bit string.
+ v, err = parseT61String(innerBytes)
+ case TagBMPString:
+ v, err = parseBMPString(innerBytes)
+
+ default:
+ err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)}
+ }
+ if err == nil {
+ val.SetString(v)
+ }
+ return
+ }
+ err = StructuralError{"unsupported: " + v.Type().String()}
+ return
+}
+
+// canHaveDefaultValue reports whether k is a Kind that we will set a default
+// value for. (A signed integer, essentially.)
+func canHaveDefaultValue(k reflect.Kind) bool {
+ switch k {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return true
+ }
+
+ return false
+}
+
+// setDefaultValue is used to install a default value, from a tag string, into
+// a Value. It is successful if the field was optional, even if a default value
+// wasn't provided or it failed to install it into the Value.
+func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
+ if !params.optional {
+ return
+ }
+ ok = true
+ if params.defaultValue == nil {
+ return
+ }
+ if canHaveDefaultValue(v.Kind()) {
+ v.SetInt(*params.defaultValue)
+ }
+ return
+}
+
+// Unmarshal parses the DER-encoded ASN.1 data structure b
+// and uses the reflect package to fill in an arbitrary value pointed at by val.
+// Because Unmarshal uses the reflect package, the structs
+// being written to must use upper case field names. If val
+// is nil or not a pointer, Unmarshal returns an error.
+//
+// After parsing b, any bytes that were leftover and not used to fill
+// val will be returned in rest. When parsing a SEQUENCE into a struct,
+// any trailing elements of the SEQUENCE that do not have matching
+// fields in val will not be included in rest, as these are considered
+// valid elements of the SEQUENCE and not trailing data.
+//
+// An ASN.1 INTEGER can be written to an int, int32, int64,
+// or *big.Int (from the math/big package).
+// If the encoded value does not fit in the Go type,
+// Unmarshal returns a parse error.
+//
+// An ASN.1 BIT STRING can be written to a BitString.
+//
+// An ASN.1 OCTET STRING can be written to a []byte.
+//
+// An ASN.1 OBJECT IDENTIFIER can be written to an
+// ObjectIdentifier.
+//
+// An ASN.1 ENUMERATED can be written to an Enumerated.
+//
+// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a time.Time.
+//
+// An ASN.1 PrintableString, IA5String, or NumericString can be written to a string.
+//
+// Any of the above ASN.1 values can be written to an interface{}.
+// The value stored in the interface has the corresponding Go type.
+// For integers, that type is int64.
+//
+// An ASN.1 SEQUENCE OF x or SET OF x can be written
+// to a slice if an x can be written to the slice's element type.
+//
+// An ASN.1 SEQUENCE or SET can be written to a struct
+// if each of the elements in the sequence can be
+// written to the corresponding element in the struct.
+//
+// The following tags on struct fields have special meaning to Unmarshal:
+//
+// application specifies that an APPLICATION tag is used
+// private specifies that a PRIVATE tag is used
+// default:x sets the default value for optional integer fields (only used if optional is also present)
+// explicit specifies that an additional, explicit tag wraps the implicit one
+// optional marks the field as ASN.1 OPTIONAL
+// set causes a SET, rather than a SEQUENCE type to be expected
+// tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC
+//
+// When decoding an ASN.1 value with an IMPLICIT tag into a string field,
+// Unmarshal will default to a PrintableString, which doesn't support
+// characters such as '@' and '&'. To force other encodings, use the following
+// tags:
+//
+// ia5 causes strings to be unmarshaled as ASN.1 IA5String values
+// numeric causes strings to be unmarshaled as ASN.1 NumericString values
+// utf8 causes strings to be unmarshaled as ASN.1 UTF8String values
+//
+// If the type of the first field of a structure is RawContent then the raw
+// ASN1 contents of the struct will be stored in it.
+//
+// If the name of a slice type ends with "SET" then it's treated as if
+// the "set" tag was set on it. This results in interpreting the type as a
+// SET OF x rather than a SEQUENCE OF x. This can be used with nested slices
+// where a struct tag cannot be given.
+//
+// Other ASN.1 types are not supported; if it encounters them,
+// Unmarshal returns a parse error.
+func Unmarshal(b []byte, val any) (rest []byte, err error) {
+ return UnmarshalWithParams(b, val, "")
+}
+
+// An invalidUnmarshalError describes an invalid argument passed to Unmarshal.
+// (The argument to Unmarshal must be a non-nil pointer.)
+type invalidUnmarshalError struct {
+ Type reflect.Type
+}
+
+func (e *invalidUnmarshalError) Error() string {
+ if e.Type == nil {
+ return "asn1: Unmarshal recipient value is nil"
+ }
+
+ if e.Type.Kind() != reflect.Pointer {
+ return "asn1: Unmarshal recipient value is non-pointer " + e.Type.String()
+ }
+ return "asn1: Unmarshal recipient value is nil " + e.Type.String()
+}
+
+// UnmarshalWithParams allows field parameters to be specified for the
+// top-level element. The form of the params is the same as the field tags.
+func UnmarshalWithParams(b []byte, val any, params string) (rest []byte, err error) {
+ v := reflect.ValueOf(val)
+ if v.Kind() != reflect.Pointer || v.IsNil() {
+ return nil, &invalidUnmarshalError{reflect.TypeOf(val)}
+ }
+ offset, err := parseField(v.Elem(), b, 0, parseFieldParameters(params))
+ if err != nil {
+ return nil, err
+ }
+ return b[offset:], nil
+}
diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go
new file mode 100644
index 0000000..b1e05b9
--- /dev/null
+++ b/src/encoding/asn1/asn1_test.go
@@ -0,0 +1,1166 @@
+// 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
+
+import (
+ "bytes"
+ "encoding/hex"
+ "fmt"
+ "math"
+ "math/big"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+)
+
+type boolTest struct {
+ in []byte
+ ok bool
+ out bool
+}
+
+var boolTestData = []boolTest{
+ {[]byte{0x00}, true, false},
+ {[]byte{0xff}, true, true},
+ {[]byte{0x00, 0x00}, false, false},
+ {[]byte{0xff, 0xff}, false, false},
+ {[]byte{0x01}, false, false},
+}
+
+func TestParseBool(t *testing.T) {
+ for i, test := range boolTestData {
+ ret, err := parseBool(test.in)
+ if (err == nil) != test.ok {
+ t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
+ }
+ if test.ok && ret != test.out {
+ t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out)
+ }
+ }
+}
+
+type int64Test struct {
+ in []byte
+ ok bool
+ out int64
+}
+
+var int64TestData = []int64Test{
+ {[]byte{0x00}, true, 0},
+ {[]byte{0x7f}, true, 127},
+ {[]byte{0x00, 0x80}, true, 128},
+ {[]byte{0x01, 0x00}, true, 256},
+ {[]byte{0x80}, true, -128},
+ {[]byte{0xff, 0x7f}, true, -129},
+ {[]byte{0xff}, true, -1},
+ {[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, true, -9223372036854775808},
+ {[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, false, 0},
+ {[]byte{}, false, 0},
+ {[]byte{0x00, 0x7f}, false, 0},
+ {[]byte{0xff, 0xf0}, false, 0},
+}
+
+func TestParseInt64(t *testing.T) {
+ for i, test := range int64TestData {
+ ret, err := parseInt64(test.in)
+ if (err == nil) != test.ok {
+ t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
+ }
+ if test.ok && ret != test.out {
+ t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out)
+ }
+ }
+}
+
+type int32Test struct {
+ in []byte
+ ok bool
+ out int32
+}
+
+var int32TestData = []int32Test{
+ {[]byte{0x00}, true, 0},
+ {[]byte{0x7f}, true, 127},
+ {[]byte{0x00, 0x80}, true, 128},
+ {[]byte{0x01, 0x00}, true, 256},
+ {[]byte{0x80}, true, -128},
+ {[]byte{0xff, 0x7f}, true, -129},
+ {[]byte{0xff}, true, -1},
+ {[]byte{0x80, 0x00, 0x00, 0x00}, true, -2147483648},
+ {[]byte{0x80, 0x00, 0x00, 0x00, 0x00}, false, 0},
+ {[]byte{}, false, 0},
+ {[]byte{0x00, 0x7f}, false, 0},
+ {[]byte{0xff, 0xf0}, false, 0},
+}
+
+func TestParseInt32(t *testing.T) {
+ for i, test := range int32TestData {
+ ret, err := parseInt32(test.in)
+ if (err == nil) != test.ok {
+ t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
+ }
+ if test.ok && int32(ret) != test.out {
+ t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out)
+ }
+ }
+}
+
+var bigIntTests = []struct {
+ in []byte
+ ok bool
+ base10 string
+}{
+ {[]byte{0xff}, true, "-1"},
+ {[]byte{0x00}, true, "0"},
+ {[]byte{0x01}, true, "1"},
+ {[]byte{0x00, 0xff}, true, "255"},
+ {[]byte{0xff, 0x00}, true, "-256"},
+ {[]byte{0x01, 0x00}, true, "256"},
+ {[]byte{}, false, ""},
+ {[]byte{0x00, 0x7f}, false, ""},
+ {[]byte{0xff, 0xf0}, false, ""},
+}
+
+func TestParseBigInt(t *testing.T) {
+ for i, test := range bigIntTests {
+ ret, err := parseBigInt(test.in)
+ if (err == nil) != test.ok {
+ t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
+ }
+ if test.ok {
+ if ret.String() != test.base10 {
+ t.Errorf("#%d: bad result from %x, got %s want %s", i, test.in, ret.String(), test.base10)
+ }
+ e, err := makeBigInt(ret)
+ if err != nil {
+ t.Errorf("%d: err=%q", i, err)
+ continue
+ }
+ result := make([]byte, e.Len())
+ e.Encode(result)
+ if !bytes.Equal(result, test.in) {
+ t.Errorf("#%d: got %x from marshaling %s, want %x", i, result, ret, test.in)
+ }
+ }
+ }
+}
+
+type bitStringTest struct {
+ in []byte
+ ok bool
+ out []byte
+ bitLength int
+}
+
+var bitStringTestData = []bitStringTest{
+ {[]byte{}, false, []byte{}, 0},
+ {[]byte{0x00}, true, []byte{}, 0},
+ {[]byte{0x07, 0x00}, true, []byte{0x00}, 1},
+ {[]byte{0x07, 0x01}, false, []byte{}, 0},
+ {[]byte{0x07, 0x40}, false, []byte{}, 0},
+ {[]byte{0x08, 0x00}, false, []byte{}, 0},
+}
+
+func TestBitString(t *testing.T) {
+ for i, test := range bitStringTestData {
+ ret, err := parseBitString(test.in)
+ if (err == nil) != test.ok {
+ t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
+ }
+ if err == nil {
+ if test.bitLength != ret.BitLength || !bytes.Equal(ret.Bytes, test.out) {
+ t.Errorf("#%d: Bad result: %v (expected %v %v)", i, ret, test.out, test.bitLength)
+ }
+ }
+ }
+}
+
+func TestBitStringAt(t *testing.T) {
+ bs := BitString{[]byte{0x82, 0x40}, 16}
+ if bs.At(0) != 1 {
+ t.Error("#1: Failed")
+ }
+ if bs.At(1) != 0 {
+ t.Error("#2: Failed")
+ }
+ if bs.At(6) != 1 {
+ t.Error("#3: Failed")
+ }
+ if bs.At(9) != 1 {
+ t.Error("#4: Failed")
+ }
+ if bs.At(-1) != 0 {
+ t.Error("#5: Failed")
+ }
+ if bs.At(17) != 0 {
+ t.Error("#6: Failed")
+ }
+}
+
+type bitStringRightAlignTest struct {
+ in []byte
+ inlen int
+ out []byte
+}
+
+var bitStringRightAlignTests = []bitStringRightAlignTest{
+ {[]byte{0x80}, 1, []byte{0x01}},
+ {[]byte{0x80, 0x80}, 9, []byte{0x01, 0x01}},
+ {[]byte{}, 0, []byte{}},
+ {[]byte{0xce}, 8, []byte{0xce}},
+ {[]byte{0xce, 0x47}, 16, []byte{0xce, 0x47}},
+ {[]byte{0x34, 0x50}, 12, []byte{0x03, 0x45}},
+}
+
+func TestBitStringRightAlign(t *testing.T) {
+ for i, test := range bitStringRightAlignTests {
+ bs := BitString{test.in, test.inlen}
+ out := bs.RightAlign()
+ if !bytes.Equal(out, test.out) {
+ t.Errorf("#%d got: %x want: %x", i, out, test.out)
+ }
+ }
+}
+
+type objectIdentifierTest struct {
+ in []byte
+ ok bool
+ out ObjectIdentifier // has base type[]int
+}
+
+var objectIdentifierTestData = []objectIdentifierTest{
+ {[]byte{}, false, []int{}},
+ {[]byte{85}, true, []int{2, 5}},
+ {[]byte{85, 0x02}, true, []int{2, 5, 2}},
+ {[]byte{85, 0x02, 0xc0, 0x00}, true, []int{2, 5, 2, 0x2000}},
+ {[]byte{0x81, 0x34, 0x03}, true, []int{2, 100, 3}},
+ {[]byte{85, 0x02, 0xc0, 0x80, 0x80, 0x80, 0x80}, false, []int{}},
+}
+
+func TestObjectIdentifier(t *testing.T) {
+ for i, test := range objectIdentifierTestData {
+ ret, err := parseObjectIdentifier(test.in)
+ if (err == nil) != test.ok {
+ t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
+ }
+ if err == nil {
+ if !reflect.DeepEqual(test.out, ret) {
+ t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out)
+ }
+ }
+ }
+
+ if s := ObjectIdentifier([]int{1, 2, 3, 4}).String(); s != "1.2.3.4" {
+ t.Errorf("bad ObjectIdentifier.String(). Got %s, want 1.2.3.4", s)
+ }
+}
+
+type timeTest struct {
+ in string
+ ok bool
+ out time.Time
+}
+
+var utcTestData = []timeTest{
+ {"910506164540-0700", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", -7*60*60))},
+ {"910506164540+0730", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", 7*60*60+30*60))},
+ {"910506234540Z", true, time.Date(1991, 05, 06, 23, 45, 40, 0, time.UTC)},
+ {"9105062345Z", true, time.Date(1991, 05, 06, 23, 45, 0, 0, time.UTC)},
+ {"5105062345Z", true, time.Date(1951, 05, 06, 23, 45, 0, 0, time.UTC)},
+ {"a10506234540Z", false, time.Time{}},
+ {"91a506234540Z", false, time.Time{}},
+ {"9105a6234540Z", false, time.Time{}},
+ {"910506a34540Z", false, time.Time{}},
+ {"910506334a40Z", false, time.Time{}},
+ {"91050633444aZ", false, time.Time{}},
+ {"910506334461Z", false, time.Time{}},
+ {"910506334400Za", false, time.Time{}},
+ /* These are invalid times. However, the time package normalises times
+ * and they were accepted in some versions. See #11134. */
+ {"000100000000Z", false, time.Time{}},
+ {"101302030405Z", false, time.Time{}},
+ {"100002030405Z", false, time.Time{}},
+ {"100100030405Z", false, time.Time{}},
+ {"100132030405Z", false, time.Time{}},
+ {"100231030405Z", false, time.Time{}},
+ {"100102240405Z", false, time.Time{}},
+ {"100102036005Z", false, time.Time{}},
+ {"100102030460Z", false, time.Time{}},
+ {"-100102030410Z", false, time.Time{}},
+ {"10-0102030410Z", false, time.Time{}},
+ {"10-0002030410Z", false, time.Time{}},
+ {"1001-02030410Z", false, time.Time{}},
+ {"100102-030410Z", false, time.Time{}},
+ {"10010203-0410Z", false, time.Time{}},
+ {"1001020304-10Z", false, time.Time{}},
+}
+
+func TestUTCTime(t *testing.T) {
+ for i, test := range utcTestData {
+ ret, err := parseUTCTime([]byte(test.in))
+ if err != nil {
+ if test.ok {
+ t.Errorf("#%d: parseUTCTime(%q) = error %v", i, test.in, err)
+ }
+ continue
+ }
+ if !test.ok {
+ t.Errorf("#%d: parseUTCTime(%q) succeeded, should have failed", i, test.in)
+ continue
+ }
+ const format = "Jan _2 15:04:05 -0700 2006" // ignore zone name, just offset
+ have := ret.Format(format)
+ want := test.out.Format(format)
+ if have != want {
+ t.Errorf("#%d: parseUTCTime(%q) = %s, want %s", i, test.in, have, want)
+ }
+ }
+}
+
+var generalizedTimeTestData = []timeTest{
+ {"20100102030405Z", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.UTC)},
+ {"20100102030405", false, time.Time{}},
+ {"20100102030405+0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", 6*60*60+7*60))},
+ {"20100102030405-0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", -6*60*60-7*60))},
+ /* These are invalid times. However, the time package normalises times
+ * and they were accepted in some versions. See #11134. */
+ {"00000100000000Z", false, time.Time{}},
+ {"20101302030405Z", false, time.Time{}},
+ {"20100002030405Z", false, time.Time{}},
+ {"20100100030405Z", false, time.Time{}},
+ {"20100132030405Z", false, time.Time{}},
+ {"20100231030405Z", false, time.Time{}},
+ {"20100102240405Z", false, time.Time{}},
+ {"20100102036005Z", false, time.Time{}},
+ {"20100102030460Z", false, time.Time{}},
+ {"-20100102030410Z", false, time.Time{}},
+ {"2010-0102030410Z", false, time.Time{}},
+ {"2010-0002030410Z", false, time.Time{}},
+ {"201001-02030410Z", false, time.Time{}},
+ {"20100102-030410Z", false, time.Time{}},
+ {"2010010203-0410Z", false, time.Time{}},
+ {"201001020304-10Z", false, time.Time{}},
+}
+
+func TestGeneralizedTime(t *testing.T) {
+ for i, test := range generalizedTimeTestData {
+ ret, err := parseGeneralizedTime([]byte(test.in))
+ if (err == nil) != test.ok {
+ t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
+ }
+ if err == nil {
+ if !reflect.DeepEqual(test.out, ret) {
+ t.Errorf("#%d: Bad result: %q → %v (expected %v)", i, test.in, ret, test.out)
+ }
+ }
+ }
+}
+
+type tagAndLengthTest struct {
+ in []byte
+ ok bool
+ out tagAndLength
+}
+
+var tagAndLengthData = []tagAndLengthTest{
+ {[]byte{0x80, 0x01}, true, tagAndLength{2, 0, 1, false}},
+ {[]byte{0xa0, 0x01}, true, tagAndLength{2, 0, 1, true}},
+ {[]byte{0x02, 0x00}, true, tagAndLength{0, 2, 0, false}},
+ {[]byte{0xfe, 0x00}, true, tagAndLength{3, 30, 0, true}},
+ {[]byte{0x1f, 0x1f, 0x00}, true, tagAndLength{0, 31, 0, false}},
+ {[]byte{0x1f, 0x81, 0x00, 0x00}, true, tagAndLength{0, 128, 0, false}},
+ {[]byte{0x1f, 0x81, 0x80, 0x01, 0x00}, true, tagAndLength{0, 0x4001, 0, false}},
+ {[]byte{0x00, 0x81, 0x80}, true, tagAndLength{0, 0, 128, false}},
+ {[]byte{0x00, 0x82, 0x01, 0x00}, true, tagAndLength{0, 0, 256, false}},
+ {[]byte{0x00, 0x83, 0x01, 0x00}, false, tagAndLength{}},
+ {[]byte{0x1f, 0x85}, false, tagAndLength{}},
+ {[]byte{0x30, 0x80}, false, tagAndLength{}},
+ // Superfluous zeros in the length should be an error.
+ {[]byte{0xa0, 0x82, 0x00, 0xff}, false, tagAndLength{}},
+ // Lengths up to the maximum size of an int should work.
+ {[]byte{0xa0, 0x84, 0x7f, 0xff, 0xff, 0xff}, true, tagAndLength{2, 0, 0x7fffffff, true}},
+ // Lengths that would overflow an int should be rejected.
+ {[]byte{0xa0, 0x84, 0x80, 0x00, 0x00, 0x00}, false, tagAndLength{}},
+ // Long length form may not be used for lengths that fit in short form.
+ {[]byte{0xa0, 0x81, 0x7f}, false, tagAndLength{}},
+ // Tag numbers which would overflow int32 are rejected. (The value below is 2^31.)
+ {[]byte{0x1f, 0x88, 0x80, 0x80, 0x80, 0x00, 0x00}, false, tagAndLength{}},
+ // Tag numbers that fit in an int32 are valid. (The value below is 2^31 - 1.)
+ {[]byte{0x1f, 0x87, 0xFF, 0xFF, 0xFF, 0x7F, 0x00}, true, tagAndLength{tag: math.MaxInt32}},
+ // Long tag number form may not be used for tags that fit in short form.
+ {[]byte{0x1f, 0x1e, 0x00}, false, tagAndLength{}},
+}
+
+func TestParseTagAndLength(t *testing.T) {
+ for i, test := range tagAndLengthData {
+ tagAndLength, _, err := parseTagAndLength(test.in, 0)
+ if (err == nil) != test.ok {
+ t.Errorf("#%d: Incorrect error result (did pass? %v, expected: %v)", i, err == nil, test.ok)
+ }
+ if err == nil && !reflect.DeepEqual(test.out, tagAndLength) {
+ t.Errorf("#%d: Bad result: %v (expected %v)", i, tagAndLength, test.out)
+ }
+ }
+}
+
+type parseFieldParametersTest struct {
+ in string
+ out fieldParameters
+}
+
+func newInt(n int) *int { return &n }
+
+func newInt64(n int64) *int64 { return &n }
+
+func newString(s string) *string { return &s }
+
+func newBool(b bool) *bool { return &b }
+
+var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParametersTest{
+ {"", fieldParameters{}},
+ {"ia5", fieldParameters{stringType: TagIA5String}},
+ {"generalized", fieldParameters{timeType: TagGeneralizedTime}},
+ {"utc", fieldParameters{timeType: TagUTCTime}},
+ {"printable", fieldParameters{stringType: TagPrintableString}},
+ {"numeric", fieldParameters{stringType: TagNumericString}},
+ {"optional", fieldParameters{optional: true}},
+ {"explicit", fieldParameters{explicit: true, tag: new(int)}},
+ {"application", fieldParameters{application: true, tag: new(int)}},
+ {"private", fieldParameters{private: true, tag: new(int)}},
+ {"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}},
+ {"default:42", fieldParameters{defaultValue: newInt64(42)}},
+ {"tag:17", fieldParameters{tag: newInt(17)}},
+ {"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}},
+ {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{optional: true, explicit: true, application: false, defaultValue: newInt64(42), tag: newInt(17), stringType: 0, timeType: 0, set: false, omitEmpty: false}},
+ {"set", fieldParameters{set: true}},
+}
+
+func TestParseFieldParameters(t *testing.T) {
+ for i, test := range parseFieldParametersTestData {
+ f := parseFieldParameters(test.in)
+ if !reflect.DeepEqual(f, test.out) {
+ t.Errorf("#%d: Bad result: %v (expected %v)", i, f, test.out)
+ }
+ }
+}
+
+type TestObjectIdentifierStruct struct {
+ OID ObjectIdentifier
+}
+
+type TestContextSpecificTags struct {
+ A int `asn1:"tag:1"`
+}
+
+type TestContextSpecificTags2 struct {
+ A int `asn1:"explicit,tag:1"`
+ B int
+}
+
+type TestContextSpecificTags3 struct {
+ S string `asn1:"tag:1,utf8"`
+}
+
+type TestElementsAfterString struct {
+ S string
+ A, B int
+}
+
+type TestBigInt struct {
+ X *big.Int
+}
+
+type TestSet struct {
+ Ints []int `asn1:"set"`
+}
+
+var unmarshalTestData = []struct {
+ in []byte
+ out any
+}{
+ {[]byte{0x02, 0x01, 0x42}, newInt(0x42)},
+ {[]byte{0x05, 0x00}, &RawValue{0, 5, false, []byte{}, []byte{0x05, 0x00}}},
+ {[]byte{0x30, 0x08, 0x06, 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d}, &TestObjectIdentifierStruct{[]int{1, 2, 840, 113549}}},
+ {[]byte{0x03, 0x04, 0x06, 0x6e, 0x5d, 0xc0}, &BitString{[]byte{110, 93, 192}, 18}},
+ {[]byte{0x30, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &[]int{1, 2, 3}},
+ {[]byte{0x02, 0x01, 0x10}, newInt(16)},
+ {[]byte{0x13, 0x04, 't', 'e', 's', 't'}, newString("test")},
+ {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, newString("test")},
+ // Ampersand is allowed in PrintableString due to mistakes by major CAs.
+ {[]byte{0x13, 0x05, 't', 'e', 's', 't', '&'}, newString("test&")},
+ {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, &RawValue{0, 22, false, []byte("test"), []byte("\x16\x04test")}},
+ {[]byte{0x04, 0x04, 1, 2, 3, 4}, &RawValue{0, 4, false, []byte{1, 2, 3, 4}, []byte{4, 4, 1, 2, 3, 4}}},
+ {[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}},
+ {[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}},
+ {[]byte{0x30, 0x03, 0x81, 0x01, '@'}, &TestContextSpecificTags3{"@"}},
+ {[]byte{0x01, 0x01, 0x00}, newBool(false)},
+ {[]byte{0x01, 0x01, 0xff}, newBool(true)},
+ {[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}},
+ {[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}, &TestBigInt{big.NewInt(0x123456)}},
+ {[]byte{0x30, 0x0b, 0x31, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &TestSet{Ints: []int{1, 2, 3}}},
+ {[]byte{0x12, 0x0b, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '}, newString("0123456789 ")},
+}
+
+func TestUnmarshal(t *testing.T) {
+ for i, test := range unmarshalTestData {
+ pv := reflect.New(reflect.TypeOf(test.out).Elem())
+ val := pv.Interface()
+ _, err := Unmarshal(test.in, val)
+ if err != nil {
+ t.Errorf("Unmarshal failed at index %d %v", i, err)
+ }
+ if !reflect.DeepEqual(val, test.out) {
+ t.Errorf("#%d:\nhave %#v\nwant %#v", i, val, test.out)
+ }
+ }
+}
+
+func TestUnmarshalWithNilOrNonPointer(t *testing.T) {
+ tests := []struct {
+ b []byte
+ v any
+ want string
+ }{
+ {b: []byte{0x05, 0x00}, v: nil, want: "asn1: Unmarshal recipient value is nil"},
+ {b: []byte{0x05, 0x00}, v: RawValue{}, want: "asn1: Unmarshal recipient value is non-pointer asn1.RawValue"},
+ {b: []byte{0x05, 0x00}, v: (*RawValue)(nil), want: "asn1: Unmarshal recipient value is nil *asn1.RawValue"},
+ }
+
+ for _, test := range tests {
+ _, err := Unmarshal(test.b, test.v)
+ if err == nil {
+ t.Errorf("Unmarshal expecting error, got nil")
+ continue
+ }
+ if g, w := err.Error(), test.want; g != w {
+ t.Errorf("InvalidUnmarshalError mismatch\nGot: %q\nWant: %q", g, w)
+ }
+ }
+}
+
+type Certificate struct {
+ TBSCertificate TBSCertificate
+ SignatureAlgorithm AlgorithmIdentifier
+ SignatureValue BitString
+}
+
+type TBSCertificate struct {
+ Version int `asn1:"optional,explicit,default:0,tag:0"`
+ SerialNumber RawValue
+ SignatureAlgorithm AlgorithmIdentifier
+ Issuer RDNSequence
+ Validity Validity
+ Subject RDNSequence
+ PublicKey PublicKeyInfo
+}
+
+type AlgorithmIdentifier struct {
+ Algorithm ObjectIdentifier
+}
+
+type RDNSequence []RelativeDistinguishedNameSET
+
+type RelativeDistinguishedNameSET []AttributeTypeAndValue
+
+type AttributeTypeAndValue struct {
+ Type ObjectIdentifier
+ Value any
+}
+
+type Validity struct {
+ NotBefore, NotAfter time.Time
+}
+
+type PublicKeyInfo struct {
+ Algorithm AlgorithmIdentifier
+ PublicKey BitString
+}
+
+func TestCertificate(t *testing.T) {
+ // This is a minimal, self-signed certificate that should parse correctly.
+ var cert Certificate
+ if _, err := Unmarshal(derEncodedSelfSignedCertBytes, &cert); err != nil {
+ t.Errorf("Unmarshal failed: %v", err)
+ }
+ if !reflect.DeepEqual(cert, derEncodedSelfSignedCert) {
+ t.Errorf("Bad result:\ngot: %+v\nwant: %+v", cert, derEncodedSelfSignedCert)
+ }
+}
+
+func TestCertificateWithNUL(t *testing.T) {
+ // This is the paypal NUL-hack certificate. It should fail to parse because
+ // NUL isn't a permitted character in a PrintableString.
+
+ var cert Certificate
+ if _, err := Unmarshal(derEncodedPaypalNULCertBytes, &cert); err == nil {
+ t.Error("Unmarshal succeeded, should not have")
+ }
+}
+
+type rawStructTest struct {
+ Raw RawContent
+ A int
+}
+
+func TestRawStructs(t *testing.T) {
+ var s rawStructTest
+ input := []byte{0x30, 0x03, 0x02, 0x01, 0x50}
+
+ rest, err := Unmarshal(input, &s)
+ if len(rest) != 0 {
+ t.Errorf("incomplete parse: %x", rest)
+ return
+ }
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if s.A != 0x50 {
+ t.Errorf("bad value for A: got %d want %d", s.A, 0x50)
+ }
+ if !bytes.Equal([]byte(s.Raw), input) {
+ t.Errorf("bad value for Raw: got %x want %x", s.Raw, input)
+ }
+}
+
+type oiEqualTest struct {
+ first ObjectIdentifier
+ second ObjectIdentifier
+ same bool
+}
+
+var oiEqualTests = []oiEqualTest{
+ {
+ ObjectIdentifier{1, 2, 3},
+ ObjectIdentifier{1, 2, 3},
+ true,
+ },
+ {
+ ObjectIdentifier{1},
+ ObjectIdentifier{1, 2, 3},
+ false,
+ },
+ {
+ ObjectIdentifier{1, 2, 3},
+ ObjectIdentifier{10, 11, 12},
+ false,
+ },
+}
+
+func TestObjectIdentifierEqual(t *testing.T) {
+ for _, o := range oiEqualTests {
+ if s := o.first.Equal(o.second); s != o.same {
+ t.Errorf("ObjectIdentifier.Equal: got: %t want: %t", s, o.same)
+ }
+ }
+}
+
+var derEncodedSelfSignedCert = Certificate{
+ TBSCertificate: TBSCertificate{
+ Version: 0,
+ SerialNumber: RawValue{Class: 0, Tag: 2, IsCompound: false, Bytes: []uint8{0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}, FullBytes: []byte{2, 9, 0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}},
+ SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}},
+ Issuer: RDNSequence{
+ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}},
+ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}},
+ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 7}, Value: "City"}},
+ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 10}, Value: "Internet Widgits Pty Ltd"}},
+ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}},
+ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}},
+ },
+ Validity: Validity{
+ NotBefore: time.Date(2009, 10, 8, 00, 25, 53, 0, time.UTC),
+ NotAfter: time.Date(2010, 10, 8, 00, 25, 53, 0, time.UTC),
+ },
+ Subject: RDNSequence{
+ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}},
+ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}},
+ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 7}, Value: "City"}},
+ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 10}, Value: "Internet Widgits Pty Ltd"}},
+ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}},
+ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}},
+ },
+ PublicKey: PublicKeyInfo{
+ Algorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}},
+ PublicKey: BitString{
+ Bytes: []uint8{
+ 0x30, 0x48, 0x2, 0x41, 0x0, 0xcd, 0xb7,
+ 0x63, 0x9c, 0x32, 0x78, 0xf0, 0x6, 0xaa, 0x27, 0x7f, 0x6e, 0xaf, 0x42,
+ 0x90, 0x2b, 0x59, 0x2d, 0x8c, 0xbc, 0xbe, 0x38, 0xa1, 0xc9, 0x2b, 0xa4,
+ 0x69, 0x5a, 0x33, 0x1b, 0x1d, 0xea, 0xde, 0xad, 0xd8, 0xe9, 0xa5, 0xc2,
+ 0x7e, 0x8c, 0x4c, 0x2f, 0xd0, 0xa8, 0x88, 0x96, 0x57, 0x72, 0x2a, 0x4f,
+ 0x2a, 0xf7, 0x58, 0x9c, 0xf2, 0xc7, 0x70, 0x45, 0xdc, 0x8f, 0xde, 0xec,
+ 0x35, 0x7d, 0x2, 0x3, 0x1, 0x0, 0x1,
+ },
+ BitLength: 592,
+ },
+ },
+ },
+ SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}},
+ SignatureValue: BitString{
+ Bytes: []uint8{
+ 0xa6, 0x7b, 0x6, 0xec, 0x5e, 0xce,
+ 0x92, 0x77, 0x2c, 0xa4, 0x13, 0xcb, 0xa3, 0xca, 0x12, 0x56, 0x8f, 0xdc, 0x6c,
+ 0x7b, 0x45, 0x11, 0xcd, 0x40, 0xa7, 0xf6, 0x59, 0x98, 0x4, 0x2, 0xdf, 0x2b,
+ 0x99, 0x8b, 0xb9, 0xa4, 0xa8, 0xcb, 0xeb, 0x34, 0xc0, 0xf0, 0xa7, 0x8c, 0xf8,
+ 0xd9, 0x1e, 0xde, 0x14, 0xa5, 0xed, 0x76, 0xbf, 0x11, 0x6f, 0xe3, 0x60, 0xaa,
+ 0xfa, 0x88, 0x21, 0x49, 0x4, 0x35,
+ },
+ BitLength: 512,
+ },
+}
+
+var derEncodedSelfSignedCertBytes = []byte{
+ 0x30, 0x82, 0x02, 0x18, 0x30,
+ 0x82, 0x01, 0xc2, 0x02, 0x09, 0x00, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c,
+ 0x98, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x30, 0x81, 0x92, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x58, 0x58, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x04, 0x43,
+ 0x69, 0x74, 0x79, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
+ 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31,
+ 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x66, 0x61, 0x6c,
+ 0x73, 0x65, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x09, 0x01, 0x16, 0x11, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x40, 0x65, 0x78,
+ 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d,
+ 0x30, 0x39, 0x31, 0x30, 0x30, 0x38, 0x30, 0x30, 0x32, 0x35, 0x35, 0x33, 0x5a,
+ 0x17, 0x0d, 0x31, 0x30, 0x31, 0x30, 0x30, 0x38, 0x30, 0x30, 0x32, 0x35, 0x35,
+ 0x33, 0x5a, 0x30, 0x81, 0x92, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x58, 0x58, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
+ 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x04, 0x43, 0x69,
+ 0x74, 0x79, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18,
+ 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
+ 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x1a,
+ 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x66, 0x61, 0x6c, 0x73,
+ 0x65, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x09, 0x01, 0x16, 0x11, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x40, 0x65, 0x78, 0x61,
+ 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x5c, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0xcd, 0xb7, 0x63, 0x9c, 0x32, 0x78,
+ 0xf0, 0x06, 0xaa, 0x27, 0x7f, 0x6e, 0xaf, 0x42, 0x90, 0x2b, 0x59, 0x2d, 0x8c,
+ 0xbc, 0xbe, 0x38, 0xa1, 0xc9, 0x2b, 0xa4, 0x69, 0x5a, 0x33, 0x1b, 0x1d, 0xea,
+ 0xde, 0xad, 0xd8, 0xe9, 0xa5, 0xc2, 0x7e, 0x8c, 0x4c, 0x2f, 0xd0, 0xa8, 0x88,
+ 0x96, 0x57, 0x72, 0x2a, 0x4f, 0x2a, 0xf7, 0x58, 0x9c, 0xf2, 0xc7, 0x70, 0x45,
+ 0xdc, 0x8f, 0xde, 0xec, 0x35, 0x7d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x03, 0x41, 0x00, 0xa6, 0x7b, 0x06, 0xec, 0x5e, 0xce, 0x92, 0x77, 0x2c, 0xa4,
+ 0x13, 0xcb, 0xa3, 0xca, 0x12, 0x56, 0x8f, 0xdc, 0x6c, 0x7b, 0x45, 0x11, 0xcd,
+ 0x40, 0xa7, 0xf6, 0x59, 0x98, 0x04, 0x02, 0xdf, 0x2b, 0x99, 0x8b, 0xb9, 0xa4,
+ 0xa8, 0xcb, 0xeb, 0x34, 0xc0, 0xf0, 0xa7, 0x8c, 0xf8, 0xd9, 0x1e, 0xde, 0x14,
+ 0xa5, 0xed, 0x76, 0xbf, 0x11, 0x6f, 0xe3, 0x60, 0xaa, 0xfa, 0x88, 0x21, 0x49,
+ 0x04, 0x35,
+}
+
+var derEncodedPaypalNULCertBytes = []byte{
+ 0x30, 0x82, 0x06, 0x44, 0x30,
+ 0x82, 0x05, 0xad, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x00, 0xf0, 0x9b,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x30, 0x82, 0x01, 0x12, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x45, 0x53, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x09, 0x42, 0x61, 0x72, 0x63, 0x65, 0x6c, 0x6f, 0x6e, 0x61,
+ 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x42, 0x61,
+ 0x72, 0x63, 0x65, 0x6c, 0x6f, 0x6e, 0x61, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x20, 0x49, 0x50, 0x53, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x73, 0x2e, 0x6c, 0x2e, 0x31, 0x2e,
+ 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x14, 0x25, 0x67, 0x65, 0x6e, 0x65,
+ 0x72, 0x61, 0x6c, 0x40, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x20, 0x43, 0x2e, 0x49, 0x2e, 0x46, 0x2e, 0x20, 0x20, 0x42, 0x2d, 0x42, 0x36,
+ 0x32, 0x32, 0x31, 0x30, 0x36, 0x39, 0x35, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x25, 0x69, 0x70, 0x73, 0x43, 0x41, 0x20, 0x43, 0x4c,
+ 0x41, 0x53, 0x45, 0x41, 0x31, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x25, 0x69, 0x70, 0x73, 0x43, 0x41, 0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41,
+ 0x31, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31,
+ 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09,
+ 0x01, 0x16, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x40, 0x69, 0x70,
+ 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39,
+ 0x30, 0x32, 0x32, 0x34, 0x32, 0x33, 0x30, 0x34, 0x31, 0x37, 0x5a, 0x17, 0x0d,
+ 0x31, 0x31, 0x30, 0x32, 0x32, 0x34, 0x32, 0x33, 0x30, 0x34, 0x31, 0x37, 0x5a,
+ 0x30, 0x81, 0x94, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x16,
+ 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0d, 0x53, 0x61, 0x6e, 0x20,
+ 0x46, 0x72, 0x61, 0x6e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x31, 0x11, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69,
+ 0x74, 0x79, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0b,
+ 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x55, 0x6e, 0x69, 0x74, 0x31, 0x2f,
+ 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x77, 0x77, 0x77, 0x2e,
+ 0x70, 0x61, 0x79, 0x70, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x73, 0x73,
+ 0x6c, 0x2e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x63, 0x63, 0x30, 0x81, 0x9f, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xd2, 0x69,
+ 0xfa, 0x6f, 0x3a, 0x00, 0xb4, 0x21, 0x1b, 0xc8, 0xb1, 0x02, 0xd7, 0x3f, 0x19,
+ 0xb2, 0xc4, 0x6d, 0xb4, 0x54, 0xf8, 0x8b, 0x8a, 0xcc, 0xdb, 0x72, 0xc2, 0x9e,
+ 0x3c, 0x60, 0xb9, 0xc6, 0x91, 0x3d, 0x82, 0xb7, 0x7d, 0x99, 0xff, 0xd1, 0x29,
+ 0x84, 0xc1, 0x73, 0x53, 0x9c, 0x82, 0xdd, 0xfc, 0x24, 0x8c, 0x77, 0xd5, 0x41,
+ 0xf3, 0xe8, 0x1e, 0x42, 0xa1, 0xad, 0x2d, 0x9e, 0xff, 0x5b, 0x10, 0x26, 0xce,
+ 0x9d, 0x57, 0x17, 0x73, 0x16, 0x23, 0x38, 0xc8, 0xd6, 0xf1, 0xba, 0xa3, 0x96,
+ 0x5b, 0x16, 0x67, 0x4a, 0x4f, 0x73, 0x97, 0x3a, 0x4d, 0x14, 0xa4, 0xf4, 0xe2,
+ 0x3f, 0x8b, 0x05, 0x83, 0x42, 0xd1, 0xd0, 0xdc, 0x2f, 0x7a, 0xe5, 0xb6, 0x10,
+ 0xb2, 0x11, 0xc0, 0xdc, 0x21, 0x2a, 0x90, 0xff, 0xae, 0x97, 0x71, 0x5a, 0x49,
+ 0x81, 0xac, 0x40, 0xf3, 0x3b, 0xb8, 0x59, 0xb2, 0x4f, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x82, 0x03, 0x21, 0x30, 0x82, 0x03, 0x1d, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x11, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02, 0x06, 0x40,
+ 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x03, 0xf8,
+ 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x61, 0x8f, 0x61, 0x34, 0x43, 0x55, 0x14,
+ 0x7f, 0x27, 0x09, 0xce, 0x4c, 0x8b, 0xea, 0x9b, 0x7b, 0x19, 0x25, 0xbc, 0x6e,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0x0e, 0x07, 0x60, 0xd4, 0x39, 0xc9, 0x1b, 0x5b, 0x5d, 0x90, 0x7b, 0x23, 0xc8,
+ 0xd2, 0x34, 0x9d, 0x4a, 0x9a, 0x46, 0x39, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d,
+ 0x11, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04,
+ 0x15, 0x30, 0x13, 0x81, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x40,
+ 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x72, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d, 0x04, 0x65, 0x16, 0x63,
+ 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4e,
+ 0x4f, 0x54, 0x20, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x45, 0x44, 0x2e,
+ 0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x65, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x68,
+ 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70,
+ 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x2f, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x02, 0x04, 0x22, 0x16, 0x20, 0x68,
+ 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70,
+ 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61,
+ 0x32, 0x30, 0x30, 0x32, 0x2f, 0x30, 0x43, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+ 0x86, 0xf8, 0x42, 0x01, 0x04, 0x04, 0x36, 0x16, 0x34, 0x68, 0x74, 0x74, 0x70,
+ 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30,
+ 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x43, 0x4c,
+ 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x46, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x03, 0x04, 0x39, 0x16, 0x37,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69,
+ 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63,
+ 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x72, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x68, 0x74,
+ 0x6d, 0x6c, 0x3f, 0x30, 0x43, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8,
+ 0x42, 0x01, 0x07, 0x04, 0x36, 0x16, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f,
+ 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41,
+ 0x31, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x3f, 0x30, 0x41, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x08, 0x04, 0x34, 0x16, 0x32, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73,
+ 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32,
+ 0x30, 0x30, 0x32, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x4c, 0x41,
+ 0x53, 0x45, 0x41, 0x31, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x30, 0x81, 0x83, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x7c, 0x30, 0x7a, 0x30, 0x39, 0xa0, 0x37, 0xa0,
+ 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70,
+ 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61,
+ 0x32, 0x30, 0x30, 0x32, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x3d, 0xa0, 0x3b, 0xa0, 0x39, 0x86, 0x37, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x69,
+ 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63,
+ 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30,
+ 0x30, 0x32, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63,
+ 0x73, 0x70, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x68, 0xee, 0x79, 0x97, 0x97, 0xdd, 0x3b,
+ 0xef, 0x16, 0x6a, 0x06, 0xf2, 0x14, 0x9a, 0x6e, 0xcd, 0x9e, 0x12, 0xf7, 0xaa,
+ 0x83, 0x10, 0xbd, 0xd1, 0x7c, 0x98, 0xfa, 0xc7, 0xae, 0xd4, 0x0e, 0x2c, 0x9e,
+ 0x38, 0x05, 0x9d, 0x52, 0x60, 0xa9, 0x99, 0x0a, 0x81, 0xb4, 0x98, 0x90, 0x1d,
+ 0xae, 0xbb, 0x4a, 0xd7, 0xb9, 0xdc, 0x88, 0x9e, 0x37, 0x78, 0x41, 0x5b, 0xf7,
+ 0x82, 0xa5, 0xf2, 0xba, 0x41, 0x25, 0x5a, 0x90, 0x1a, 0x1e, 0x45, 0x38, 0xa1,
+ 0x52, 0x58, 0x75, 0x94, 0x26, 0x44, 0xfb, 0x20, 0x07, 0xba, 0x44, 0xcc, 0xe5,
+ 0x4a, 0x2d, 0x72, 0x3f, 0x98, 0x47, 0xf6, 0x26, 0xdc, 0x05, 0x46, 0x05, 0x07,
+ 0x63, 0x21, 0xab, 0x46, 0x9b, 0x9c, 0x78, 0xd5, 0x54, 0x5b, 0x3d, 0x0c, 0x1e,
+ 0xc8, 0x64, 0x8c, 0xb5, 0x50, 0x23, 0x82, 0x6f, 0xdb, 0xb8, 0x22, 0x1c, 0x43,
+ 0x96, 0x07, 0xa8, 0xbb,
+}
+
+var stringSliceTestData = [][]string{
+ {"foo", "bar"},
+ {"foo", "\\bar"},
+ {"foo", "\"bar\""},
+ {"foo", "åäö"},
+}
+
+func TestStringSlice(t *testing.T) {
+ for _, test := range stringSliceTestData {
+ bs, err := Marshal(test)
+ if err != nil {
+ t.Error(err)
+ }
+
+ var res []string
+ _, err = Unmarshal(bs, &res)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if fmt.Sprintf("%v", res) != fmt.Sprintf("%v", test) {
+ t.Errorf("incorrect marshal/unmarshal; %v != %v", res, test)
+ }
+ }
+}
+
+type explicitTaggedTimeTest struct {
+ Time time.Time `asn1:"explicit,tag:0"`
+}
+
+var explicitTaggedTimeTestData = []struct {
+ in []byte
+ out explicitTaggedTimeTest
+}{
+ {[]byte{0x30, 0x11, 0xa0, 0xf, 0x17, 0xd, '9', '1', '0', '5', '0', '6', '1', '6', '4', '5', '4', '0', 'Z'},
+ explicitTaggedTimeTest{time.Date(1991, 05, 06, 16, 45, 40, 0, time.UTC)}},
+ {[]byte{0x30, 0x17, 0xa0, 0xf, 0x18, 0x13, '2', '0', '1', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '+', '0', '6', '0', '7'},
+ explicitTaggedTimeTest{time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", 6*60*60+7*60))}},
+}
+
+func TestExplicitTaggedTime(t *testing.T) {
+ // Test that a time.Time will match either tagUTCTime or
+ // tagGeneralizedTime.
+ for i, test := range explicitTaggedTimeTestData {
+ var got explicitTaggedTimeTest
+ _, err := Unmarshal(test.in, &got)
+ if err != nil {
+ t.Errorf("Unmarshal failed at index %d %v", i, err)
+ }
+ if !got.Time.Equal(test.out.Time) {
+ t.Errorf("#%d: got %v, want %v", i, got.Time, test.out.Time)
+ }
+ }
+}
+
+type implicitTaggedTimeTest struct {
+ Time time.Time `asn1:"tag:24"`
+}
+
+func TestImplicitTaggedTime(t *testing.T) {
+ // An implicitly tagged time value, that happens to have an implicit
+ // tag equal to a GENERALIZEDTIME, should still be parsed as a UTCTime.
+ // (There's no "timeType" in fieldParameters to determine what type of
+ // time should be expected when implicitly tagged.)
+ der := []byte{0x30, 0x0f, 0x80 | 24, 0xd, '9', '1', '0', '5', '0', '6', '1', '6', '4', '5', '4', '0', 'Z'}
+ var result implicitTaggedTimeTest
+ if _, err := Unmarshal(der, &result); err != nil {
+ t.Fatalf("Error while parsing: %s", err)
+ }
+ if expected := time.Date(1991, 05, 06, 16, 45, 40, 0, time.UTC); !result.Time.Equal(expected) {
+ t.Errorf("Wrong result. Got %v, want %v", result.Time, expected)
+ }
+}
+
+type truncatedExplicitTagTest struct {
+ Test int `asn1:"explicit,tag:0"`
+}
+
+func TestTruncatedExplicitTag(t *testing.T) {
+ // This crashed Unmarshal in the past. See #11154.
+ der := []byte{
+ 0x30, // SEQUENCE
+ 0x02, // two bytes long
+ 0xa0, // context-specific, tag 0
+ 0x30, // 48 bytes long
+ }
+
+ var result truncatedExplicitTagTest
+ if _, err := Unmarshal(der, &result); err == nil {
+ t.Error("Unmarshal returned without error")
+ }
+}
+
+type invalidUTF8Test struct {
+ Str string `asn1:"utf8"`
+}
+
+func TestUnmarshalInvalidUTF8(t *testing.T) {
+ data := []byte("0\x05\f\x03a\xc9c")
+ var result invalidUTF8Test
+ _, err := Unmarshal(data, &result)
+
+ const expectedSubstring = "UTF"
+ if err == nil {
+ t.Fatal("Successfully unmarshaled invalid UTF-8 data")
+ } else if !strings.Contains(err.Error(), expectedSubstring) {
+ t.Fatalf("Expected error to mention %q but error was %q", expectedSubstring, err.Error())
+ }
+}
+
+func TestMarshalNilValue(t *testing.T) {
+ nilValueTestData := []any{
+ nil,
+ struct{ V any }{},
+ }
+ for i, test := range nilValueTestData {
+ if _, err := Marshal(test); err == nil {
+ t.Fatalf("#%d: successfully marshaled nil value", i)
+ }
+ }
+}
+
+type unexported struct {
+ X int
+ y int
+}
+
+type exported struct {
+ X int
+ Y int
+}
+
+func TestUnexportedStructField(t *testing.T) {
+ want := StructuralError{"struct contains unexported fields"}
+
+ _, err := Marshal(unexported{X: 5, y: 1})
+ if err != want {
+ t.Errorf("got %v, want %v", err, want)
+ }
+
+ bs, err := Marshal(exported{X: 5, Y: 1})
+ if err != nil {
+ t.Fatal(err)
+ }
+ var u unexported
+ _, err = Unmarshal(bs, &u)
+ if err != want {
+ t.Errorf("got %v, want %v", err, want)
+ }
+}
+
+func TestNull(t *testing.T) {
+ marshaled, err := Marshal(NullRawValue)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(NullBytes, marshaled) {
+ t.Errorf("Expected Marshal of NullRawValue to yield %x, got %x", NullBytes, marshaled)
+ }
+
+ unmarshaled := RawValue{}
+ if _, err := Unmarshal(NullBytes, &unmarshaled); err != nil {
+ t.Fatal(err)
+ }
+
+ unmarshaled.FullBytes = NullRawValue.FullBytes
+ if len(unmarshaled.Bytes) == 0 {
+ // DeepEqual considers a nil slice and an empty slice to be different.
+ unmarshaled.Bytes = NullRawValue.Bytes
+ }
+
+ if !reflect.DeepEqual(NullRawValue, unmarshaled) {
+ t.Errorf("Expected Unmarshal of NullBytes to yield %v, got %v", NullRawValue, unmarshaled)
+ }
+}
+
+func TestExplicitTagRawValueStruct(t *testing.T) {
+ type foo struct {
+ A RawValue `asn1:"optional,explicit,tag:5"`
+ B []byte `asn1:"optional,explicit,tag:6"`
+ }
+ before := foo{B: []byte{1, 2, 3}}
+ derBytes, err := Marshal(before)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var after foo
+ if rest, err := Unmarshal(derBytes, &after); err != nil || len(rest) != 0 {
+ t.Fatal(err)
+ }
+
+ got := fmt.Sprintf("%#v", after)
+ want := fmt.Sprintf("%#v", before)
+ if got != want {
+ t.Errorf("got %s, want %s (DER: %x)", got, want, derBytes)
+ }
+}
+
+func TestTaggedRawValue(t *testing.T) {
+ type taggedRawValue struct {
+ A RawValue `asn1:"tag:5"`
+ }
+ type untaggedRawValue struct {
+ A RawValue
+ }
+ const isCompound = 0x20
+ const tag = 5
+
+ tests := []struct {
+ shouldMatch bool
+ derBytes []byte
+ }{
+ {false, []byte{0x30, 3, TagInteger, 1, 1}},
+ {true, []byte{0x30, 3, (ClassContextSpecific << 6) | tag, 1, 1}},
+ {true, []byte{0x30, 3, (ClassContextSpecific << 6) | tag | isCompound, 1, 1}},
+ {false, []byte{0x30, 3, (ClassApplication << 6) | tag | isCompound, 1, 1}},
+ {false, []byte{0x30, 3, (ClassPrivate << 6) | tag | isCompound, 1, 1}},
+ }
+
+ for i, test := range tests {
+ var tagged taggedRawValue
+ if _, err := Unmarshal(test.derBytes, &tagged); (err == nil) != test.shouldMatch {
+ t.Errorf("#%d: unexpected result parsing %x: %s", i, test.derBytes, err)
+ }
+
+ // An untagged RawValue should accept anything.
+ var untagged untaggedRawValue
+ if _, err := Unmarshal(test.derBytes, &untagged); err != nil {
+ t.Errorf("#%d: unexpected failure parsing %x with untagged RawValue: %s", i, test.derBytes, err)
+ }
+ }
+}
+
+var bmpStringTests = []struct {
+ decoded string
+ encodedHex string
+}{
+ {"", "0000"},
+ // Example from https://tools.ietf.org/html/rfc7292#appendix-B.
+ {"Beavis", "0042006500610076006900730000"},
+ // Some characters from the "Letterlike Symbols Unicode block".
+ {"\u2115 - Double-struck N", "21150020002d00200044006f00750062006c0065002d00730074007200750063006b0020004e0000"},
+}
+
+func TestBMPString(t *testing.T) {
+ for i, test := range bmpStringTests {
+ encoded, err := hex.DecodeString(test.encodedHex)
+ if err != nil {
+ t.Fatalf("#%d: failed to decode from hex string", i)
+ }
+
+ decoded, err := parseBMPString(encoded)
+
+ if err != nil {
+ t.Errorf("#%d: decoding output gave an error: %s", i, err)
+ continue
+ }
+
+ if decoded != test.decoded {
+ t.Errorf("#%d: decoding output resulted in %q, but it should have been %q", i, decoded, test.decoded)
+ continue
+ }
+ }
+}
+
+func TestNonMinimalEncodedOID(t *testing.T) {
+ h, err := hex.DecodeString("060a2a80864886f70d01010b")
+ if err != nil {
+ t.Fatalf("failed to decode from hex string: %s", err)
+ }
+ var oid ObjectIdentifier
+ _, err = Unmarshal(h, &oid)
+ if err == nil {
+ t.Fatalf("accepted non-minimally encoded oid")
+ }
+}
diff --git a/src/encoding/asn1/common.go b/src/encoding/asn1/common.go
new file mode 100644
index 0000000..40115df
--- /dev/null
+++ b/src/encoding/asn1/common.go
@@ -0,0 +1,185 @@
+// 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
+
+import (
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+// ASN.1 objects have metadata preceding them:
+// the tag: the type of the object
+// a flag denoting if this object is compound or not
+// the class type: the namespace of the tag
+// the length of the object, in bytes
+
+// Here are some standard tags and classes
+
+// ASN.1 tags represent the type of the following object.
+const (
+ TagBoolean = 1
+ TagInteger = 2
+ TagBitString = 3
+ TagOctetString = 4
+ TagNull = 5
+ TagOID = 6
+ TagEnum = 10
+ TagUTF8String = 12
+ TagSequence = 16
+ TagSet = 17
+ TagNumericString = 18
+ TagPrintableString = 19
+ TagT61String = 20
+ TagIA5String = 22
+ TagUTCTime = 23
+ TagGeneralizedTime = 24
+ TagGeneralString = 27
+ TagBMPString = 30
+)
+
+// ASN.1 class types represent the namespace of the tag.
+const (
+ ClassUniversal = 0
+ ClassApplication = 1
+ ClassContextSpecific = 2
+ ClassPrivate = 3
+)
+
+type tagAndLength struct {
+ class, tag, length int
+ isCompound bool
+}
+
+// ASN.1 has IMPLICIT and EXPLICIT tags, which can be translated as "instead
+// of" and "in addition to". When not specified, every primitive type has a
+// default tag in the UNIVERSAL class.
+//
+// For example: a BIT STRING is tagged [UNIVERSAL 3] by default (although ASN.1
+// doesn't actually have a UNIVERSAL keyword). However, by saying [IMPLICIT
+// CONTEXT-SPECIFIC 42], that means that the tag is replaced by another.
+//
+// On the other hand, if it said [EXPLICIT CONTEXT-SPECIFIC 10], then an
+// /additional/ tag would wrap the default tag. This explicit tag will have the
+// compound flag set.
+//
+// (This is used in order to remove ambiguity with optional elements.)
+//
+// You can layer EXPLICIT and IMPLICIT tags to an arbitrary depth, however we
+// don't support that here. We support a single layer of EXPLICIT or IMPLICIT
+// tagging with tag strings on the fields of a structure.
+
+// fieldParameters is the parsed representation of tag string from a structure field.
+type fieldParameters struct {
+ optional bool // true iff the field is OPTIONAL
+ explicit bool // true iff an EXPLICIT tag is in use.
+ application bool // true iff an APPLICATION tag is in use.
+ private bool // true iff a PRIVATE tag is in use.
+ defaultValue *int64 // a default value for INTEGER typed fields (maybe nil).
+ tag *int // the EXPLICIT or IMPLICIT tag (maybe nil).
+ stringType int // the string tag to use when marshaling.
+ timeType int // the time tag to use when marshaling.
+ set bool // true iff this should be encoded as a SET
+ omitEmpty bool // true iff this should be omitted if empty when marshaling.
+
+ // Invariants:
+ // if explicit is set, tag is non-nil.
+}
+
+// Given a tag string with the format specified in the package comment,
+// parseFieldParameters will parse it into a fieldParameters structure,
+// ignoring unknown parts of the string.
+func parseFieldParameters(str string) (ret fieldParameters) {
+ var part string
+ for len(str) > 0 {
+ part, str, _ = strings.Cut(str, ",")
+ switch {
+ case part == "optional":
+ ret.optional = true
+ case part == "explicit":
+ ret.explicit = true
+ if ret.tag == nil {
+ ret.tag = new(int)
+ }
+ case part == "generalized":
+ ret.timeType = TagGeneralizedTime
+ case part == "utc":
+ ret.timeType = TagUTCTime
+ case part == "ia5":
+ ret.stringType = TagIA5String
+ case part == "printable":
+ ret.stringType = TagPrintableString
+ case part == "numeric":
+ ret.stringType = TagNumericString
+ case part == "utf8":
+ ret.stringType = TagUTF8String
+ case strings.HasPrefix(part, "default:"):
+ i, err := strconv.ParseInt(part[8:], 10, 64)
+ if err == nil {
+ ret.defaultValue = new(int64)
+ *ret.defaultValue = i
+ }
+ case strings.HasPrefix(part, "tag:"):
+ i, err := strconv.Atoi(part[4:])
+ if err == nil {
+ ret.tag = new(int)
+ *ret.tag = i
+ }
+ case part == "set":
+ ret.set = true
+ case part == "application":
+ ret.application = true
+ if ret.tag == nil {
+ ret.tag = new(int)
+ }
+ case part == "private":
+ ret.private = true
+ if ret.tag == nil {
+ ret.tag = new(int)
+ }
+ case part == "omitempty":
+ ret.omitEmpty = true
+ }
+ }
+ return
+}
+
+// Given a reflected Go type, getUniversalType returns the default tag number
+// and expected compound flag.
+func getUniversalType(t reflect.Type) (matchAny bool, tagNumber int, isCompound, ok bool) {
+ switch t {
+ case rawValueType:
+ return true, -1, false, true
+ case objectIdentifierType:
+ return false, TagOID, false, true
+ case bitStringType:
+ return false, TagBitString, false, true
+ case timeType:
+ return false, TagUTCTime, false, true
+ case enumeratedType:
+ return false, TagEnum, false, true
+ case bigIntType:
+ return false, TagInteger, false, true
+ }
+ switch t.Kind() {
+ case reflect.Bool:
+ return false, TagBoolean, false, true
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return false, TagInteger, false, true
+ case reflect.Struct:
+ return false, TagSequence, true, true
+ case reflect.Slice:
+ if t.Elem().Kind() == reflect.Uint8 {
+ return false, TagOctetString, false, true
+ }
+ if strings.HasSuffix(t.Name(), "SET") {
+ return false, TagSet, true, true
+ }
+ return false, TagSequence, true, true
+ case reflect.String:
+ return false, TagPrintableString, false, true
+ }
+ return false, 0, false, false
+}
diff --git a/src/encoding/asn1/marshal.go b/src/encoding/asn1/marshal.go
new file mode 100644
index 0000000..c243349
--- /dev/null
+++ b/src/encoding/asn1/marshal.go
@@ -0,0 +1,747 @@
+// 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
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "math/big"
+ "reflect"
+ "sort"
+ "time"
+ "unicode/utf8"
+)
+
+var (
+ byte00Encoder encoder = byteEncoder(0x00)
+ byteFFEncoder encoder = byteEncoder(0xff)
+)
+
+// encoder represents an ASN.1 element that is waiting to be marshaled.
+type encoder interface {
+ // Len returns the number of bytes needed to marshal this element.
+ Len() int
+ // Encode encodes this element by writing Len() bytes to dst.
+ Encode(dst []byte)
+}
+
+type byteEncoder byte
+
+func (c byteEncoder) Len() int {
+ return 1
+}
+
+func (c byteEncoder) Encode(dst []byte) {
+ dst[0] = byte(c)
+}
+
+type bytesEncoder []byte
+
+func (b bytesEncoder) Len() int {
+ return len(b)
+}
+
+func (b bytesEncoder) Encode(dst []byte) {
+ if copy(dst, b) != len(b) {
+ panic("internal error")
+ }
+}
+
+type stringEncoder string
+
+func (s stringEncoder) Len() int {
+ return len(s)
+}
+
+func (s stringEncoder) Encode(dst []byte) {
+ if copy(dst, s) != len(s) {
+ panic("internal error")
+ }
+}
+
+type multiEncoder []encoder
+
+func (m multiEncoder) Len() int {
+ var size int
+ for _, e := range m {
+ size += e.Len()
+ }
+ return size
+}
+
+func (m multiEncoder) Encode(dst []byte) {
+ var off int
+ for _, e := range m {
+ e.Encode(dst[off:])
+ off += e.Len()
+ }
+}
+
+type setEncoder []encoder
+
+func (s setEncoder) Len() int {
+ var size int
+ for _, e := range s {
+ size += e.Len()
+ }
+ return size
+}
+
+func (s setEncoder) Encode(dst []byte) {
+ // Per X690 Section 11.6: The encodings of the component values of a
+ // set-of value shall appear in ascending order, the encodings being
+ // compared as octet strings with the shorter components being padded
+ // at their trailing end with 0-octets.
+ //
+ // First we encode each element to its TLV encoding and then use
+ // octetSort to get the ordering expected by X690 DER rules before
+ // writing the sorted encodings out to dst.
+ l := make([][]byte, len(s))
+ for i, e := range s {
+ l[i] = make([]byte, e.Len())
+ e.Encode(l[i])
+ }
+
+ sort.Slice(l, func(i, j int) bool {
+ // Since we are using bytes.Compare to compare TLV encodings we
+ // don't need to right pad s[i] and s[j] to the same length as
+ // suggested in X690. If len(s[i]) < len(s[j]) the length octet of
+ // s[i], which is the first determining byte, will inherently be
+ // smaller than the length octet of s[j]. This lets us skip the
+ // padding step.
+ return bytes.Compare(l[i], l[j]) < 0
+ })
+
+ var off int
+ for _, b := range l {
+ copy(dst[off:], b)
+ off += len(b)
+ }
+}
+
+type taggedEncoder struct {
+ // scratch contains temporary space for encoding the tag and length of
+ // an element in order to avoid extra allocations.
+ scratch [8]byte
+ tag encoder
+ body encoder
+}
+
+func (t *taggedEncoder) Len() int {
+ return t.tag.Len() + t.body.Len()
+}
+
+func (t *taggedEncoder) Encode(dst []byte) {
+ t.tag.Encode(dst)
+ t.body.Encode(dst[t.tag.Len():])
+}
+
+type int64Encoder int64
+
+func (i int64Encoder) Len() int {
+ n := 1
+
+ for i > 127 {
+ n++
+ i >>= 8
+ }
+
+ for i < -128 {
+ n++
+ i >>= 8
+ }
+
+ return n
+}
+
+func (i int64Encoder) Encode(dst []byte) {
+ n := i.Len()
+
+ for j := 0; j < n; j++ {
+ dst[j] = byte(i >> uint((n-1-j)*8))
+ }
+}
+
+func base128IntLength(n int64) int {
+ if n == 0 {
+ return 1
+ }
+
+ l := 0
+ for i := n; i > 0; i >>= 7 {
+ l++
+ }
+
+ return l
+}
+
+func appendBase128Int(dst []byte, n int64) []byte {
+ l := base128IntLength(n)
+
+ for i := l - 1; i >= 0; i-- {
+ o := byte(n >> uint(i*7))
+ o &= 0x7f
+ if i != 0 {
+ o |= 0x80
+ }
+
+ dst = append(dst, o)
+ }
+
+ return dst
+}
+
+func makeBigInt(n *big.Int) (encoder, error) {
+ if n == nil {
+ return nil, StructuralError{"empty integer"}
+ }
+
+ if n.Sign() < 0 {
+ // A negative number has to be converted to two's-complement
+ // form. So we'll invert and subtract 1. If the
+ // most-significant-bit isn't set then we'll need to pad the
+ // beginning with 0xff in order to keep the number negative.
+ nMinus1 := new(big.Int).Neg(n)
+ nMinus1.Sub(nMinus1, bigOne)
+ bytes := nMinus1.Bytes()
+ for i := range bytes {
+ bytes[i] ^= 0xff
+ }
+ if len(bytes) == 0 || bytes[0]&0x80 == 0 {
+ return multiEncoder([]encoder{byteFFEncoder, bytesEncoder(bytes)}), nil
+ }
+ return bytesEncoder(bytes), nil
+ } else if n.Sign() == 0 {
+ // Zero is written as a single 0 zero rather than no bytes.
+ return byte00Encoder, nil
+ } else {
+ bytes := n.Bytes()
+ if len(bytes) > 0 && bytes[0]&0x80 != 0 {
+ // We'll have to pad this with 0x00 in order to stop it
+ // looking like a negative number.
+ return multiEncoder([]encoder{byte00Encoder, bytesEncoder(bytes)}), nil
+ }
+ return bytesEncoder(bytes), nil
+ }
+}
+
+func appendLength(dst []byte, i int) []byte {
+ n := lengthLength(i)
+
+ for ; n > 0; n-- {
+ dst = append(dst, byte(i>>uint((n-1)*8)))
+ }
+
+ return dst
+}
+
+func lengthLength(i int) (numBytes int) {
+ numBytes = 1
+ for i > 255 {
+ numBytes++
+ i >>= 8
+ }
+ return
+}
+
+func appendTagAndLength(dst []byte, t tagAndLength) []byte {
+ b := uint8(t.class) << 6
+ if t.isCompound {
+ b |= 0x20
+ }
+ if t.tag >= 31 {
+ b |= 0x1f
+ dst = append(dst, b)
+ dst = appendBase128Int(dst, int64(t.tag))
+ } else {
+ b |= uint8(t.tag)
+ dst = append(dst, b)
+ }
+
+ if t.length >= 128 {
+ l := lengthLength(t.length)
+ dst = append(dst, 0x80|byte(l))
+ dst = appendLength(dst, t.length)
+ } else {
+ dst = append(dst, byte(t.length))
+ }
+
+ return dst
+}
+
+type bitStringEncoder BitString
+
+func (b bitStringEncoder) Len() int {
+ return len(b.Bytes) + 1
+}
+
+func (b bitStringEncoder) Encode(dst []byte) {
+ dst[0] = byte((8 - b.BitLength%8) % 8)
+ if copy(dst[1:], b.Bytes) != len(b.Bytes) {
+ panic("internal error")
+ }
+}
+
+type oidEncoder []int
+
+func (oid oidEncoder) Len() int {
+ l := base128IntLength(int64(oid[0]*40 + oid[1]))
+ for i := 2; i < len(oid); i++ {
+ l += base128IntLength(int64(oid[i]))
+ }
+ return l
+}
+
+func (oid oidEncoder) Encode(dst []byte) {
+ dst = appendBase128Int(dst[:0], int64(oid[0]*40+oid[1]))
+ for i := 2; i < len(oid); i++ {
+ dst = appendBase128Int(dst, int64(oid[i]))
+ }
+}
+
+func makeObjectIdentifier(oid []int) (e encoder, err error) {
+ if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
+ return nil, StructuralError{"invalid object identifier"}
+ }
+
+ return oidEncoder(oid), nil
+}
+
+func makePrintableString(s string) (e encoder, err error) {
+ for i := 0; i < len(s); i++ {
+ // The asterisk is often used in PrintableString, even though
+ // it is invalid. If a PrintableString was specifically
+ // requested then the asterisk is permitted by this code.
+ // Ampersand is allowed in parsing due a handful of CA
+ // certificates, however when making new certificates
+ // it is rejected.
+ if !isPrintable(s[i], allowAsterisk, rejectAmpersand) {
+ return nil, StructuralError{"PrintableString contains invalid character"}
+ }
+ }
+
+ return stringEncoder(s), nil
+}
+
+func makeIA5String(s string) (e encoder, err error) {
+ for i := 0; i < len(s); i++ {
+ if s[i] > 127 {
+ return nil, StructuralError{"IA5String contains invalid character"}
+ }
+ }
+
+ return stringEncoder(s), nil
+}
+
+func makeNumericString(s string) (e encoder, err error) {
+ for i := 0; i < len(s); i++ {
+ if !isNumeric(s[i]) {
+ return nil, StructuralError{"NumericString contains invalid character"}
+ }
+ }
+
+ return stringEncoder(s), nil
+}
+
+func makeUTF8String(s string) encoder {
+ return stringEncoder(s)
+}
+
+func appendTwoDigits(dst []byte, v int) []byte {
+ return append(dst, byte('0'+(v/10)%10), byte('0'+v%10))
+}
+
+func appendFourDigits(dst []byte, v int) []byte {
+ var bytes [4]byte
+ for i := range bytes {
+ bytes[3-i] = '0' + byte(v%10)
+ v /= 10
+ }
+ return append(dst, bytes[:]...)
+}
+
+func outsideUTCRange(t time.Time) bool {
+ year := t.Year()
+ return year < 1950 || year >= 2050
+}
+
+func makeUTCTime(t time.Time) (e encoder, err error) {
+ dst := make([]byte, 0, 18)
+
+ dst, err = appendUTCTime(dst, t)
+ if err != nil {
+ return nil, err
+ }
+
+ return bytesEncoder(dst), nil
+}
+
+func makeGeneralizedTime(t time.Time) (e encoder, err error) {
+ dst := make([]byte, 0, 20)
+
+ dst, err = appendGeneralizedTime(dst, t)
+ if err != nil {
+ return nil, err
+ }
+
+ return bytesEncoder(dst), nil
+}
+
+func appendUTCTime(dst []byte, t time.Time) (ret []byte, err error) {
+ year := t.Year()
+
+ switch {
+ case 1950 <= year && year < 2000:
+ dst = appendTwoDigits(dst, year-1900)
+ case 2000 <= year && year < 2050:
+ dst = appendTwoDigits(dst, year-2000)
+ default:
+ return nil, StructuralError{"cannot represent time as UTCTime"}
+ }
+
+ return appendTimeCommon(dst, t), nil
+}
+
+func appendGeneralizedTime(dst []byte, t time.Time) (ret []byte, err error) {
+ year := t.Year()
+ if year < 0 || year > 9999 {
+ return nil, StructuralError{"cannot represent time as GeneralizedTime"}
+ }
+
+ dst = appendFourDigits(dst, year)
+
+ return appendTimeCommon(dst, t), nil
+}
+
+func appendTimeCommon(dst []byte, t time.Time) []byte {
+ _, month, day := t.Date()
+
+ dst = appendTwoDigits(dst, int(month))
+ dst = appendTwoDigits(dst, day)
+
+ hour, min, sec := t.Clock()
+
+ dst = appendTwoDigits(dst, hour)
+ dst = appendTwoDigits(dst, min)
+ dst = appendTwoDigits(dst, sec)
+
+ _, offset := t.Zone()
+
+ switch {
+ case offset/60 == 0:
+ return append(dst, 'Z')
+ case offset > 0:
+ dst = append(dst, '+')
+ case offset < 0:
+ dst = append(dst, '-')
+ }
+
+ offsetMinutes := offset / 60
+ if offsetMinutes < 0 {
+ offsetMinutes = -offsetMinutes
+ }
+
+ dst = appendTwoDigits(dst, offsetMinutes/60)
+ dst = appendTwoDigits(dst, offsetMinutes%60)
+
+ return dst
+}
+
+func stripTagAndLength(in []byte) []byte {
+ _, offset, err := parseTagAndLength(in, 0)
+ if err != nil {
+ return in
+ }
+ return in[offset:]
+}
+
+func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error) {
+ switch value.Type() {
+ case flagType:
+ return bytesEncoder(nil), nil
+ case timeType:
+ t := value.Interface().(time.Time)
+ if params.timeType == TagGeneralizedTime || outsideUTCRange(t) {
+ return makeGeneralizedTime(t)
+ }
+ return makeUTCTime(t)
+ case bitStringType:
+ return bitStringEncoder(value.Interface().(BitString)), nil
+ case objectIdentifierType:
+ return makeObjectIdentifier(value.Interface().(ObjectIdentifier))
+ case bigIntType:
+ return makeBigInt(value.Interface().(*big.Int))
+ }
+
+ switch v := value; v.Kind() {
+ case reflect.Bool:
+ if v.Bool() {
+ return byteFFEncoder, nil
+ }
+ return byte00Encoder, nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return int64Encoder(v.Int()), nil
+ case reflect.Struct:
+ t := v.Type()
+
+ for i := 0; i < t.NumField(); i++ {
+ if !t.Field(i).IsExported() {
+ return nil, StructuralError{"struct contains unexported fields"}
+ }
+ }
+
+ startingField := 0
+
+ n := t.NumField()
+ if n == 0 {
+ return bytesEncoder(nil), nil
+ }
+
+ // If the first element of the structure is a non-empty
+ // RawContents, then we don't bother serializing the rest.
+ if t.Field(0).Type == rawContentsType {
+ s := v.Field(0)
+ if s.Len() > 0 {
+ bytes := s.Bytes()
+ /* The RawContents will contain the tag and
+ * length fields but we'll also be writing
+ * those ourselves, so we strip them out of
+ * bytes */
+ return bytesEncoder(stripTagAndLength(bytes)), nil
+ }
+
+ startingField = 1
+ }
+
+ switch n1 := n - startingField; n1 {
+ case 0:
+ return bytesEncoder(nil), nil
+ case 1:
+ return makeField(v.Field(startingField), parseFieldParameters(t.Field(startingField).Tag.Get("asn1")))
+ default:
+ m := make([]encoder, n1)
+ for i := 0; i < n1; i++ {
+ m[i], err = makeField(v.Field(i+startingField), parseFieldParameters(t.Field(i+startingField).Tag.Get("asn1")))
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return multiEncoder(m), nil
+ }
+ case reflect.Slice:
+ sliceType := v.Type()
+ if sliceType.Elem().Kind() == reflect.Uint8 {
+ return bytesEncoder(v.Bytes()), nil
+ }
+
+ var fp fieldParameters
+
+ switch l := v.Len(); l {
+ case 0:
+ return bytesEncoder(nil), nil
+ case 1:
+ return makeField(v.Index(0), fp)
+ default:
+ m := make([]encoder, l)
+
+ for i := 0; i < l; i++ {
+ m[i], err = makeField(v.Index(i), fp)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if params.set {
+ return setEncoder(m), nil
+ }
+ return multiEncoder(m), nil
+ }
+ case reflect.String:
+ switch params.stringType {
+ case TagIA5String:
+ return makeIA5String(v.String())
+ case TagPrintableString:
+ return makePrintableString(v.String())
+ case TagNumericString:
+ return makeNumericString(v.String())
+ default:
+ return makeUTF8String(v.String()), nil
+ }
+ }
+
+ return nil, StructuralError{"unknown Go type"}
+}
+
+func makeField(v reflect.Value, params fieldParameters) (e encoder, err error) {
+ if !v.IsValid() {
+ return nil, fmt.Errorf("asn1: cannot marshal nil value")
+ }
+ // If the field is an interface{} then recurse into it.
+ if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 {
+ return makeField(v.Elem(), params)
+ }
+
+ if v.Kind() == reflect.Slice && v.Len() == 0 && params.omitEmpty {
+ return bytesEncoder(nil), nil
+ }
+
+ if params.optional && params.defaultValue != nil && canHaveDefaultValue(v.Kind()) {
+ defaultValue := reflect.New(v.Type()).Elem()
+ defaultValue.SetInt(*params.defaultValue)
+
+ if reflect.DeepEqual(v.Interface(), defaultValue.Interface()) {
+ return bytesEncoder(nil), nil
+ }
+ }
+
+ // If no default value is given then the zero value for the type is
+ // assumed to be the default value. This isn't obviously the correct
+ // behavior, but it's what Go has traditionally done.
+ if params.optional && params.defaultValue == nil {
+ if reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) {
+ return bytesEncoder(nil), nil
+ }
+ }
+
+ if v.Type() == rawValueType {
+ rv := v.Interface().(RawValue)
+ if len(rv.FullBytes) != 0 {
+ return bytesEncoder(rv.FullBytes), nil
+ }
+
+ t := new(taggedEncoder)
+
+ t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound}))
+ t.body = bytesEncoder(rv.Bytes)
+
+ return t, nil
+ }
+
+ matchAny, tag, isCompound, ok := getUniversalType(v.Type())
+ if !ok || matchAny {
+ return nil, StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())}
+ }
+
+ if params.timeType != 0 && tag != TagUTCTime {
+ return nil, StructuralError{"explicit time type given to non-time member"}
+ }
+
+ if params.stringType != 0 && tag != TagPrintableString {
+ return nil, StructuralError{"explicit string type given to non-string member"}
+ }
+
+ switch tag {
+ case TagPrintableString:
+ if params.stringType == 0 {
+ // This is a string without an explicit string type. We'll use
+ // a PrintableString if the character set in the string is
+ // sufficiently limited, otherwise we'll use a UTF8String.
+ for _, r := range v.String() {
+ if r >= utf8.RuneSelf || !isPrintable(byte(r), rejectAsterisk, rejectAmpersand) {
+ if !utf8.ValidString(v.String()) {
+ return nil, errors.New("asn1: string not valid UTF-8")
+ }
+ tag = TagUTF8String
+ break
+ }
+ }
+ } else {
+ tag = params.stringType
+ }
+ case TagUTCTime:
+ if params.timeType == TagGeneralizedTime || outsideUTCRange(v.Interface().(time.Time)) {
+ tag = TagGeneralizedTime
+ }
+ }
+
+ if params.set {
+ if tag != TagSequence {
+ return nil, StructuralError{"non sequence tagged as set"}
+ }
+ tag = TagSet
+ }
+
+ // makeField can be called for a slice that should be treated as a SET
+ // but doesn't have params.set set, for instance when using a slice
+ // with the SET type name suffix. In this case getUniversalType returns
+ // TagSet, but makeBody doesn't know about that so will treat the slice
+ // as a sequence. To work around this we set params.set.
+ if tag == TagSet && !params.set {
+ params.set = true
+ }
+
+ t := new(taggedEncoder)
+
+ t.body, err = makeBody(v, params)
+ if err != nil {
+ return nil, err
+ }
+
+ bodyLen := t.body.Len()
+
+ class := ClassUniversal
+ if params.tag != nil {
+ if params.application {
+ class = ClassApplication
+ } else if params.private {
+ class = ClassPrivate
+ } else {
+ class = ClassContextSpecific
+ }
+
+ if params.explicit {
+ t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{ClassUniversal, tag, bodyLen, isCompound}))
+
+ tt := new(taggedEncoder)
+
+ tt.body = t
+
+ tt.tag = bytesEncoder(appendTagAndLength(tt.scratch[:0], tagAndLength{
+ class: class,
+ tag: *params.tag,
+ length: bodyLen + t.tag.Len(),
+ isCompound: true,
+ }))
+
+ return tt, nil
+ }
+
+ // implicit tag.
+ tag = *params.tag
+ }
+
+ t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{class, tag, bodyLen, isCompound}))
+
+ return t, nil
+}
+
+// Marshal returns the ASN.1 encoding of val.
+//
+// In addition to the struct tags recognised by Unmarshal, the following can be
+// used:
+//
+// ia5: causes strings to be marshaled as ASN.1, IA5String values
+// omitempty: causes empty slices to be skipped
+// printable: causes strings to be marshaled as ASN.1, PrintableString values
+// utf8: causes strings to be marshaled as ASN.1, UTF8String values
+// utc: causes time.Time to be marshaled as ASN.1, UTCTime values
+// generalized: causes time.Time to be marshaled as ASN.1, GeneralizedTime values
+func Marshal(val any) ([]byte, error) {
+ return MarshalWithParams(val, "")
+}
+
+// MarshalWithParams allows field parameters to be specified for the
+// top-level element. The form of the params is the same as the field tags.
+func MarshalWithParams(val any, params string) ([]byte, error) {
+ e, err := makeField(reflect.ValueOf(val), parseFieldParameters(params))
+ if err != nil {
+ return nil, err
+ }
+ b := make([]byte, e.Len())
+ e.Encode(b)
+ return b, nil
+}
diff --git a/src/encoding/asn1/marshal_test.go b/src/encoding/asn1/marshal_test.go
new file mode 100644
index 0000000..d9c3cf4
--- /dev/null
+++ b/src/encoding/asn1/marshal_test.go
@@ -0,0 +1,406 @@
+// 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
+
+import (
+ "bytes"
+ "encoding/hex"
+ "math/big"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+)
+
+type intStruct struct {
+ A int
+}
+
+type twoIntStruct struct {
+ A int
+ B int
+}
+
+type bigIntStruct struct {
+ A *big.Int
+}
+
+type nestedStruct struct {
+ A intStruct
+}
+
+type rawContentsStruct struct {
+ Raw RawContent
+ A int
+}
+
+type implicitTagTest struct {
+ A int `asn1:"implicit,tag:5"`
+}
+
+type explicitTagTest struct {
+ A int `asn1:"explicit,tag:5"`
+}
+
+type flagTest struct {
+ A Flag `asn1:"tag:0,optional"`
+}
+
+type generalizedTimeTest struct {
+ A time.Time `asn1:"generalized"`
+}
+
+type ia5StringTest struct {
+ A string `asn1:"ia5"`
+}
+
+type printableStringTest struct {
+ A string `asn1:"printable"`
+}
+
+type genericStringTest struct {
+ A string
+}
+
+type optionalRawValueTest struct {
+ A RawValue `asn1:"optional"`
+}
+
+type omitEmptyTest struct {
+ A []string `asn1:"omitempty"`
+}
+
+type defaultTest struct {
+ A int `asn1:"optional,default:1"`
+}
+
+type applicationTest struct {
+ A int `asn1:"application,tag:0"`
+ B int `asn1:"application,tag:1,explicit"`
+}
+
+type privateTest struct {
+ A int `asn1:"private,tag:0"`
+ B int `asn1:"private,tag:1,explicit"`
+ C int `asn1:"private,tag:31"` // tag size should be 2 octet
+ D int `asn1:"private,tag:128"` // tag size should be 3 octet
+}
+
+type numericStringTest struct {
+ A string `asn1:"numeric"`
+}
+
+type testSET []int
+
+var PST = time.FixedZone("PST", -8*60*60)
+
+type marshalTest struct {
+ in any
+ out string // hex encoded
+}
+
+func farFuture() time.Time {
+ t, err := time.Parse(time.RFC3339, "2100-04-05T12:01:01Z")
+ if err != nil {
+ panic(err)
+ }
+ return t
+}
+
+var marshalTests = []marshalTest{
+ {10, "02010a"},
+ {127, "02017f"},
+ {128, "02020080"},
+ {-128, "020180"},
+ {-129, "0202ff7f"},
+ {intStruct{64}, "3003020140"},
+ {bigIntStruct{big.NewInt(0x123456)}, "30050203123456"},
+ {twoIntStruct{64, 65}, "3006020140020141"},
+ {nestedStruct{intStruct{127}}, "3005300302017f"},
+ {[]byte{1, 2, 3}, "0403010203"},
+ {implicitTagTest{64}, "3003850140"},
+ {explicitTagTest{64}, "3005a503020140"},
+ {flagTest{true}, "30028000"},
+ {flagTest{false}, "3000"},
+ {time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"},
+ {time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"},
+ {time.Unix(1258325776, 0).In(PST), "17113039313131353134353631362d30383030"},
+ {farFuture(), "180f32313030303430353132303130315a"},
+ {generalizedTimeTest{time.Unix(1258325776, 0).UTC()}, "3011180f32303039313131353232353631365a"},
+ {BitString{[]byte{0x80}, 1}, "03020780"},
+ {BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"},
+ {ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"},
+ {ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"},
+ {ObjectIdentifier([]int{2, 100, 3}), "0603813403"},
+ {"test", "130474657374"},
+ {
+ "" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 127 times 'x'
+ "137f" +
+ "7878787878787878787878787878787878787878787878787878787878787878" +
+ "7878787878787878787878787878787878787878787878787878787878787878" +
+ "7878787878787878787878787878787878787878787878787878787878787878" +
+ "78787878787878787878787878787878787878787878787878787878787878",
+ },
+ {
+ "" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 128 times 'x'
+ "138180" +
+ "7878787878787878787878787878787878787878787878787878787878787878" +
+ "7878787878787878787878787878787878787878787878787878787878787878" +
+ "7878787878787878787878787878787878787878787878787878787878787878" +
+ "7878787878787878787878787878787878787878787878787878787878787878",
+ },
+ {ia5StringTest{"test"}, "3006160474657374"},
+ {optionalRawValueTest{}, "3000"},
+ {printableStringTest{"test"}, "3006130474657374"},
+ {printableStringTest{"test*"}, "30071305746573742a"},
+ {genericStringTest{"test"}, "3006130474657374"},
+ {genericStringTest{"test*"}, "30070c05746573742a"},
+ {genericStringTest{"test&"}, "30070c057465737426"},
+ {rawContentsStruct{nil, 64}, "3003020140"},
+ {rawContentsStruct{[]byte{0x30, 3, 1, 2, 3}, 64}, "3003010203"},
+ {RawValue{Tag: 1, Class: 2, IsCompound: false, Bytes: []byte{1, 2, 3}}, "8103010203"},
+ {testSET([]int{10}), "310302010a"},
+ {omitEmptyTest{[]string{}}, "3000"},
+ {omitEmptyTest{[]string{"1"}}, "30053003130131"},
+ {"Σ", "0c02cea3"},
+ {defaultTest{0}, "3003020100"},
+ {defaultTest{1}, "3000"},
+ {defaultTest{2}, "3003020102"},
+ {applicationTest{1, 2}, "30084001016103020102"},
+ {privateTest{1, 2, 3, 4}, "3011c00101e103020102df1f0103df81000104"},
+ {numericStringTest{"1 9"}, "30051203312039"},
+}
+
+func TestMarshal(t *testing.T) {
+ for i, test := range marshalTests {
+ data, err := Marshal(test.in)
+ if err != nil {
+ t.Errorf("#%d failed: %s", i, err)
+ }
+ out, _ := hex.DecodeString(test.out)
+ if !bytes.Equal(out, data) {
+ t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out)
+
+ }
+ }
+}
+
+type marshalWithParamsTest struct {
+ in any
+ params string
+ out string // hex encoded
+}
+
+var marshalWithParamsTests = []marshalWithParamsTest{
+ {intStruct{10}, "set", "310302010a"},
+ {intStruct{10}, "application", "600302010a"},
+ {intStruct{10}, "private", "e00302010a"},
+}
+
+func TestMarshalWithParams(t *testing.T) {
+ for i, test := range marshalWithParamsTests {
+ data, err := MarshalWithParams(test.in, test.params)
+ if err != nil {
+ t.Errorf("#%d failed: %s", i, err)
+ }
+ out, _ := hex.DecodeString(test.out)
+ if !bytes.Equal(out, data) {
+ t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out)
+
+ }
+ }
+}
+
+type marshalErrTest struct {
+ in any
+ err string
+}
+
+var marshalErrTests = []marshalErrTest{
+ {bigIntStruct{nil}, "empty integer"},
+ {numericStringTest{"a"}, "invalid character"},
+ {ia5StringTest{"\xb0"}, "invalid character"},
+ {printableStringTest{"!"}, "invalid character"},
+}
+
+func TestMarshalError(t *testing.T) {
+ for i, test := range marshalErrTests {
+ _, err := Marshal(test.in)
+ if err == nil {
+ t.Errorf("#%d should fail, but success", i)
+ continue
+ }
+
+ if !strings.Contains(err.Error(), test.err) {
+ t.Errorf("#%d got: %v want %v", i, err, test.err)
+ }
+ }
+}
+
+func TestInvalidUTF8(t *testing.T) {
+ _, err := Marshal(string([]byte{0xff, 0xff}))
+ if err == nil {
+ t.Errorf("invalid UTF8 string was accepted")
+ }
+}
+
+func TestMarshalOID(t *testing.T) {
+ var marshalTestsOID = []marshalTest{
+ {[]byte("\x06\x01\x30"), "0403060130"}, // bytes format returns a byte sequence \x04
+ // {ObjectIdentifier([]int{0}), "060100"}, // returns an error as OID 0.0 has the same encoding
+ {[]byte("\x06\x010"), "0403060130"}, // same as above "\x06\x010" = "\x06\x01" + "0"
+ {ObjectIdentifier([]int{2, 999, 3}), "0603883703"}, // Example of ITU-T X.690
+ {ObjectIdentifier([]int{0, 0}), "060100"}, // zero OID
+ }
+ for i, test := range marshalTestsOID {
+ data, err := Marshal(test.in)
+ if err != nil {
+ t.Errorf("#%d failed: %s", i, err)
+ }
+ out, _ := hex.DecodeString(test.out)
+ if !bytes.Equal(out, data) {
+ t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out)
+ }
+ }
+}
+
+func TestIssue11130(t *testing.T) {
+ data := []byte("\x06\x010") // == \x06\x01\x30 == OID = 0 (the figure)
+ var v any
+ // v has Zero value here and Elem() would panic
+ _, err := Unmarshal(data, &v)
+ if err != nil {
+ t.Errorf("%v", err)
+ return
+ }
+ if reflect.TypeOf(v).String() != reflect.TypeOf(ObjectIdentifier{}).String() {
+ t.Errorf("marshal OID returned an invalid type")
+ return
+ }
+
+ data1, err := Marshal(v)
+ if err != nil {
+ t.Errorf("%v", err)
+ return
+ }
+
+ if !bytes.Equal(data, data1) {
+ t.Errorf("got: %q, want: %q \n", data1, data)
+ return
+ }
+
+ var v1 any
+ _, err = Unmarshal(data1, &v1)
+ if err != nil {
+ t.Errorf("%v", err)
+ return
+ }
+ if !reflect.DeepEqual(v, v1) {
+ t.Errorf("got: %#v data=%q, want : %#v data=%q\n ", v1, data1, v, data)
+ }
+}
+
+func BenchmarkMarshal(b *testing.B) {
+ b.ReportAllocs()
+
+ for i := 0; i < b.N; i++ {
+ for _, test := range marshalTests {
+ Marshal(test.in)
+ }
+ }
+}
+
+func TestSetEncoder(t *testing.T) {
+ testStruct := struct {
+ Strings []string `asn1:"set"`
+ }{
+ Strings: []string{"a", "aa", "b", "bb", "c", "cc"},
+ }
+
+ // Expected ordering of the SET should be:
+ // a, b, c, aa, bb, cc
+
+ output, err := Marshal(testStruct)
+ if err != nil {
+ t.Errorf("%v", err)
+ }
+
+ expectedOrder := []string{"a", "b", "c", "aa", "bb", "cc"}
+ var resultStruct struct {
+ Strings []string `asn1:"set"`
+ }
+ rest, err := Unmarshal(output, &resultStruct)
+ if err != nil {
+ t.Errorf("%v", err)
+ }
+ if len(rest) != 0 {
+ t.Error("Unmarshal returned extra garbage")
+ }
+ if !reflect.DeepEqual(expectedOrder, resultStruct.Strings) {
+ t.Errorf("Unexpected SET content. got: %s, want: %s", resultStruct.Strings, expectedOrder)
+ }
+}
+
+func TestSetEncoderSETSliceSuffix(t *testing.T) {
+ type testSetSET []string
+ testSet := testSetSET{"a", "aa", "b", "bb", "c", "cc"}
+
+ // Expected ordering of the SET should be:
+ // a, b, c, aa, bb, cc
+
+ output, err := Marshal(testSet)
+ if err != nil {
+ t.Errorf("%v", err)
+ }
+
+ expectedOrder := testSetSET{"a", "b", "c", "aa", "bb", "cc"}
+ var resultSet testSetSET
+ rest, err := Unmarshal(output, &resultSet)
+ if err != nil {
+ t.Errorf("%v", err)
+ }
+ if len(rest) != 0 {
+ t.Error("Unmarshal returned extra garbage")
+ }
+ if !reflect.DeepEqual(expectedOrder, resultSet) {
+ t.Errorf("Unexpected SET content. got: %s, want: %s", resultSet, expectedOrder)
+ }
+}
+
+func BenchmarkUnmarshal(b *testing.B) {
+ b.ReportAllocs()
+
+ type testCase struct {
+ in []byte
+ out any
+ }
+ var testData []testCase
+ for _, test := range unmarshalTestData {
+ pv := reflect.New(reflect.TypeOf(test.out).Elem())
+ inCopy := make([]byte, len(test.in))
+ copy(inCopy, test.in)
+ outCopy := pv.Interface()
+
+ testData = append(testData, testCase{
+ in: inCopy,
+ out: outCopy,
+ })
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ for _, testCase := range testData {
+ _, _ = Unmarshal(testCase.in, testCase.out)
+ }
+ }
+}