Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1141 lines
33 KiB
1141 lines
33 KiB
// 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. |
|
// |
|
// This is a fork of the Go standard library ASN.1 implementation |
|
// (encoding/asn1). The main difference is that this version tries to correct |
|
// for errors (e.g. use of tagPrintableString when the string data is really |
|
// ISO8859-1 - a common error present in many x509 certificates in the wild.) |
|
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" |
|
"strings" |
|
"time" |
|
"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 |
|
Field string |
|
} |
|
|
|
func (e StructuralError) Error() string { |
|
var prefix string |
|
if e.Field != "" { |
|
prefix = e.Field + ": " |
|
} |
|
return "asn1: structure error: " + prefix + e.Msg |
|
} |
|
|
|
// A SyntaxError suggests that the ASN.1 data is invalid. |
|
type SyntaxError struct { |
|
Msg string |
|
Field string |
|
} |
|
|
|
func (e SyntaxError) Error() string { |
|
var prefix string |
|
if e.Field != "" { |
|
prefix = e.Field + ": " |
|
} |
|
return "asn1: syntax error: " + prefix + e.Msg |
|
} |
|
|
|
// We start by dealing with each of the primitive types in turn. |
|
|
|
// BOOLEAN |
|
|
|
func parseBool(bytes []byte, fieldName string) (ret bool, err error) { |
|
if len(bytes) != 1 { |
|
err = SyntaxError{"invalid boolean", fieldName} |
|
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", fieldName} |
|
} |
|
|
|
return |
|
} |
|
|
|
// INTEGER |
|
|
|
// checkInteger returns nil if the given bytes are a valid DER-encoded |
|
// INTEGER and an error otherwise. |
|
func checkInteger(bytes []byte, fieldName string) error { |
|
if len(bytes) == 0 { |
|
return StructuralError{"empty integer", fieldName} |
|
} |
|
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", fieldName} |
|
} |
|
return nil |
|
} |
|
|
|
// parseInt64 treats the given bytes as a big-endian, signed integer and |
|
// returns the result. |
|
func parseInt64(bytes []byte, fieldName string) (ret int64, err error) { |
|
err = checkInteger(bytes, fieldName) |
|
if err != nil { |
|
return |
|
} |
|
if len(bytes) > 8 { |
|
// We'll overflow an int64 in this case. |
|
err = StructuralError{"integer too large", fieldName} |
|
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, fieldName string) (int32, error) { |
|
if err := checkInteger(bytes, fieldName); err != nil { |
|
return 0, err |
|
} |
|
ret64, err := parseInt64(bytes, fieldName) |
|
if err != nil { |
|
return 0, err |
|
} |
|
if ret64 != int64(int32(ret64)) { |
|
return 0, StructuralError{"integer too large", fieldName} |
|
} |
|
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, fieldName string) (*big.Int, error) { |
|
if err := checkInteger(bytes, fieldName); 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, fieldName string) (ret BitString, err error) { |
|
if len(bytes) == 0 { |
|
err = SyntaxError{"zero length BIT STRING", fieldName} |
|
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", fieldName} |
|
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, fieldName string) (s []int, err error) { |
|
if len(bytes) == 0 { |
|
err = SyntaxError{"zero length OBJECT IDENTIFIER", fieldName} |
|
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, fieldName) |
|
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, fieldName) |
|
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, fieldName string) (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", fieldName} |
|
return |
|
} |
|
ret64 <<= 7 |
|
b := bytes[offset] |
|
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", fieldName} |
|
} |
|
return |
|
} |
|
} |
|
err = SyntaxError{"truncated base 128 integer", fieldName} |
|
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, fieldName string) (ret string, err error) { |
|
for _, b := range bytes { |
|
if !isNumeric(b) { |
|
return "", SyntaxError{"NumericString contains invalid character", fieldName} |
|
} |
|
} |
|
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, fieldName string) (ret string, err error) { |
|
for _, b := range bytes { |
|
if !isPrintable(b, allowAsterisk, allowAmpersand) { |
|
err = SyntaxError{"PrintableString contains invalid character", fieldName} |
|
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, fieldName string) (ret string, err error) { |
|
for _, b := range bytes { |
|
if b >= utf8.RuneSelf { |
|
err = SyntaxError{"IA5String contains invalid character", fieldName} |
|
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 |
|
} |
|
|
|
// 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, fieldName string) (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, fieldName) |
|
if err != nil { |
|
return |
|
} |
|
// Tags should be encoded in minimal form. |
|
if ret.tag < 0x1f { |
|
err = SyntaxError{"non-minimal tag", fieldName} |
|
return |
|
} |
|
} |
|
if offset >= len(bytes) { |
|
err = SyntaxError{"truncated tag or length", fieldName} |
|
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)", fieldName} |
|
return |
|
} |
|
ret.length = 0 |
|
for i := 0; i < numBytes; i++ { |
|
if offset >= len(bytes) { |
|
err = SyntaxError{"truncated tag or length", fieldName} |
|
return |
|
} |
|
b = bytes[offset] |
|
offset++ |
|
if ret.length >= 1<<23 { |
|
// We can't shift ret.length up without |
|
// overflowing. |
|
err = StructuralError{"length too large", fieldName} |
|
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", fieldName} |
|
return |
|
} |
|
} |
|
// Short lengths must be encoded in short form. |
|
if ret.length < 0x80 { |
|
err = StructuralError{"non-minimal length", fieldName} |
|
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, fieldName string) (ret reflect.Value, err error) { |
|
matchAny, expectedTag, compoundType, ok := getUniversalType(elemType) |
|
if !ok { |
|
err = StructuralError{"unknown Go type for slice", fieldName} |
|
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, fieldName) |
|
if err != nil { |
|
return |
|
} |
|
switch t.tag { |
|
case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString: |
|
// 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{fmt.Sprintf("sequence tag mismatch (got:%+v, want:0/%d/%t)", t, expectedTag, compoundType), fieldName} |
|
return |
|
} |
|
if invalidLength(offset, t.length, len(bytes)) { |
|
err = SyntaxError{"truncated sequence", fieldName} |
|
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 returns true iff offset + length > sliceLength, or if the |
|
// addition would overflow. |
|
func invalidLength(offset, length, sliceLength int) bool { |
|
return offset+length < offset || offset+length > sliceLength |
|
} |
|
|
|
// Tests whether the data in |bytes| would be a valid ISO8859-1 string. |
|
// Clearly, a sequence of bytes comprised solely of valid ISO8859-1 |
|
// codepoints does not imply that the encoding MUST be ISO8859-1, rather that |
|
// you would not encounter an error trying to interpret the data as such. |
|
func couldBeISO8859_1(bytes []byte) bool { |
|
for _, b := range bytes { |
|
if b < 0x20 || (b >= 0x7F && b < 0xA0) { |
|
return false |
|
} |
|
} |
|
return true |
|
} |
|
|
|
// Checks whether the data in |bytes| would be a valid T.61 string. |
|
// Clearly, a sequence of bytes comprised solely of valid T.61 |
|
// codepoints does not imply that the encoding MUST be T.61, rather that |
|
// you would not encounter an error trying to interpret the data as such. |
|
func couldBeT61(bytes []byte) bool { |
|
for _, b := range bytes { |
|
switch b { |
|
case 0x00: |
|
// Since we're guessing at (incorrect) encodings for a |
|
// PrintableString, we'll err on the side of caution and disallow |
|
// strings with a NUL in them, don't want to re-create a PayPal NUL |
|
// situation in monitors. |
|
fallthrough |
|
case 0x23, 0x24, 0x5C, 0x5E, 0x60, 0x7B, 0x7D, 0x7E, 0xA5, 0xA6, 0xAC, 0xAD, 0xAE, 0xAF, |
|
0xB9, 0xBA, 0xC0, 0xC9, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, |
|
0xDA, 0xDB, 0xDC, 0xDE, 0xDF, 0xE5, 0xFF: |
|
// These are all invalid code points in T.61, so it can't be a T.61 string. |
|
return false |
|
} |
|
} |
|
return true |
|
} |
|
|
|
// Converts the data in |bytes| to the equivalent UTF-8 string. |
|
func iso8859_1ToUTF8(bytes []byte) string { |
|
buf := make([]rune, len(bytes)) |
|
for i, b := range bytes { |
|
buf[i] = rune(b) |
|
} |
|
return string(buf) |
|
} |
|
|
|
// 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", params.name} |
|
} |
|
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, params.name) |
|
if err != nil { |
|
return |
|
} |
|
if invalidLength(offset, t.length, len(bytes)) { |
|
err = SyntaxError{"data truncated", params.name} |
|
return |
|
} |
|
var result interface{} |
|
if !t.isCompound && t.class == ClassUniversal { |
|
innerBytes := bytes[offset : offset+t.length] |
|
switch t.tag { |
|
case TagPrintableString: |
|
result, err = parsePrintableString(innerBytes, params.name) |
|
if err != nil && strings.Contains(err.Error(), "PrintableString contains invalid character") { |
|
// Probably an ISO8859-1 string stuffed in, check if it |
|
// would be valid and assume that's what's happened if so, |
|
// otherwise try T.61, failing that give up and just assign |
|
// the bytes |
|
switch { |
|
case couldBeISO8859_1(innerBytes): |
|
result, err = iso8859_1ToUTF8(innerBytes), nil |
|
case couldBeT61(innerBytes): |
|
result, err = parseT61String(innerBytes) |
|
default: |
|
result = nil |
|
err = errors.New("PrintableString contains invalid character, but couldn't determine correct String type.") |
|
} |
|
} |
|
case TagNumericString: |
|
result, err = parseNumericString(innerBytes, params.name) |
|
case TagIA5String: |
|
result, err = parseIA5String(innerBytes, params.name) |
|
case TagT61String: |
|
result, err = parseT61String(innerBytes) |
|
case TagUTF8String: |
|
result, err = parseUTF8String(innerBytes) |
|
case TagInteger: |
|
result, err = parseInt64(innerBytes, params.name) |
|
case TagBitString: |
|
result, err = parseBitString(innerBytes, params.name) |
|
case TagOID: |
|
result, err = parseObjectIdentifier(innerBytes, params.name) |
|
case TagUTCTime: |
|
result, err = parseUTCTime(innerBytes) |
|
case TagGeneralizedTime: |
|
result, err = parseGeneralizedTime(innerBytes) |
|
case TagOctetString: |
|
result = 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, params.name) |
|
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", params.name} |
|
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, params.name) |
|
if err != nil { |
|
return |
|
} |
|
} else { |
|
if fieldType != flagType { |
|
err = StructuralError{"zero length explicit tag was not an asn1.Flag", params.name} |
|
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", params.name} |
|
} |
|
return |
|
} |
|
} |
|
|
|
matchAny, universalTag, compoundType, ok1 := getUniversalType(fieldType) |
|
if !ok1 { |
|
err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType), params.name} |
|
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: |
|
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 |
|
} |
|
|
|
// 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), params.name} |
|
} |
|
return |
|
} |
|
if invalidLength(offset, t.length, len(bytes)) { |
|
err = SyntaxError{"data truncated", params.name} |
|
return |
|
} |
|
innerBytes := bytes[offset : offset+t.length] |
|
offset += t.length |
|
|
|
// We deal with the structures defined in this package first. |
|
switch fieldType { |
|
case rawValueType: |
|
result := RawValue{t.class, t.tag, t.isCompound, innerBytes, bytes[initOffset:offset]} |
|
v.Set(reflect.ValueOf(result)) |
|
return |
|
case objectIdentifierType: |
|
newSlice, err1 := parseObjectIdentifier(innerBytes, params.name) |
|
v.Set(reflect.MakeSlice(v.Type(), len(newSlice), len(newSlice))) |
|
if err1 == nil { |
|
reflect.Copy(v, reflect.ValueOf(newSlice)) |
|
} |
|
err = err1 |
|
return |
|
case bitStringType: |
|
bs, err1 := parseBitString(innerBytes, params.name) |
|
if err1 == nil { |
|
v.Set(reflect.ValueOf(bs)) |
|
} |
|
err = err1 |
|
return |
|
case timeType: |
|
var time time.Time |
|
var err1 error |
|
if universalTag == TagUTCTime { |
|
time, err1 = parseUTCTime(innerBytes) |
|
} else { |
|
time, err1 = parseGeneralizedTime(innerBytes) |
|
} |
|
if err1 == nil { |
|
v.Set(reflect.ValueOf(time)) |
|
} |
|
err = err1 |
|
return |
|
case enumeratedType: |
|
parsedInt, err1 := parseInt32(innerBytes, params.name) |
|
if err1 == nil { |
|
v.SetInt(int64(parsedInt)) |
|
} |
|
err = err1 |
|
return |
|
case flagType: |
|
v.SetBool(true) |
|
return |
|
case bigIntType: |
|
parsedInt, err1 := parseBigInt(innerBytes, params.name) |
|
if err1 == nil { |
|
v.Set(reflect.ValueOf(parsedInt)) |
|
} |
|
err = err1 |
|
return |
|
} |
|
switch val := v; val.Kind() { |
|
case reflect.Bool: |
|
parsedBool, err1 := parseBool(innerBytes, params.name) |
|
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, params.name) |
|
if err1 == nil { |
|
val.SetInt(int64(parsedInt)) |
|
} |
|
err = err1 |
|
} else { |
|
parsedInt, err1 := parseInt64(innerBytes, params.name) |
|
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).PkgPath != "" { |
|
err = StructuralError{"struct contains unexported fields", structType.Field(i).Name} |
|
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 |
|
} |
|
innerParams := parseFieldParameters(field.Tag.Get("asn1")) |
|
innerParams.name = field.Name |
|
innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, innerParams) |
|
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(), params.name) |
|
if err1 == nil { |
|
val.Set(newSlice) |
|
} |
|
err = err1 |
|
return |
|
case reflect.String: |
|
var v string |
|
switch universalTag { |
|
case TagPrintableString: |
|
v, err = parsePrintableString(innerBytes, params.name) |
|
case TagNumericString: |
|
v, err = parseNumericString(innerBytes, params.name) |
|
case TagIA5String: |
|
v, err = parseIA5String(innerBytes, params.name) |
|
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) |
|
default: |
|
err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag), params.name} |
|
} |
|
if err == nil { |
|
val.SetString(v) |
|
} |
|
return |
|
} |
|
err = StructuralError{"unsupported: " + v.Type().String(), params.name} |
|
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. |
|
// |
|
// 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 |
|
// 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 |
|
// |
|
// 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 type name of a slice element ends with "SET" then it's treated as if |
|
// the "set" tag was set on it. 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 interface{}) (rest []byte, err error) { |
|
return UnmarshalWithParams(b, val, "") |
|
} |
|
|
|
// 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 interface{}, params string) (rest []byte, err error) { |
|
v := reflect.ValueOf(val).Elem() |
|
offset, err := parseField(v, b, 0, parseFieldParameters(params)) |
|
if err != nil { |
|
return nil, err |
|
} |
|
return b[offset:], nil |
|
}
|
|
|