ber

package
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: 20 Imported by: 0

Documentation

Overview

Package ber implements the ASN.1 Basic Encoding Rules (BER). The Basic Encoding Rules are defined in Rec. ITU-T X.690. See also “A Layman's Guide to a Subset of ASN.1, BER, and DER”.

See the package documentation of the asn1 package for details how Go types translate to ASN.1 types. Types following that specification can be encoded into and decoded from a stream of binary data using the Basic Encoding Rules using this package. The following limitations apply:

  • When decoding an ASN.1 INTEGER type into a Go integer the size of the integer is limited by the size of the Go type. This limitation does not apply to *math/big.Int.
  • When decoding an ASN.1 REAL type into a Go float64 or float32, the size of the value is limited by the size of the Go type. When using *math/big.Float the size limitations of that type apply.
  • When decoding binary data into a pre-allocated byte slice the data will overwrite existing data in the slice.
  • When decoding binary data into a byte array, the number of bytes in the element must match the length of the array exactly.
  • When decoding a constructed element into an array the number of sequence elements must match the length of the array exactly.
  • Decoding into an interface{} will decode known types as their corresponding Go values. Unrecognized types will be stored as RawValue.

Index

Constants

View Source
const LengthIndefinite = -1

LengthIndefinite when used as a magic number for the length of a Header indicates that the element is encoded using the constructed indefinite-length format.

Variables

This section is empty.

Functions

func CombinedLength

func CombinedLength(ls ...int) int

CombinedLength returns the length of an element (not including its header) consisting of child elements of the specified lengths. If any of the passed lengths are LengthIndefinite, the result is LengthIndefinite as well.

func Marshal

func Marshal(val any) ([]byte, error)

Marshal returns the BER-encoding of val or an error if encoding fails.

func MarshalWithParams

func MarshalWithParams(val any, params string) ([]byte, error)

MarshalWithParams marshals the BER-encoding of val into a byte slice and returns it. The format of the params is described in the asn1 package. Using the `asn1:"-"` option has no effect here.

func Unmarshal

func Unmarshal(b []byte, val any) error

Unmarshal parses a BER-encoded ASN.1 data structure from b. See Decoder.Decode for details. If any data is left over in b after val has been decoded, an error is returned.

func UnmarshalWithParams

func UnmarshalWithParams(b []byte, val any, params string) error

UnmarshalWithParams allows field parameters to be specified for the top-level element. The form of the params is the same as the field tags. See Decoder.Decode for details.

Types

type BerDecoder

type BerDecoder interface {
	BerDecode(tag asn1.Tag, r ElementReader) error
}

BerDecoder is the interfaces implemented by types that can decode themselves from a binary representation in BER-encoding. In addition, types may want to implement the BerMatcher interface to provide support for optional elements.

The reader r reads the contents of the element, identified by tag. In particular r does not read the tag and length bytes of the element being decoded. At the end of the element r returns io.EOF, even if there are more bytes in the original data stream. If an implementation does not read r to completion, any remaining bytes are discarded. If r.Constructed() returns true, the remaining elements are syntactically validated.

Implementations must attempt to decode from r irrespective of the tag. However, implementations may alter their decoding behavior according to the tag used. Implementations SHOULD validate that r.Constructed() meets the requirements of the element. If decoding fails, an error must be returned that explains the failure. Usually such an error should be a SyntaxError or StructuralError. Implementations can also return io.ErrUnexpectedEOF to indicate that r returned io.EOF before the entire data was read.

The length of the element being decoded is available through r. Note that an element may use the indefinite length encoding in which case r might indicate a length of LengthIndefinite.

type BerEncoder

type BerEncoder interface {
	BerEncode() (h Header, wt io.WriterTo, err error)
}

BerEncoder is an interface that can be implemented by types that provide custom logic to encode themselves as an ASN.1 type using Basic Encoding Rules.

Encoding using BER is a two-step process: First the size of values is estimated and then elements are written to a byte stream. The BerEncode method realizes this by returning an io.WriterTo instead of writing data directly to a writer. Implementations write exactly the amount of bytes promised by h.Length. The writer passed to wt implements io.ByteWriter.

Implementations should return any validation errors from BerEncode. Errors returned from wt are assumed to be writing errors of the underlying writer.

If an element uses the indefinite-length format, the final two zero octets are written automatically and must not be written by wt. Custom constructed elements may want to use the Sequence type to facilitate their encoding. Note that struct tags override the class and tag of the returned header.

type BerMatcher

type BerMatcher interface {
	BerMatch(asn1.Tag) bool
}

