The Go Programming Language

Source file src/pkg/asn1/common.go

     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	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.Atoi64(part[8:])
   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	}

release.r60.3. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.