asn1

package module
v0.0.0-...-741ff08 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 23, 2025 License: BSD-3-Clause Imports: 6 Imported by: 0

README

ASN.1 Data Structures in Go

Go Reference Test Status

This package provides base types for implementing ASN.1 data structures as defined to Rec. ITU-T X.680 in Go. The goal of this package is not to provide a comprehensive implementation of all ASN.1 features but to provide a framework for modeling, encoding, and decoding ASN.1 data structures in Go. Encoding rules for data structures are implemented in the sub-packages of this module.

Modeling of ASN.1 Data Structures

The codello.dev/asn1 package defines rules for modeling ASN.1 data structures in Go. The details are described in the package documentation. The modeling approach takes inspiration from the encoding packages in the Go standard library by using standard Go types such as structs as base building blocks.

Consider the following data structure:

DEFINITIONS
IMPLICIT TAGS
BEGIN

MyType ::= SEQUENCE {
    Num                  INTEGER
    Str                  UTF8String   OPTIONAL
    Data [APPLICATION 5] OCTET STRING
    ...
}
END

This could be translated into the following Go type:

package main

import "codello.dev/asn1"

type MyType struct {
	Num  int
	Str  string `asn1:"optional"`
	Data []byte `asn1:"application,tag:5"`
	asn1.Extensible
}

Most of the standard Go types such as string, int, float64, or time.Time have defined counterparts in the package. Custom types can also be used, although you probably need to implement support for the encoding rules you need to support.

Encoding and Decoding of ASN.1 Data Structures

Encoding rules for ASN.1 data structures are implemented in the corresponding sub-packages. Currently, the following encoding rules are supported:

Encoding and decoding generally uses the reflect package and works similar to encoding packages in the standard library. Currently only the Basic Encoding Rules are implemented. You can encode or decode an ASN.1 type like this:

package main

import (
	"io"

	"codello.dev/asn1/ber"
)

var val *MyType // see example above

func main() {
	// decode from a byte slice
	var data []byte
	err := ber.Unmarshal(data, &val)

	// decode from an io.Reader
	var r io.Reader
	err = ber.NewDecoder(r).Decode(&val)

	// encode to a byte slice
	data, err = ber.Marshal(val)

	// encode into an io.Writer
	var w io.Writer
	err = ber.NewEncoder(w).Encode(val)
}

If you have a need to implement a custom encoding scheme for a type, you can implement the interfaces ber.BerEncoder, ber.BerDecoder, and ber.BerMatcher. For example if you wanted a custom struct type MyString to encode as if it were a string, you could do this as follows:

Example Implementation
package main

import (
	"io"
	"strings"

	"codello.dev/asn1"
	"codello.dev/asn1/ber"
)

type MyString struct {
	data string
	// other fields
}

// BerEncode defines how s is encoded using BER. It returns identification
// information of the element (its ber.Header) as well as an io.WriterTo.
// The io.WriterTo value will do the actual encoding of the value into bytes.
func (s *MyString) BerEncode() (ber.Header, io.WriterTo, error) {
	return ber.Header{
		Tag:    asn1.Tag{Class: asn1.ClassApplication, Number: 15},
		Length: len(s.data),
	}, strings.NewReader(s.data), nil
}

// BerMatch is used to implement ASN.1 OPTIONAL elements. It is called before
// BerDecode to find out, if an element with the specified tag could be decoded
// by s. If BerMatch is not implemented, a value matches any tag.
func (s *MyString) BerMatch(tag asn1.Tag) bool {
	return tag == asn1.Tag{Class: asn1.ClassApplication, Number: 15}
}

// BerDecode decodes a data stream from r into s. The ber.ElementReader type
// provides various methods to simplify reading primitive or constructed
// types. For constructed types a common strategy is to wrap it in a
// ber.Decoder to do recursive decoding.
func (s *MyString) BerDecode(_ asn1.Tag, r ber.ElementReader) error {
	// you
	buf := strings.Builder{}
	_, err := io.Copy(&buf, r)
	s.data = buf.String()
	return err
}

Contributing

If you encounter a bug or are missing a feature, please do open an issue.

Pull requests are also very welcome, for example to add support for additional encoding rules or ASN.1 features.

Documentation

Overview

Package asn1 implements types for ASN.1 encoded data-structures as defined in Rec. ITU-T X.680. This package only defines Go types for some types defined by ASN.1. Encoding and decoding of data structures using different encoding rules is implemented in subpackages of this package.

Mapping of ASN.1 Types to Go Types

