| // 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 |
| 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. |
| 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 == "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 == "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) (tagNumber int, isCompound, ok bool) { |
| switch t { |
| case objectIdentifierType: |
| return TagOID, false, true |
| case bitStringType: |
| return TagBitString, false, true |
| case timeType: |
| return TagUTCTime, false, true |
| case enumeratedType: |
| return TagEnum, false, true |
| case bigIntType: |
| return TagInteger, false, true |
| } |
| switch t.Kind() { |
| case reflect.Bool: |
| return TagBoolean, false, true |
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
| return TagInteger, false, true |
| case reflect.Struct: |
| return TagSequence, true, true |
| case reflect.Slice: |
| if t.Elem().Kind() == reflect.Uint8 { |
| return TagOctetString, false, true |
| } |
| if strings.HasSuffix(t.Name(), "SET") { |
| return TagSet, true, true |
| } |
| return TagSequence, true, true |
| case reflect.String: |
| return TagPrintableString, false, true |
| } |
| return 0, false, false |
| } |