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