Many ASN.1 types have corresponding types with the same name defined in this package. See the package documentation for those types for specifics about their limitations. Additionally, the following Go types translate into their ASN.1 counterparts:

  • A Go bool corresponds to the ASN.1 BOOLEAN type.
  • All Go integer types and math/big.Int correspond to the ASN.1 INTEGER type. The supported size is limited by the Go type.
  • The types float32 and float64 and math/big.Float correspond to the ASN.1 REAL type. The supported size is limited by the Go type.
  • Go types with an underlying integer type correspond to the ASN.1 ENUMERATED type.
  • The Go string type corresponds to ASN.1 UTF8String type. A string can be decoded from any ASN.1 string type defined in this package.
  • A byte slice or byte array corresponds to an ASN.1 OCTET STRING. - Types that implement encoding.BinaryMarshaler or encoding.BinaryUnmarshaler correspond to an ASN.1 OCTET string.
  • The type time.Time corresponds to the ASN.1 TIME type. A time.Time value can be decoded from any ASN.1 time type defined in this package.
  • Go slices and arrays correspond to the ASN.1 SEQUENCE type. Their define the contents of the SEQUENCE.
  • Go structs correspond to the ASN.1 SEQUENCE type. The struct fields define the contents of the sequence, in order of definition. See the next section for details.

You can define your own types and use them alongside the types defined in this package. In order to add encoding/decoding support for a specific set of encoding rules, consult the corresponding package documentation for the encoding rules.

Defining ASN.1 Data Structures

ASN.1 data can be defined via Go structs. Take the following example:

DEFINITIONS
IMPLICIT TAGS
BEGIN

MyType ::= SEQUENCE {
	Num                  INTEGER
	Str                  UTF8String   OPTIONAL
	Data [APPLICATION 5] OCTET STRING
}
END

This could be translated into the following Go type:

type MyType struct {
	Num  int
	Str  string `asn1:"optional"`
	Data []byte `asn1:"application,tag:5"`
}

The Go type MyType defines the contents of the ASN.1 SEQUENCE. The order in which the struct fields are defined, corresponds to the order of elements within the SEQUENCE. Struct members must use exported (upper case) names. Unexported members are ignored. Fields of anonymous struct members are treated as if they were fields of the surrounding struct. Exported members can be explicitly ignored by using a `asn1:"-"` struct tag. Additional configuration is possible via struct tags. The following struct tags are supported:

tag:x       specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC
application specifies that an APPLICATION tag is used
private     specifies that a PRIVATE tag is used
explicit    mark the element as explicit
optional    marks the field as ASN.1 OPTIONAL
omitzero    omit this field if it is a zero value
nullable    allows ASN.1 NULL for this element

Using the struct tag `asn1:"tag:x"` (where x is a non-negative integer) overrides the intrinsic type of the member type. This corresponds to IMPLICIT TAGS in the ASN.1 syntax. By default, the tag number x is assumed to be CONTEXT SPECIFIC. To indicate a different class, use the "application" or "private" tag. The "universal" tag is supported for completeness but its use should be avoided as it can easily lead to invalid encodings.

ASN.1 allows an element to be marked as EXPLICIT. The effect of the `asn1:"explicit"` tag depends on the encoding rules used. When using "explicit" you must also use "tag:x". Nested EXPLICIT tags cannot be indicated via struct tags.

ASN.1 OPTIONAL elements can be marked with an `asn1:"optional"` tag. If an optional value is absent during decoding, no error is generated and the field is left unmodified. Optionality during encoding is controlled via the `asn1:"omitzero"` tag. If "omitzero" is present and the value for a field is the zero value, the field will be omitted during encoding. If a type implements IsZero() bool, that method is consulted, otherwise the zero value for its type will be used. Usually this should be paired with "optional" to ensure consistent encodes and decodes for a type.

The `asn1:"nullable"` struct tag indicates that the type may contain an ASN.1 NULL instead of an actual value for the type. If NULL is encountered for a "nullable" field, the field is set to its zero value. During encoding NULL is written if the field contains the zero value for its type. Usually "nullable" is used with pointer types.

Structs can make use of the Extensible type to be marked as extensible. This corresponds to the ASN.1 extension marker. See the documentation on Extensible for details. Currently, there is no counterpart for ASN.1 EXTENSIBILITY IMPLIED.

Index

Examples

Constants

