183 lines
5.4 KiB
Go
183 lines
5.4 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package asn1
|
|
|
|
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
|
|
)
|
|
|
|
// 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) {
|
|
for _, part := range strings.Split(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
|
|
}
|