1// Copyright 2009 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package asn1 6 7import ( 8 "reflect" 9 "strconv" 10 "strings" 11) 12 13// ASN.1 objects have metadata preceding them: 14// the tag: the type of the object 15// a flag denoting if this object is compound or not 16// the class type: the namespace of the tag 17// the length of the object, in bytes 18 19// Here are some standard tags and classes 20 21// ASN.1 tags represent the type of the following object. 22const ( 23 TagBoolean = 1 24 TagInteger = 2 25 TagBitString = 3 26 TagOctetString = 4 27 TagNull = 5 28 TagOID = 6 29 TagEnum = 10 30 TagUTF8String = 12 31 TagSequence = 16 32 TagSet = 17 33 TagNumericString = 18 34 TagPrintableString = 19 35 TagT61String = 20 36 TagIA5String = 22 37 TagUTCTime = 23 38 TagGeneralizedTime = 24 39 TagGeneralString = 27 40 TagBMPString = 30 41) 42 43// ASN.1 class types represent the namespace of the tag. 44const ( 45 ClassUniversal = 0 46 ClassApplication = 1 47 ClassContextSpecific = 2 48 ClassPrivate = 3 49) 50 51type tagAndLength struct { 52 class, tag, length int 53 isCompound bool 54} 55 56// ASN.1 has IMPLICIT and EXPLICIT tags, which can be translated as "instead 57// of" and "in addition to". When not specified, every primitive type has a 58// default tag in the UNIVERSAL class. 59// 60// For example: a BIT STRING is tagged [UNIVERSAL 3] by default (although ASN.1 61// doesn't actually have a UNIVERSAL keyword). However, by saying [IMPLICIT 62// CONTEXT-SPECIFIC 42], that means that the tag is replaced by another. 63// 64// On the other hand, if it said [EXPLICIT CONTEXT-SPECIFIC 10], then an 65// /additional/ tag would wrap the default tag. This explicit tag will have the 66// compound flag set. 67// 68// (This is used in order to remove ambiguity with optional elements.) 69// 70// You can layer EXPLICIT and IMPLICIT tags to an arbitrary depth, however we 71// don't support that here. We support a single layer of EXPLICIT or IMPLICIT 72// tagging with tag strings on the fields of a structure. 73 74// fieldParameters is the parsed representation of tag string from a structure field. 75type fieldParameters struct { 76 optional bool // true iff the field is OPTIONAL 77 explicit bool // true iff an EXPLICIT tag is in use. 78 application bool // true iff an APPLICATION tag is in use. 79 private bool // true iff a PRIVATE tag is in use. 80 defaultValue *int64 // a default value for INTEGER typed fields (maybe nil). 81 tag *int // the EXPLICIT or IMPLICIT tag (maybe nil). 82 stringType int // the string tag to use when marshaling. 83 timeType int // the time tag to use when marshaling. 84 set bool // true iff this should be encoded as a SET 85 omitEmpty bool // true iff this should be omitted if empty when marshaling. 86 87 // Invariants: 88 // if explicit is set, tag is non-nil. 89} 90 91// Given a tag string with the format specified in the package comment, 92// parseFieldParameters will parse it into a fieldParameters structure, 93// ignoring unknown parts of the string. 94func parseFieldParameters(str string) (ret fieldParameters) { 95 var part string 96 for len(str) > 0 { 97 part, str, _ = strings.Cut(str, ",") 98 switch { 99 case part == "optional": 100 ret.optional = true 101 case part == "explicit": 102 ret.explicit = true 103 if ret.tag == nil { 104 ret.tag = new(int) 105 } 106 case part == "generalized": 107 ret.timeType = TagGeneralizedTime 108 case part == "utc": 109 ret.timeType = TagUTCTime 110 case part == "ia5": 111 ret.stringType = TagIA5String 112 case part == "printable": 113 ret.stringType = TagPrintableString 114 case part == "numeric": 115 ret.stringType = TagNumericString 116 case part == "utf8": 117 ret.stringType = TagUTF8String 118 case strings.HasPrefix(part, "default:"): 119 i, err := strconv.ParseInt(part[8:], 10, 64) 120 if err == nil { 121 ret.defaultValue = new(int64) 122 *ret.defaultValue = i 123 } 124 case strings.HasPrefix(part, "tag:"): 125 i, err := strconv.Atoi(part[4:]) 126 if err == nil { 127 ret.tag = new(int) 128 *ret.tag = i 129 } 130 case part == "set": 131 ret.set = true 132 case part == "application": 133 ret.application = true 134 if ret.tag == nil { 135 ret.tag = new(int) 136 } 137 case part == "private": 138 ret.private = true 139 if ret.tag == nil { 140 ret.tag = new(int) 141 } 142 case part == "omitempty": 143 ret.omitEmpty = true 144 } 145 } 146 return 147} 148 149// Given a reflected Go type, getUniversalType returns the default tag number 150// and expected compound flag. 151func getUniversalType(t reflect.Type) (matchAny bool, tagNumber int, isCompound, ok bool) { 152 switch t { 153 case rawValueType: 154 return true, -1, false, true 155 case objectIdentifierType: 156 return false, TagOID, false, true 157 case bitStringType: 158 return false, TagBitString, false, true 159 case timeType: 160 return false, TagUTCTime, false, true 161 case enumeratedType: 162 return false, TagEnum, false, true 163 case bigIntType: 164 return false, TagInteger, false, true 165 } 166 switch t.Kind() { 167 case reflect.Bool: 168 return false, TagBoolean, false, true 169 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 170 return false, TagInteger, false, true 171 case reflect.Struct: 172 return false, TagSequence, true, true 173 case reflect.Slice: 174 if t.Elem().Kind() == reflect.Uint8 { 175 return false, TagOctetString, false, true 176 } 177 if strings.HasSuffix(t.Name(), "SET") { 178 return false, TagSet, true, true 179 } 180 return false, TagSequence, true, true 181 case reflect.String: 182 return false, TagPrintableString, false, true 183 } 184 return false, 0, false, false 185} 186