View Source
const (
	TagBoolean          uint = 1
	TagInteger          uint = 2
	TagBitString        uint = 3
	TagOctetString      uint = 4
	TagNull             uint = 5
	TagOID              uint = 6
	TagObjectDescriptor uint = 7
	TagExternal         uint = 8
	TagReal             uint = 9
	TagEnumerated       uint = 10
	TagEmbeddedPDV      uint = 11
	TagUTF8String       uint = 12
	TagRelativeOID      uint = 13
	TagTime             uint = 14
	TagSequence         uint = 16
	TagSet              uint = 17
	TagNumericString    uint = 18
	TagPrintableString  uint = 19
	TagTeletexString    uint = 20
	TagT61String             = TagTeletexString
	TagVideotexString   uint = 21
	TagIA5String        uint = 22
	TagUTCTime          uint = 23
	TagGeneralizedTime  uint = 24
	TagGraphicString    uint = 25
	TagVisibleString    uint = 26
	TagISO646String          = TagVisibleString
	TagGeneralString    uint = 27
	TagUniversalString  uint = 28
	TagCharacterString  uint = 29
	TagBMPString        uint = 30
	TagDate             uint = 31
	TagTimeOfDay        uint = 32
	TagDateTime         uint = 33
	TagDuration         uint = 34
)

These are some ASN.1 tag numbers are defined in the ClassUniversal namespace. These assignments are defined in Rec. ITU-T X.680, Section 8, Table 1.

View Source
const TagReserved = 0

TagReserved is a reserved tag number in the ClassUniversal namespace to be used by encoding rules. This assignment is defined in Rec. ITU-T X.680, Section 8, Table 1.

Variables

This section is empty.

Functions

This section is empty.

Types

type BMPString

type BMPString string

BMPString represents the corresponding ASN.1 type. A BMPString can hold any character of the Unicode Basic Multilingual Plane. Note that this type uses standard Go strings which are UTF-8 encoded. The encoding of a BMPString in BER for example uses big endian UTF-16.

In most cases UTF8String is a more appropriate type.

See also section 41 of Rec. ITU-T X.680.

func (BMPString) IsValid

func (s BMPString) IsValid() bool

IsValid reports whether s contains valid UTF-8.

type BitString

type BitString struct {
	Bytes     []byte // bits packed into bytes.
	BitLength int    // length in bits.
}

BitString implements the ASN.1 BIT STRING type. A bit string is padded up to the nearest byte in memory and the number of valid bits is recorded. Padding bits will be encoded and decoded as zero bits.

See also section 22 of Rec. ITU-T X.680.

func (BitString) At

func (s BitString) At(i int) int

At returns the bit at the given index. If the index is out of range At panics.

func (BitString) IsValid

func (s BitString) IsValid() bool

IsValid reports whether there are enough bytes in s for the indicated BitLength.

func (BitString) Len

func (s BitString) Len() int

Len returns the number of bits in s.

func (BitString) RightAlign

func (s BitString) RightAlign() []byte

RightAlign returns a slice where the padding bits are at the beginning. The slice may share memory with the BitString.

func (BitString) String

func (s BitString) String() string

String formats s into a readable binary representation. Bits will be grouped into bytes. The last group may have fewer than 8 characters.

type Class

type Class uint8

Class holds the class part of an ASN.1 tag. The class acts as a namespace for the tag number. A Class value is an unsigned 2-bit integer. Class values whose value exceeds 2 bits are invalid.

const (
	ClassUniversal Class = iota
	ClassApplication
	ClassContextSpecific
	ClassPrivate
)

Predefined Class constants. These are all the possible values that can be encoded in the Class type.

func (Class) IsValid

func (c Class) IsValid() bool

IsValid reports whether c is a valid Class value.

func (Class) String

func (i Class) String() string

type Date

type Date time.Time

Date represents the ASN.1 DATE type. The value must not contain time or location information.

See also section 38 of Rec. ITU-T X.680.

func (Date) IsValid

func (t Date) IsValid() bool

IsValid reports whether t only contains date information.

func (Date) String

func (d Date) String() string

type DateTime

type DateTime time.Time

DateTime represents the ASN.1 DATE-TIME type. Values cannot contain location information.

See also section 38 of Rec. ITU-T X.680.

func (DateTime) IsValid

func (t DateTime) IsValid() bool

IsValid reports whether t contains only date and time information.

func (DateTime) String

func (t DateTime) String() string

String returns the ASN.1 notation of d.

type Duration

type Duration time.Duration

Duration represents the ASN.1 DURATION type. Only durations that can be represented as a time.Duration are valid, that is durations cannot use units above hours.

