// 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" "internal/saferio" "math" "math/big" "slices" "strconv" "bytes" "time" "unicode/utf16" "unicode/utf8" "unsafe" ) // 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 []byte } 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 []byte } 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 } // parseInt32 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 := &big.Int{} if len(bytes) > 0 && bytes[0]&0x80 == 0x80 { // This is a negative number. notBytes := []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 0. 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 := []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< 0 { s.WriteByte('.') } s.Write(strconv.AppendInt(buf, int64(v), 10)) } return s.String() } // 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 = []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 := []byte(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 = "20060102150405.999999999Z0700" s := []byte(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 []byte, err error) { for _, b := range bytes { if !isNumeric(b) { return "", SyntaxError{"NumericString contains invalid character"} } } return []byte(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 []byte, err error) { for _, b := range bytes { if !isPrintable(b, allowAsterisk, allowAmpersand) { err = SyntaxError{"PrintableString contains invalid character"} return } } ret = []byte(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 []byte, err error) { for _, b := range bytes { if b >= utf8.RuneSelf { err = SyntaxError{"IA5String contains invalid character"} return } } ret = []byte(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 []byte, err error) { // T.61 is a defunct ITU 8-bit character encoding which preceded Unicode. // T.61 uses a code page layout that _almost_ exactly maps to the code // page layout of the ISO 8859-1 (Latin-1) character encoding, with the // exception that a number of characters in Latin-1 are not present // in T.61. // // Instead of mapping which characters are present in Latin-1 but not T.61, // we just treat these strings as being encoded using Latin-1. This matches // what most of the world does, including BoringSSL. buf := []byte{:0:len(bytes)} for _, v := range bytes { // All the 1-byte UTF-8 runes map 1-1 with Latin-1. buf = utf8.AppendRune(buf, rune(v)) } return []byte(buf), nil } // UTF8String // parseUTF8String parses an ASN.1 UTF8String (raw UTF-8) from the given byte // array and returns it. func parseUTF8String(bytes []byte) (ret []byte, err error) { if !utf8.Valid(bytes) { return "", errors.New("asn1: invalid UTF-8 string") } return []byte(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) ([]byte, error) { // BMPString uses the defunct UCS-2 16-bit character encoding, which // covers the Basic Multilingual Plane (BMP). UTF-16 was an extension of // UCS-2, containing all of the same code points, but also including // multi-code point characters (by using surrogate code points). We can // treat a UCS-2 encoded string as a UTF-16 encoded string, as long as // we reject out the UTF-16 specific code points. This matches the // BoringSSL behavior. if len(bmpString)%2 != 0 { return "", errors.New("invalid BMPString") } // Strip terminator if present. if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 { bmpString = bmpString[:l-2] } s := []uint16{:0:len(bmpString)/2} for len(bmpString) > 0 { point := uint16(bmpString[0])<<8 + uint16(bmpString[1]) // Reject UTF-16 code points that are permanently reserved // noncharacters (0xfffe, 0xffff, and 0xfdd0-0xfdef) and surrogates // (0xd800-0xdfff). if point == 0xfffe || point == 0xffff || (point >= 0xfdd0 && point <= 0xfdef) || (point >= 0xd800 && point <= 0xdfff) { return "", errors.New("invalid BMPString") } s = append(s, point) bmpString = bmpString[2:] } runes := utf16.Decode(s) buf := []byte{:0:len(runes)*3} var tmp [utf8.UTFMax]byte for _, r := range runes { n := utf8.EncodeRune(tmp[:], r) buf = append(buf, tmp[:n]...) } return buf, 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. // parseSequenceOfUnsafe parses a SEQUENCE OF / SET OF into a raw slice buffer. // elemType is the element type code. Returns a pointer to a _sliceHeader. func parseSequenceOfUnsafe(data []byte, elemType *_rawType) (slicePtr unsafe.Pointer, err error) { matchAny, expectedTag, compoundType, ok := getUniversalType(elemType) if !ok { err = StructuralError{"unknown Go type for slice"} return } numElements := 0 for offset := 0; offset < len(data); { var t tagAndLength t, offset, err = parseTagAndLength(data, offset) if err != nil { return } switch t.tag { case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString, TagBMPString: t.tag = TagPrintableString case TagGeneralizedTime, TagUTCTime: 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(data)) { err = SyntaxError{"truncated sequence"} return } offset += t.length numElements++ } elemSize := uint64(elemType.size()) safeCap := saferio.SliceCapWithSize(elemSize, uint64(numElements)) if safeCap < 0 { err = SyntaxError{fmt.Sprintf("slice too big: %d elements of %d bytes", numElements, elemSize)} return } slicePtr = makeSliceOf(elemType, numElements, safeCap) params := fieldParameters{} offset := 0 for i := 0; i < numElements; i++ { elemPtr := sliceIndex(slicePtr, elemType, i) offset, err = parseFieldUnsafe(elemType, elemPtr, data, offset, params) if err != nil { return } } return } // Type code pointers for known ASN.1 types. Obtained by decomposing // a nil pointer interface to extract the compiler's type descriptor. var ( _bitStringTC = typeCodeOf((*BitString)(nil)).elem() _objectIdentifierTC = typeCodeOf((*ObjectIdentifier)(nil)).elem() _enumeratedTC = typeCodeOf((*Enumerated)(nil)).elem() _flagTC = typeCodeOf((*Flag)(nil)).elem() _timeTC = typeCodeOf((*time.Time)(nil)).elem() _rawValueTC = typeCodeOf((*RawValue)(nil)).elem() _rawContentsTC = typeCodeOf((*RawContent)(nil)).elem() _bigIntPtrTC = typeCodeOf((**big.Int)(nil)).elem() // *big.Int, not 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 } // getASN1Tag extracts the "asn1" value from a raw struct tag string. func getASN1Tag(tag []byte) []byte { key := []byte(`asn1:"`) i := bytes.Index(tag, key) if i < 0 { return nil } tag = tag[i+len(key):] j := bytes.IndexByte(tag, '"') if j < 0 { return tag } return tag[:j] } // parseFieldUnsafe is the main parsing function. Given a type code, a pointer // to the destination, and a byte slice with offset, it parses an ASN.1 value // and writes it directly through the unsafe pointer. func parseFieldUnsafe(typ *_rawType, ptr unsafe.Pointer, data []byte, initOffset int, params fieldParameters) (offset int, err error) { offset = initOffset if offset == len(data) { if !setDefaultValueUnsafe(typ, ptr, params) { err = SyntaxError{"sequence truncated"} } return } // Deal with the ANY type (interface{}). if typ.kind() == akInterface { var t tagAndLength t, offset, err = parseTagAndLength(data, offset) if err != nil { return } if invalidLength(offset, t.length, len(data)) { err = SyntaxError{"data truncated"} return } var result any if !t.isCompound && t.class == ClassUniversal { innerBytes := data[offset : offset+t.length] switch t.tag { case TagBoolean: result, err = parseBool(innerBytes) 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) } } offset += t.length if err != nil { return } if result != nil { *(*any)(ptr) = result } return } t, offset, err := parseTagAndLength(data, offset) if err != nil { return } if params.explicit { expectedClass := ClassContextSpecific if params.application { expectedClass = ClassApplication } if offset == len(data) { err = StructuralError{"explicit tag has no child"} return } if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) { if typ == _rawValueTC { // The inner element should not be parsed for RawValues. } else if t.length > 0 { t, offset, err = parseTagAndLength(data, offset) if err != nil { return } } else { if typ != _flagTC { err = StructuralError{"zero length explicit tag was not an asn1.Flag"} return } *(*Flag)(ptr) = true return } } else { ok := setDefaultValueUnsafe(typ, ptr, params) if ok { offset = initOffset } else { err = StructuralError{"explicitly tagged member didn't match"} } return } } matchAny, universalTag, compoundType, ok1 := getUniversalType(typ) if !ok1 { err = StructuralError{fmt.Sprintf("unknown Go type (kind %d)", typ.kind())} 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 { if t.class == ClassUniversal { if t.tag == TagGeneralizedTime { universalTag = t.tag } } else if params.timeType != 0 { universalTag = params.timeType } } 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) { ok := setDefaultValueUnsafe(typ, ptr, params) if ok { offset = initOffset } else { err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, typ.typeName(), offset)} } return } if invalidLength(offset, t.length, len(data)) { err = SyntaxError{"data truncated"} return } innerBytes := data[offset : offset+t.length] offset += t.length // Known concrete types by type code pointer comparison. if typ == _rawValueTC { *(*RawValue)(ptr) = RawValue{t.class, t.tag, t.isCompound, innerBytes, data[initOffset:offset]} return } if typ == _objectIdentifierTC { oid, e := parseObjectIdentifier(innerBytes) if e != nil { err = e return } *(*ObjectIdentifier)(ptr) = oid return } if typ == _bitStringTC { bs, e := parseBitString(innerBytes) if e != nil { err = e return } *(*BitString)(ptr) = bs return } if typ == _timeTC { var tv time.Time var e error if universalTag == TagUTCTime { tv, e = parseUTCTime(innerBytes) } else { tv, e = parseGeneralizedTime(innerBytes) } if e != nil { err = e return } *(*time.Time)(ptr) = tv return } if typ == _enumeratedTC { iv, e := parseInt32(innerBytes) if e != nil { err = e return } *(*Enumerated)(ptr) = Enumerated(iv) return } if typ == _flagTC { *(*Flag)(ptr) = true return } if typ == _bigIntPtrTC { bi, e := parseBigInt(innerBytes) if e != nil { err = e return } *(**big.Int)(ptr) = bi return } // Kind-based dispatch for primitive types. switch typ.kind() { case akBool: bv, e := parseBool(innerBytes) if e == nil { *(*bool)(ptr) = bv } err = e return case akInt, akInt32, akInt64: if typ.size() == 4 { iv, e := parseInt32(innerBytes) if e == nil { *(*int32)(ptr) = iv } err = e } else { iv, e := parseInt64(innerBytes) if e == nil { *(*int64)(ptr) = iv } err = e } return case akStruct: for i := 0; i < typ.numField(); i++ { if !typ.fieldExported(i) { err = StructuralError{"struct contains unexported fields"} return } } if typ.numField() > 0 && typ.fieldType(0) == _rawContentsTC { rc := RawContent(data[initOffset:offset]) *(*RawContent)(unsafe.Add(ptr, typ.fieldOffset(0))) = rc } innerOffset := 0 for i := 0; i < typ.numField(); i++ { if i == 0 && typ.fieldType(0) == _rawContentsTC { continue } ft := typ.fieldType(i) fp := unsafe.Add(ptr, typ.fieldOffset(i)) tag := getASN1Tag(typ.fieldTag(i)) innerOffset, err = parseFieldUnsafe(ft, fp, innerBytes, innerOffset, parseFieldParameters(tag)) if err != nil { return } } return case akSlice: if typ.elem().kind() == akUint8 { dst := make([]byte, len(innerBytes)) copy(dst, innerBytes) *(*[]byte)(ptr) = dst return } slicePtr, e := parseSequenceOfUnsafe(innerBytes, typ.elem()) if e == nil { *(*_sliceHeader)(ptr) = *(*_sliceHeader)(slicePtr) } err = e return case akBytes: var sv []byte switch universalTag { case TagPrintableString: sv, err = parsePrintableString(innerBytes) case TagNumericString: sv, err = parseNumericString(innerBytes) case TagIA5String: sv, err = parseIA5String(innerBytes) case TagT61String: sv, err = parseT61String(innerBytes) case TagUTF8String: sv, err = parseUTF8String(innerBytes) case TagGeneralString: sv, err = parseT61String(innerBytes) case TagBMPString: sv, err = parseBMPString(innerBytes) default: err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} } if err == nil { *(*[]byte)(ptr) = sv } return } err = StructuralError{"unsupported: " + typ.typeName()} return } // setDefaultValueUnsafe installs a default value from tag parameters. // Returns true if the field was optional (even if no default was set). func setDefaultValueUnsafe(typ *_rawType, ptr unsafe.Pointer, params fieldParameters) bool { if !params.optional { return false } if params.defaultValue == nil { return true } switch typ.kind() { case akInt, akInt8, akInt16, akInt32, akInt64: switch typ.size() { case 1: *(*int8)(ptr) = int8(*params.defaultValue) case 2: *(*int16)(ptr) = int16(*params.defaultValue) case 4: *(*int32)(ptr) = int32(*params.defaultValue) default: *(*int64)(ptr) = *params.defaultValue } } return true } // Unmarshal parses the DER-encoded ASN.1 data structure b // and fills in an arbitrary value pointed at by val. // val must be a non-nil pointer. func Unmarshal(b []byte, val any) (rest []byte, err error) { return UnmarshalWithParams(b, val, "") } // invalidUnmarshalError describes an invalid argument passed to Unmarshal. type invalidUnmarshalError struct { typeName []byte isNil bool isNonPtr bool } func (e *invalidUnmarshalError) Error() string { if e.isNil && len(e.typeName) == 0 { return "asn1: Unmarshal recipient value is nil" } if e.isNonPtr { return "asn1: Unmarshal recipient value is non-pointer " + e.typeName } return "asn1: Unmarshal recipient value is nil " + e.typeName } // 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 []byte) (rest []byte, err error) { elemType, dataPtr := derefPtr(val) if elemType == nil { tc := typeCodeOf(val) name := []byte("unknown") if tc != nil { if n := tc.typeName(); len(n) > 0 { name = n } } return nil, &invalidUnmarshalError{typeName: name, isNonPtr: true} } if dataPtr == nil { name := elemType.typeName() if len(name) == 0 { name = []byte("unknown") } return nil, &invalidUnmarshalError{typeName: name, isNil: true} } offset, err := parseFieldUnsafe(elemType, dataPtr, b, 0, parseFieldParameters(params)) if err != nil { return nil, err } return b[offset:], nil }