| // 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 preceeding 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 |
| |
| const ( |
| tagBoolean = 1 |
| tagInteger = 2 |
| tagBitString = 3 |
| tagOctetString = 4 |
| tagOID = 6 |
| tagSequence = 16 |
| tagSet = 17 |
| tagPrintableString = 19 |
| tagIA5String = 22 |
| tagUTCTime = 23 |
| ) |
| |
| 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 and EXPLICIT 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. |
| |
| // 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, ",", 0) { |
| switch { |
| case part == "optional": |
| ret.optional = true |
| case part == "explicit": |
| ret.explicit = true |
| if ret.tag == nil { |
| ret.tag = new(int) |
| *ret.tag = 0 |
| } |
| case part == "ia5": |
| ret.stringType = tagIA5String |
| case part == "printable": |
| ret.stringType = tagPrintableString |
| case strings.HasPrefix(part, "default:"): |
| i, err := strconv.Atoi64(part[8:]) |
| 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 |
| } |
| } |
| } |
| 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 |
| } |
| switch i := t.(type) { |
| case *reflect.BoolType: |
| return tagBoolean, false, true |
| case *reflect.IntType: |
| return tagInteger, false, true |
| case *reflect.Int64Type: |
| return tagInteger, false, true |
| case *reflect.StructType: |
| return tagSequence, true, true |
| case *reflect.SliceType: |
| if _, ok := t.(*reflect.SliceType).Elem().(*reflect.Uint8Type); ok { |
| return tagOctetString, false, true |
| } |
| return tagSequence, true, true |
| case *reflect.StringType: |
| return tagPrintableString, false, true |
| } |
| return 0, false, false |
| } |