Платформа ЦРНП "Мирокод" для разработки проектов
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.
689 lines
16 KiB
689 lines
16 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 |
|
|
|
import ( |
|
"errors" |
|
"fmt" |
|
"math/big" |
|
"reflect" |
|
"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 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, fieldName string) (encoder, error) { |
|
if n == nil { |
|
return nil, StructuralError{"empty integer", fieldName} |
|
} |
|
|
|
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, fieldName string) (e encoder, err error) { |
|
if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) { |
|
return nil, StructuralError{"invalid object identifier", fieldName} |
|
} |
|
|
|
return oidEncoder(oid), nil |
|
} |
|
|
|
func makePrintableString(s, fieldName 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", fieldName} |
|
} |
|
} |
|
|
|
return stringEncoder(s), nil |
|
} |
|
|
|
func makeIA5String(s, fieldName string) (e encoder, err error) { |
|
for i := 0; i < len(s); i++ { |
|
if s[i] > 127 { |
|
return nil, StructuralError{"IA5String contains invalid character", fieldName} |
|
} |
|
} |
|
|
|
return stringEncoder(s), nil |
|
} |
|
|
|
func makeNumericString(s string, fieldName string) (e encoder, err error) { |
|
for i := 0; i < len(s); i++ { |
|
if !isNumeric(s[i]) { |
|
return nil, StructuralError{"NumericString contains invalid character", fieldName} |
|
} |
|
} |
|
|
|
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, fieldName string) (e encoder, err error) { |
|
dst := make([]byte, 0, 18) |
|
|
|
dst, err = appendUTCTime(dst, t, fieldName) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
return bytesEncoder(dst), nil |
|
} |
|
|
|
func makeGeneralizedTime(t time.Time, fieldName string) (e encoder, err error) { |
|
dst := make([]byte, 0, 20) |
|
|
|
dst, err = appendGeneralizedTime(dst, t, fieldName) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
return bytesEncoder(dst), nil |
|
} |
|
|
|
func appendUTCTime(dst []byte, t time.Time, fieldName string) (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", fieldName} |
|
} |
|
|
|
return appendTimeCommon(dst, t), nil |
|
} |
|
|
|
func appendGeneralizedTime(dst []byte, t time.Time, fieldName string) (ret []byte, err error) { |
|
year := t.Year() |
|
if year < 0 || year > 9999 { |
|
return nil, StructuralError{"cannot represent time as GeneralizedTime", fieldName} |
|
} |
|
|
|
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, params.name) |
|
} |
|
return makeUTCTime(t, params.name) |
|
case bitStringType: |
|
return bitStringEncoder(value.Interface().(BitString)), nil |
|
case objectIdentifierType: |
|
return makeObjectIdentifier(value.Interface().(ObjectIdentifier), params.name) |
|
case bigIntType: |
|
return makeBigInt(value.Interface().(*big.Int), params.name) |
|
} |
|
|
|
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).PkgPath != "" { |
|
return nil, StructuralError{"struct contains unexported fields", t.Field(i).Name} |
|
} |
|
} |
|
|
|
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 |
|
} |
|
} |
|
|
|
return multiEncoder(m), nil |
|
} |
|
case reflect.String: |
|
switch params.stringType { |
|
case TagIA5String: |
|
return makeIA5String(v.String(), params.name) |
|
case TagPrintableString: |
|
return makePrintableString(v.String(), params.name) |
|
case TagNumericString: |
|
return makeNumericString(v.String(), params.name) |
|
default: |
|
return makeUTF8String(v.String()), nil |
|
} |
|
} |
|
|
|
return nil, StructuralError{"unknown Go type", params.name} |
|
} |
|
|
|
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()), params.name} |
|
} |
|
|
|
if params.timeType != 0 && tag != TagUTCTime { |
|
return nil, StructuralError{"explicit time type given to non-time member", params.name} |
|
} |
|
|
|
if params.stringType != 0 && tag != TagPrintableString { |
|
return nil, StructuralError{"explicit string type given to non-string member", params.name} |
|
} |
|
|
|
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", params.name} |
|
} |
|
tag = TagSet |
|
} |
|
|
|
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 { |
|
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 interface{}) ([]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 interface{}, 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 |
|
}
|
|
|