See also section 38 of Rec. ITU-T X.680.

func (Duration) String

func (d Duration) String() string

String returns the ASN.1 notation of d.

type Enumerated

type Enumerated int

Enumerated exists as a type mainly for documentation purposes. Any type with an underlying integer type is recognized as the ENUMERATED type. Types may implement an IsValid() bool method to indicate whether a value is valid for the enum.

See also section 20 of Rec. ITU-T X.680.

Example
type Option int
type MyType struct {
	I int    // ASN.1 INTEGER
	J Option // ASN.1 ENUMERATED
}
Output:

type Extensible

type Extensible struct{}

Extensible marks a struct as extensible. It corresponds to the ASN.1 extension marker. The Extensible type is intended to be embedded in a struct as an anonymous field. An extensible struct can be decoded from a representation that contains additional fields. For details see section 52 of Rec. ITU-T X.680. If a struct embeds the Extensible type, it must be the last non-ignored ASN.1 field, i.e. the following members must be either unexported or use the `asn1:"-"` struct tag.

Example
type MyType struct {
	Str string
	Extensible

	private int    // ok, unexported field
	ignored string `asn1:"-"` // ok, ignored
	// Public int // not ok, cannot appear after Extensible
}
Output:

type GeneralizedTime

type GeneralizedTime time.Time

GeneralizedTime represents the corresponding ASN.1 type. This type can represent dates between years 1 and 9999.

See also section 46 of Rec. ITU-T X.680.

func (GeneralizedTime) IsValid

func (t GeneralizedTime) IsValid() bool

IsValid reports if the year of t is between 1 and 9999.

func (GeneralizedTime) String

func (t GeneralizedTime) String() string

String returns a string representation of t that matches its representation in ASN.1 notation.

type IA5String

type IA5String string

IA5String represents the ASN.1 type IA5String. An IA5String must consist on ASCII characters only. Note that it is possible to create IA5String values in Go that violate this constraint. Use the IsValid method to check whether a string's contents are ASCII only.

See also section 41 of Rec. ITU-T X.680.

func (IA5String) IsValid

func (s IA5String) IsValid() bool

IsValid reports whether the contents of s consist only of ASCII characters.

type Null

type Null struct{}

Null represents the ASN.1 NULL type. If your data structure contains fixed NULL elements this type offers a convenient way to indicate their presence. If your data structure contains fields that may or may not be null, it is probably better to use a nullable type such as a pointer.

See also section 24 of Rec. ITU-T X.680.

type NumericString

type NumericString string

NumericString corresponds to the ASN.1 NumericString type. A NumericString can only consist of the digits 0-9 and space. Note that it is possible to create NumericString values in Go that violate this constraint. Use the IsValid method to check whether a string's contents are numeric.

See also section 41 of Rec. ITU-T X.680.

func (NumericString) IsValid

func (s NumericString) IsValid() bool

IsValid reports whether s consists only of allowed numeric characters.

type ObjectIdentifier

type ObjectIdentifier []uint

An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER. The semantics of an object identifier are specified in Rec. ITU-T X.660.

See also section 32 of Rec. ITU-T X.680.

func (ObjectIdentifier) Equal

func (oid ObjectIdentifier) Equal(other ObjectIdentifier) bool

Equal reports whether oid and other represent the same identifier.

func (ObjectIdentifier) String

func (oid ObjectIdentifier) String() string

String returns the dot-separated notation of oid.

type PrintableString

type PrintableString string

PrintableString represents the ASN.1 type PrintableString. A printable string can only contain the following ASCII characters:

A-Z	// upper case letters
a-z	// lower case letters
0-9	// digits
 	// space
'	// apostrophe
()	// Parenthesis
+-/	// plus, hyphen, solidus
.,:	// fill stop, comma, colon
=	// equals sign
?	// question mark

See also section 41 of Rec. ITU-T X.680.

func (PrintableString) IsValid

func (s PrintableString) IsValid() bool

IsValid reports whether s consists only of printable characters.

type RelativeOID

type RelativeOID []uint

RelativeOID represents the ASN.1 RELATIVE OID type. This is similar to the ObjectIdentifier type, but a RelativeOID is only a suffix of an OID.

See also section 32 of Rec. ITU-T X.680.

func (RelativeOID) Equal

func (oid RelativeOID) Equal(other RelativeOID) bool

Equal reports whether oid and other represent the same identifier.

func (RelativeOID) String

func (oid RelativeOID) String() string

String returns the dot-separated notation of oid.

