| // 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:len(part)]); |
| if err == nil { |
| ret.defaultValue = new(int64); |
| *ret.defaultValue = i; |
| } |
| case strings.HasPrefix(part, "tag:"): |
| i, err := strconv.Atoi(part[4:len(part)]); |
| 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; |
| } |