Home | History | Annotate | Download | only in asn1
      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 
      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 // ASN.1 tags represent the type of the following object.
     22 const (
     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.
     41 const (
     42 	ClassUniversal       = 0
     43 	ClassApplication     = 1
     44 	ClassContextSpecific = 2
     45 	ClassPrivate         = 3
     46 )
     47 
     48 type 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.
     72 type 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.
     90 func 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 		case part == "printable":
    107 			ret.stringType = TagPrintableString
    108 		case part == "utf8":
    109 			ret.stringType = TagUTF8String
    110 		case strings.HasPrefix(part, "default:"):
    111 			i, err := strconv.ParseInt(part[8:], 10, 64)
    112 			if err == nil {
    113 				ret.defaultValue = new(int64)
    114 				*ret.defaultValue = i
    115 			}
    116 		case strings.HasPrefix(part, "tag:"):
    117 			i, err := strconv.Atoi(part[4:])
    118 			if err == nil {
    119 				ret.tag = new(int)
    120 				*ret.tag = i
    121 			}
    122 		case part == "set":
    123 			ret.set = true
    124 		case part == "application":
    125 			ret.application = true
    126 			if ret.tag == nil {
    127 				ret.tag = new(int)
    128 			}
    129 		case part == "omitempty":
    130 			ret.omitEmpty = true
    131 		}
    132 	}
    133 	return
    134 }
    135 
    136 // Given a reflected Go type, getUniversalType returns the default tag number
    137 // and expected compound flag.
    138 func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) {
    139 	switch t {
    140 	case objectIdentifierType:
    141 		return TagOID, false, true
    142 	case bitStringType:
    143 		return TagBitString, false, true
    144 	case timeType:
    145 		return TagUTCTime, false, true
    146 	case enumeratedType:
    147 		return TagEnum, false, true
    148 	case bigIntType:
    149 		return TagInteger, false, true
    150 	}
    151 	switch t.Kind() {
    152 	case reflect.Bool:
    153 		return TagBoolean, false, true
    154 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    155 		return TagInteger, false, true
    156 	case reflect.Struct:
    157 		return TagSequence, true, true
    158 	case reflect.Slice:
    159 		if t.Elem().Kind() == reflect.Uint8 {
    160 			return TagOctetString, false, true
    161 		}
    162 		if strings.HasSuffix(t.Name(), "SET") {
    163 			return TagSet, true, true
    164 		}
    165 		return TagSequence, true, true
    166 	case reflect.String:
    167 		return TagPrintableString, false, true
    168 	}
    169 	return 0, false, false
    170 }
    171