type Set

type Set[T comparable] map[T]struct{}

Set represents the ASN.1 SET OF type. This is only a very basic implementation of a set in Go. If you have specific requirements working with sets you might be better off to define your own set type.

See also section 27 and 28 of Rec. ITU-T X.680.

func NewSet

func NewSet[T comparable](ts ...T) Set[T]

NewSet creates a new set with the specified elements.

func (Set[T]) Add

func (s Set[T]) Add(value T)

Add adds value to the set.

func (Set[T]) Contains

func (s Set[T]) Contains(value T) bool

Contains indicates whether value is contained within the set.

func (Set[T]) Remove

func (s Set[T]) Remove(value T)

Remove removes value from the set, if it was present.

type Tag

type Tag struct {
	Class  Class
	Number uint
}

Tag constitutes an ASN.1 tag, consisting of its class and number. For details, see Section 8 of Rec. ITU-T X.680.

func (Tag) String

func (t Tag) String() string

String returns a string representation t in a format similar to the one used in ASN.1 notation. The tag number is enclosed by square brackets and prefixed with the class used. To avoid ambiguity the UNIVERSAL word is used for universal tags, although this is not valid ASN.1 syntax.

Example
t1 := Tag{asn1.ClassApplication, 17}
t2 := Tag{asn1.ClassContextSpecific, 8}
t3 := Tag{ClassUniversal, 2}
fmt.Println(t1.String())
fmt.Println(t2.String())
fmt.Println(t3.String())
Output:

[APPLICATION 17]
[8]
[UNIVERSAL 2]

type Time

type Time time.Time

Time represents the ASN.1 TIME type. This type can only hold a subset of valid ASN.1 TIME values, namely those that can be represented by a time instant. In particular recurrences or intervals are not supported.

See also section 38 of Rec. ITU-T X.680.

func (Time) String

func (t Time) String() string

String returns an ISO 8601 compatible representation of t.

type TimeOfDay

type TimeOfDay time.Time

TimeOfDay represents the ASN.1 TIME-OF-DAY type. The value must not contain date or location information.

See also section 38 of Rec. ITU-T X.680.

func (TimeOfDay) IsValid

func (t TimeOfDay) IsValid() bool

IsValid reports whether t only contains time data.

func (TimeOfDay) String

func (t TimeOfDay) String() string

String returns the ASN.1 notation of t.

type UTCTime

type UTCTime time.Time

UTCTime represents the corresponding ASN.1 type. Only dates between 1950 and 2049 can be represented by this type.

See also section 47 of Rec. ITU-T X.680.

func (UTCTime) IsValid

func (t UTCTime) IsValid() bool

IsValid reports whether the year of t is between 1950 and 2049.

func (UTCTime) String

func (t UTCTime) String() string

String returns the time of t in the format YYMMDDhhmmssZ or YYMMDDhhmmss+hhmm.

type UTF8String

type UTF8String string

UTF8String represents the ASN.1 UTF8String type. It can only hold valid UTF-8 values. UTF8String is also the default type for standard Go strings.

See also section 41 of Rec. ITU-T X.680.

func (UTF8String) IsValid

func (s UTF8String) IsValid() bool

IsValid reports whether s is a valid UTF-8 string.

type UniversalString

type UniversalString string

UniversalString represents the corresponding ASN.1 type. A UniversalString can contain any Unicode character. Note that the Go type uses standard Go strings which are UTF-8 encoded. The encoding of a UniversalString in BER for example uses big endian UTF-32.

In most cases UTF8String is a more appropriate type.

See also section 41 of Rec. ITU-T X.680.

func (UniversalString) IsValid

func (s UniversalString) IsValid() bool

IsValid reports whether consists of a valid UTF-8 encoding. Note that this does not validate the encoding of a UniversalString but its Go representation.

type VisibleString

type VisibleString string

VisibleString represents the corresponding ASN.1 type. It is limited to visible ASCII characters. In particular this does not include ASCII control characters. Note that it is possible to create VisibleString values in Go that violate this constraint. Use the IsValid method to check whether a string's contents are visible ASCII only.

See also section 41 of Rec. ITU-T X.680.

func (VisibleString) IsValid

func (s VisibleString) IsValid() bool

IsValid reports whether s only consists of visible ASCII characters.

Directories

Path Synopsis
Package ber implements the ASN.1 Basic Encoding Rules (BER).
Package ber implements the ASN.1 Basic Encoding Rules (BER).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
JackTT - Gopher 🇻🇳