BerMatcher can be implemented by types that implement BerDecoder to add support for optional elements. The BerMatch method is consulted if no tag number is given via struct tags. Implementations implement this interface by returning a boolean value indicating whether h.Class and h.Tag match the intrinsic tag of the element, i.e. if based on the h.Class and h.Tag it is expected that decoding will succeed.

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

Decoder implements stream-based decoding of BER-encoded ASN.1 types. The Decoder type implements specialized buffering for BER-data. See the NewDecoder function for details.

To create a Decoder, use the NewDecoder function.

func NewDecoder

func NewDecoder(r io.Reader) (d *Decoder)

NewDecoder creates a new Decoder reading from r.

Decoding BER requires single-byte reads. If r implements io.ByteReader then it is assumed that the reader is efficient enough so no buffering is done by d. Assuming that r produces a valid BER-encoding then d will never read more bytes than required to parse one element.

If r implements ElementReader and is reading a constructed element, d will decode those elements from r without additional buffering.

If r does not implement io.ByteReader then the Decoder will use its own buffering. If possible buffering is restricted to a single BER-encoded type: As long as the BER-encoded types read from r only use a definite-length format on the top-level element, d will not read more bytes from r than required to parse one element. If the indefinite-length encoding is used, then d might read more bytes from r than needed.

func (*Decoder) Decode

func (d *Decoder) Decode(val any) error

Decode parses a BER-encoded ASN.1 data structure and uses the reflect package to fill in an arbitrary value pointed at by val. Because Decode uses the reflect package, the structs being written to must use exported (upper case) field names. If val is nil or not a pointer, Unmarshal returns an error.

func (*Decoder) DecodeAll

func (d *Decoder) DecodeAll(val any) error

DecodeAll decodes all elements from d into the value pointed to by val. The value pointed to by val must be able to decode a constructed ASN.1 type. See Decoder.Decode for details on the decoding process.

This method blocks until the underlying reader of d returns io.EOF, or an error is encountered.

func (*Decoder) DecodeWithParams

func (d *Decoder) DecodeWithParams(val any, params string) error

DecodeWithParams works like Decoder.Decode but accepts additional parameters applied to the top-level element. The format for params is the same as for struct tags supported by this package. Using the `asn1:"optional"` or `asn1:"-"` options has no effect here.

func (*Decoder) More

func (d *Decoder) More() bool

More indicates whether there might be more elements in d that can be decoded.

If d encounters a BER-encoded type during decoding that is syntactically invalid, d tries to discard the respective element so that decoding can continue. However, some errors are irrecoverable so that d is in an unsafe state where decoding more elements may yield invalid results.

This method indicates whether d is in a valid state and can decode more elements. This method does not indicate if there are more elements. In particular a return value of true does not guarantee that d.Next() does not return an error.

After d.Next() has returned an io.EOF error, this method will return false.

func (*Decoder) Next

func (d *Decoder) Next() (Header, ElementReader, error)

Next parses the next element from d.

The returned ElementReader is valid until the next call to Next(). If the reader is not read to completion, any remaining bytes will be discarded when Next() is called. It is the responsibility of the caller to close the returned ElementReader in order to validate the syntax of any remaining bytes.

If no more elements are available, io.EOF is returned.

type ElementReader

type ElementReader interface {
	// Constructed reports whether this ElementReader is reading a constructed or
	// primitive element.
	Constructed() bool

	// Next parses the next child element of a constructed element. If Next is
	// called for an element that uses the primitive encoding, an error is returned.
	//
	// The returned ElementReader is valid until the next call to Next(). If it is
	// not read to completion, any remaining bytes will be discarded when Next() is
	// called again. It is the responsibility of the caller to close the returned
	// ElementReader in order to validate the syntax of any remaining bytes.
	//
	// If no more elements are available, io.EOF is returned.
	Next() (Header, ElementReader, error) // only constructed

	// More reports whether the reader is in a valid state to decode more data. This
	// method does not indicate if reading the next element or byte will succeed. In
	// particular a return value of true does not guarantee that Next() or Read()
	// does not return an error. After Next() or Read() has returned an io.EOF
	// error, this method will return false.
	//
	// For primitive elements this is equivalent to Len() > 0.
	More() bool

	// Len returns the number of bytes remaining in the reader or -1 if its size is
	// unknown. Before Read(), ReadByte(), Close(), or Next() is called this returns
	// the indicated length of the element. If the element uses the indefinite
	// length encoding but a parent element uses a fixed length encoding, this
	// returns the number of remaining bytes in that parent.
	//
	// When used with constructed elements the result after calling Next() is
	// undefined until the returned element has been closed.
	Len() int

	// Close discards any remaining bytes in the reader. If a constructed format is
	// used, this will validate the BER structure of the remaining bytes. If the
	// remaining bytes are not a valid BER encoding, an error is returned.
	//
	// It is safe to call Close() multiple times, however subsequent calls may
	// return different errors or nil.
	Close() error

	io.Reader     // only primitive
	io.ByteReader // only primitive
}

