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