| 1 |
747 |
jeremybenn |
// 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 |
|
|
|
| 5 |
|
|
package asn1
|
| 6 |
|
|
|
| 7 |
|
|
import (
|
| 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 |
|
|
const (
|
| 22 |
|
|
tagBoolean = 1
|
| 23 |
|
|
tagInteger = 2
|
| 24 |
|
|
tagBitString = 3
|
| 25 |
|
|
tagOctetString = 4
|
| 26 |
|
|
tagOID = 6
|
| 27 |
|
|
tagEnum = 10
|
| 28 |
|
|
tagUTF8String = 12
|
| 29 |
|
|
tagSequence = 16
|
| 30 |
|
|
tagSet = 17
|
| 31 |
|
|
tagPrintableString = 19
|
| 32 |
|
|
tagT61String = 20
|
| 33 |
|
|
tagIA5String = 22
|
| 34 |
|
|
tagUTCTime = 23
|
| 35 |
|
|
tagGeneralizedTime = 24
|
| 36 |
|
|
tagGeneralString = 27
|
| 37 |
|
|
)
|
| 38 |
|
|
|
| 39 |
|
|
const (
|
| 40 |
|
|
classUniversal = 0
|
| 41 |
|
|
classApplication = 1
|
| 42 |
|
|
classContextSpecific = 2
|
| 43 |
|
|
classPrivate = 3
|
| 44 |
|
|
)
|
| 45 |
|
|
|
| 46 |
|
|
type tagAndLength struct {
|
| 47 |
|
|
class, tag, length int
|
| 48 |
|
|
isCompound bool
|
| 49 |
|
|
}
|
| 50 |
|
|
|
| 51 |
|
|
// ASN.1 has IMPLICIT and EXPLICIT tags, which can be translated as "instead
|
| 52 |
|
|
// of" and "in addition to". When not specified, every primitive type has a
|
| 53 |
|
|
// default tag in the UNIVERSAL class.
|
| 54 |
|
|
//
|
| 55 |
|
|
// For example: a BIT STRING is tagged [UNIVERSAL 3] by default (although ASN.1
|
| 56 |
|
|
// doesn't actually have a UNIVERSAL keyword). However, by saying [IMPLICIT
|
| 57 |
|
|
// CONTEXT-SPECIFIC 42], that means that the tag is replaced by another.
|
| 58 |
|
|
//
|
| 59 |
|
|
// On the other hand, if it said [EXPLICIT CONTEXT-SPECIFIC 10], then an
|
| 60 |
|
|
// /additional/ tag would wrap the default tag. This explicit tag will have the
|
| 61 |
|
|
// compound flag set.
|
| 62 |
|
|
//
|
| 63 |
|
|
// (This is used in order to remove ambiguity with optional elements.)
|
| 64 |
|
|
//
|
| 65 |
|
|
// You can layer EXPLICIT and IMPLICIT tags to an arbitrary depth, however we
|
| 66 |
|
|
// don't support that here. We support a single layer of EXPLICIT or IMPLICIT
|
| 67 |
|
|
// tagging with tag strings on the fields of a structure.
|
| 68 |
|
|
|
| 69 |
|
|
// fieldParameters is the parsed representation of tag string from a structure field.
|
| 70 |
|
|
type fieldParameters struct {
|
| 71 |
|
|
optional bool // true iff the field is OPTIONAL
|
| 72 |
|
|
explicit bool // true iff an EXPLICIT tag is in use.
|
| 73 |
|
|
application bool // true iff an APPLICATION tag is in use.
|
| 74 |
|
|
defaultValue *int64 // a default value for INTEGER typed fields (maybe nil).
|
| 75 |
|
|
tag *int // the EXPLICIT or IMPLICIT tag (maybe nil).
|
| 76 |
|
|
stringType int // the string tag to use when marshaling.
|
| 77 |
|
|
set bool // true iff this should be encoded as a SET
|
| 78 |
|
|
|
| 79 |
|
|
// Invariants:
|
| 80 |
|
|
// if explicit is set, tag is non-nil.
|
| 81 |
|
|
}
|
| 82 |
|
|
|
| 83 |
|
|
// Given a tag string with the format specified in the package comment,
|
| 84 |
|
|
// parseFieldParameters will parse it into a fieldParameters structure,
|
| 85 |
|
|
// ignoring unknown parts of the string.
|
| 86 |
|
|
func parseFieldParameters(str string) (ret fieldParameters) {
|
| 87 |
|
|
for _, part := range strings.Split(str, ",") {
|
| 88 |
|
|
switch {
|
| 89 |
|
|
case part == "optional":
|
| 90 |
|
|
ret.optional = true
|
| 91 |
|
|
case part == "explicit":
|
| 92 |
|
|
ret.explicit = true
|
| 93 |
|
|
if ret.tag == nil {
|
| 94 |
|
|
ret.tag = new(int)
|
| 95 |
|
|
}
|
| 96 |
|
|
case part == "ia5":
|
| 97 |
|
|
ret.stringType = tagIA5String
|
| 98 |
|
|
case part == "printable":
|
| 99 |
|
|
ret.stringType = tagPrintableString
|
| 100 |
|
|
case strings.HasPrefix(part, "default:"):
|
| 101 |
|
|
i, err := strconv.ParseInt(part[8:], 10, 64)
|
| 102 |
|
|
if err == nil {
|
| 103 |
|
|
ret.defaultValue = new(int64)
|
| 104 |
|
|
*ret.defaultValue = i
|
| 105 |
|
|
}
|
| 106 |
|
|
case strings.HasPrefix(part, "tag:"):
|
| 107 |
|
|
i, err := strconv.Atoi(part[4:])
|
| 108 |
|
|
if err == nil {
|
| 109 |
|
|
ret.tag = new(int)
|
| 110 |
|
|
*ret.tag = i
|
| 111 |
|
|
}
|
| 112 |
|
|
case part == "set":
|
| 113 |
|
|
ret.set = true
|
| 114 |
|
|
case part == "application":
|
| 115 |
|
|
ret.application = true
|
| 116 |
|
|
if ret.tag == nil {
|
| 117 |
|
|
ret.tag = new(int)
|
| 118 |
|
|
}
|
| 119 |
|
|
}
|
| 120 |
|
|
}
|
| 121 |
|
|
return
|
| 122 |
|
|
}
|
| 123 |
|
|
|
| 124 |
|
|
// Given a reflected Go type, getUniversalType returns the default tag number
|
| 125 |
|
|
// and expected compound flag.
|
| 126 |
|
|
func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) {
|
| 127 |
|
|
switch t {
|
| 128 |
|
|
case objectIdentifierType:
|
| 129 |
|
|
return tagOID, false, true
|
| 130 |
|
|
case bitStringType:
|
| 131 |
|
|
return tagBitString, false, true
|
| 132 |
|
|
case timeType:
|
| 133 |
|
|
return tagUTCTime, false, true
|
| 134 |
|
|
case enumeratedType:
|
| 135 |
|
|
return tagEnum, false, true
|
| 136 |
|
|
case bigIntType:
|
| 137 |
|
|
return tagInteger, false, true
|
| 138 |
|
|
}
|
| 139 |
|
|
switch t.Kind() {
|
| 140 |
|
|
case reflect.Bool:
|
| 141 |
|
|
return tagBoolean, false, true
|
| 142 |
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
| 143 |
|
|
return tagInteger, false, true
|
| 144 |
|
|
case reflect.Struct:
|
| 145 |
|
|
return tagSequence, true, true
|
| 146 |
|
|
case reflect.Slice:
|
| 147 |
|
|
if t.Elem().Kind() == reflect.Uint8 {
|
| 148 |
|
|
return tagOctetString, false, true
|
| 149 |
|
|
}
|
| 150 |
|
|
if strings.HasSuffix(t.Name(), "SET") {
|
| 151 |
|
|
return tagSet, true, true
|
| 152 |
|
|
}
|
| 153 |
|
|
return tagSequence, true, true
|
| 154 |
|
|
case reflect.String:
|
| 155 |
|
|
return tagPrintableString, false, true
|
| 156 |
|
|
}
|
| 157 |
|
|
return 0, false, false
|
| 158 |
|
|
}
|