ElementReader is a reader type for reading a BER-encoded element.

Elements can use the primitive or constructed encoding method. Depending on the encoding method only certain methods can be used. Reading methods of an ElementReader return io.EOF when the element is read to completion, although the underlying data source might have more bytes available. Before the element is read to completion, io.ErrUnexpectedEOF may be returned.

Closing an ElementReader is optional. Closing validates that the remaining bytes of a constructed element are valid BER-encoding.

type EncodeError

type EncodeError struct {
	Value reflect.Value
	Err   error
}

EncodeError indicates that a value failed validation during encoding. Errors returned from a BerDecoder are wrapped in an EncodeError before they are returned from the Encoder.

func (*EncodeError) Error

func (e *EncodeError) Error() string

func (*EncodeError) Unwrap

func (e *EncodeError) Unwrap() error

type Encoder

type Encoder struct {
	// contains filtered or unexported fields
}

Encoder implements encoding ASN.1 types into a BER-encoded data stream. It is the counterpart to the Decoder type.

To create a new Encoder, use the NewEncoder function.

func NewEncoder

func NewEncoder(w io.Writer) *Encoder

NewEncoder creates a new Encoder. Writing BER data requires single-byte writes. If w implements io.ByteWriter it is assumed to be efficient enough so no additional buffering is done. If w does not implement io.ByteWriter, writes to w will be buffered. The buffer will be flushed after writing an element in Encoder.Encode or Encoder.EncodeWithParams.

func (*Encoder) Encode

func (e *Encoder) Encode(val any) error

Encode writes the BER-encoding of val to its underlying writer. If encoding fails, an error is returned. If a value fails validation before encoding, an EncodeError will be returned.

func (*Encoder) EncodeWithParams

func (e *Encoder) EncodeWithParams(val any, params string) (err error)

EncodeWithParams writes the BER-encoding of val to its underlying writer. The format for params is described in the asn1 package. Using the `asn1:"-"` option has no effect here.

type Flag

type Flag bool

A Flag accepts any data and is set to true if present. A flag cannot be encoded into BER. In most cases a Flag should be used on an optional element.

type Header struct {
	Tag         asn1.Tag
	Length      int
	Constructed bool
}

Header represents the BER header of an encoded element. The Length of an element indicates the number of bytes that make up the contents of the element. Length can also be the special value LengthIndefinite if the element uses the constructed indefinite-length encoding. In that case Constructed must also be set to true.

type InvalidDecodeError

type InvalidDecodeError struct {
	Value reflect.Value
	// contains filtered or unexported fields
}

InvalidDecodeError indicates that an invalid value was passed to an Unmarshal or Decode function. The invalid value might be nested within the passed value.

func (*InvalidDecodeError) Error

func (e *InvalidDecodeError) Error() string

type RawValue

type RawValue struct {
	Tag         asn1.Tag
	Constructed bool
	Bytes       []byte
}

A RawValue represents an un-decoded ASN.1 object. During decoding the syntax of structured elements is validated so the Bytes are guaranteed to contain a valid BER encoding. During encoding the bytes are written as-is without any validation.

func (RawValue) String

func (rv RawValue) String() string

String returns a string representation of rv. The byte contents of rv are only included if they are short enough.

type Sequence

type Sequence struct {
	Tag asn1.Tag // defaults to [UNIVERSAL 16]
	// contains filtered or unexported fields
}

Sequence is a type that simplifies the encoding of constructed BER elements. The zero value constitutes an empty sequence. If you want to implement a custom, constructed BerEncoder you can use a Sequence like this:

func (*myType) BerEncode() (asn1.Header, io.WriterTo, error) {
	s := &Sequence{
		Tag: asn1.Tag{asn1.ClassApplication, 15}
	}
	s.Append("A String")
	s.Append(42)
	return s.BerEncode()
}

Despite its name the Sequence type can be used to encode any constructed type, not just ASN.1 SEQUENCE types.

func SequenceOf

func SequenceOf(val any) (s *Sequence, err error)

SequenceOf returns a sequence containing the elements of the passed struct, slice, or array. If val is not a struct, slice, or array, or any if the values contained within val cannot be encoded, an error is returned.

func (*Sequence) Append

func (s *Sequence) Append(val ...any) error

Append adds an element at the end of the sequence. If the type of val does not permit encoding to BER an error of type UnsupportedTypeError is returned. In particular if the type of val is supported, no error will be returned. Validation is deferred to the BerEncode method.

func (*Sequence) AppendWithParams

func (s *Sequence) AppendWithParams(val any, params string) error

AppendWithParams adds an element at the end of the sequence. The format of params is the same as for struct tags documented in the documentation of this package. If the type of val does not permit encoding to BER an error of type UnsupportedTypeError is returned. In particular if the type of val is supported, no error will be returned. Validation is deferred to the BerEncode method.

func (*Sequence) BerEncode

func (s *Sequence) BerEncode() (Header, io.WriterTo, error)

BerEncode encodes the sequence into the BER format. The length of the returned header is calculated as follows:

  • If any of the sequence elements use the indefinite length format, the resulting length is also indefinite.
  • If the sum of the lengths of the elements of s overflows the int type, the resulting length is indefinite.
  • Otherwise the length is the sum of the lengths of the elements of s.

If encoding of any element fails, the error is returned by this method.

type StringReader

type StringReader struct {
	// contains filtered or unexported fields
}

StringReader implements reading of BER-encoded ASN.1 string types. String types can use the primitive or constructed encoding. When using the constructed encoding strings can be arbitrarily nested. StringReader understands both types of encodings and offers a flexible interface to read arbitrary string types.

A StringReader must be created via NewStringReader.

func NewStringReader

func NewStringReader(tag asn1.Tag, r ElementReader) *StringReader

NewStringReader creates a new StringReader reading from r. r can be constructed or primitive. If r is constructed, every child element must use the class and tag identified by the specified tag.

func (*StringReader) Bytes

func (r *StringReader) Bytes() ([]byte, error)

Bytes returns all unread bytes from r in a new byte slice. The returned slice may be retained by the caller. If a read error occurs, it is returned.

func (*StringReader) Constructed

func (r *StringReader) Constructed() bool

Constructed indicates whether r is using the constructed or primitive encoding.

func (*StringReader) Read

func (r *StringReader) Read(p []byte) (n int, err error)

Read reads the encoded string as a sequence of bytes. This method takes care of combining strings that use the constructed encoding. After the whole string has been read, io.EOF is returned.

func (*StringReader) ReadByte

func (r *StringReader) ReadByte() (b byte, err error)

ReadByte complements Read by only reading a single byte form r.

func (*StringReader) String

func (r *StringReader) String() (string, error)

String returns all unread bytes from r as a string.

func (*StringReader) Strings

func (r *StringReader) Strings() iter.Seq2[ElementReader, error]

Strings returns a sequence of string elements that use the primitive encoding. The error will be nil for all elements of the sequence, except potentially the last one. The sequence ends when io.EOF is encountered. Note that there will be no sequence element with an io.EOF error.

If reading has already begun via Read or ReadByte, the sequence will only contain elements that are completely unread. Any primitive element that has been partially read is discarded.

type StructuralError

type StructuralError struct {
	Tag  asn1.Tag // element being decoded
	Type reflect.Type
	Err  error
}

A StructuralError suggests that the ASN.1 data is valid, but the Go type which is receiving it doesn't match or can't fit the data.

See also SyntaxError.

func (*StructuralError) Error

func (e *StructuralError) Error() string

func (*StructuralError) Unwrap

func (e *StructuralError) Unwrap() error

type SyntaxError

type SyntaxError struct {
	Tag asn1.Tag // element in which the syntax error occurred
	Err error
}

A SyntaxError suggests that the ASN.1 data is invalid. This can either indicate that the nesting of structured elements contains an error, or that a primitive element could not be decoded from its BER representation.

For errors that are not directly related to the syntax of the BER byte stream, StructuralError is a better fit.

func (*SyntaxError) Error

func (e *SyntaxError) Error() string

func (*SyntaxError) Unwrap

func (e *SyntaxError) Unwrap() error

type UnsupportedTypeError

type UnsupportedTypeError struct {
	Type reflect.Type
	// contains filtered or unexported fields
}

UnsupportedTypeError indicates that a value was passed to Marshal or an Encode function that cannot be encoded to BER.

func (*UnsupportedTypeError) Error

func (e *UnsupportedTypeError) Error() string

Jump to

Keyboard shortcuts

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