cate

package module
v0.0.0-...-fdaa4dd Latest Latest
Warning

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

Go to latest
Published: Apr 2, 2026 License: Apache-2.0 Imports: 20 Imported by: 3

README

Compact (ASN.1) Tagged Encoding

Cate is a compact, ASN.1 compatible tagged binary encoding scheme, kind of like BSON.

Cate is

  • A lightweight binary encoding with explicit type tags.
  • Compatible with ASN.1 BER/DER structure.
  • Optimized for space efficiency while maintaining self-describing data.
  • Capable of encoding primitive types (integers, strings, booleans, etc) and composite types (structs, lists).

Cate is NOT

A general-purpose ASN.1 encoder or decoder. Cate can decode arbitrary ASN.1 TLVs, but value decoding support is intentionally limited to only what is necessary for encoding Go values. For example, supporting UtcTime or GeneralizedTime is explicitly not a goal because they are surplus to requirements - DateTime is sufficient for encoding Go values. Feature requests to support additional ASN.1 values are unlikely to be accepted unless they allow previously un-encodable Go values to be encoded.

Documentation

Overview

Package cate implements a Compact ASN.1 Tagged Encoding scheme.

Cate is a lightweight binary encoding with explicit type tags that is compatible with ASN.1 BER/DER structure. It's optimized for space efficiency while maintaining self-describing data. The package can encode and decode primitive types (integers, strings, booleans) and composite types (structs, slices).

Cate is not a general-purpose ASN.1 encoder or decoder. It can decode arbitrary ASN.1 TLVs, but value decoding support is intentionally limited to only what is necessary for encoding Go values.

Basic usage:

// Encoding
data, err := cate.Marshal(value)
if err != nil {
	// handle error
}

// Decoding
var result MyType
err = cate.Unmarshal(data, &result)
if err != nil {
	// handle error
}

For more control over encoding/decoding behavior, use the Options API:

// Enable implicit tagging and omit zero values
data, err := cate.Marshal(value,
	cate.UseImplicitTags(true),
	cate.OmitZero(true))

// Decode with the same options
err = cate.Unmarshal(data, &result,
	cate.UseImplicitTags(true),
	cate.OmitZero(true))

Custom types can implement the Marshaller and Unmarshaller interfaces to control their ASN.1 representation:

func (v MyType) MarshalCATE(enc *cate.Encoder) (cate.Value, error) {
	// Custom encoding logic
}

func (v *MyType) UnmarshalCATE(dec *cate.Decoder, val cate.Value) error {
	// Custom decoding logic
}

Reference Handling

**References are not currently supported for external types.**

Cate supports encoding and decoding of data structures with cyclic references. This allows for complex data structures like linked lists, trees with parent pointers, or general graph structures.

When encoding with references enabled:

  1. The encoder tracks each value it has seen using a map from value to reference ID.
  2. When a value is first encountered, it's assigned a unique reference ID and encoded normally.
  3. When a value is encountered again, only its reference ID is encoded.

During decoding:

  1. The decoder maintains a map from reference IDs to their corresponding values.
  2. When a reference is decoded, the decoder looks up the corresponding value.
  3. For forward references (references to values not yet seen), placeholder values are created and later populated when the actual value is decoded.

References are encoded using application-specific tags:

  • For a reference definition: Application-specific tag with reference type number, containing a SEQUENCE of (INTEGER reference-id, actual-value).
  • For a reference use: Application-specific tag with reference type number, containing just the INTEGER reference-id.

Types must implement the referenceable interface to participate in reference handling:

type referenceable interface {
    refKey() TagNumber
}

The refKey method returns the tag number to use for references to this type.

REAL

The first byte of a REAL value specifies how it is encoded. Bits 7-6 indicate the encoding:

  • 00 - decimal encoding
  • 01 - special values
  • 1x - binary encoding

The supported special values are:

  • 0x40 = +∞
  • 0x41 = -∞
  • 0x42 = NaN
  • 0x43 = -0.0

Decimal encoding is not supported. Decimal encoded values will be returned as [Raw]. For binary encoded values, the bit fields of the first byte are:

  • Bit 7 - always 1 for binary encoding.
  • Bit 6 - sign (0 = positive, 1 = negative).
  • Bit 5-4 - exponent base (0 = base 2, 1 = base 8, 2 = base 16).
  • Bit 3-2 - "scale factor", an extra base 2 exponent/multiplier with a value of 0-3.
  • Bit 1-0 - exponent length (0-2 = exponent is the next 1-3 bytes, 3 = the next byte is the exponent length in bytes).

The remaining bytes (after the exponent) are the mantissa. The value is S × N × 2^F × base^E (S=sign, N=mantissa, F=scale factor, E=exponent).

When encoding, CATE only uses base 2 and does not use the scale factor.

Index

Constants

View Source
const (
	// Universal is the universal ASN.1 tag class.
	Universal Class = 0x00

	// Application is the application-specific ASN.1 tag class.
	Application Class = 0x40

	// ContextSpecific is the context-specific ASN.1 tag class.
	ContextSpecific Class = 0x80

	// Private is the private ASN.1 tag class.
	Private Class = 0xC0

	// Primitive form indicates a single value.
	Primitive Form = 0x00

	// Constructed form indicates a structured value containing other values.
	Constructed Form = 0x20
)

Variables

This section is empty.

Functions

func Copy

func Copy(dst io.Writer, src io.Reader) error

Copy copies a single ASN.1 TLV from the source to the destination.

func Dump

func Dump(wr io.Writer, rd io.Reader, prefix, indent string)

Dump writes a human-readable representation of the ASN.1 TLV data read from rd to wr. prefix is prepended to each line and indent is appended to the prefix for each level of nesting.

func GetOption

func GetOption[V any](o Options, opt func(V) Options) V

GetOption retrieves the value of an option.

func GetParent

func GetParent[V any](dec *Decoder) (V, bool)

GetParent walks up the decode stack and returns the first parent/ancestor of type V.

func Marshal

func Marshal(value any, opts ...Options) ([]byte, error)

Marshal encodes the provided value into ASN.1 BER-encoded data. It handles encoding various Go types such as basic types, structs, and slices.

func Unmarshal

func Unmarshal(data []byte, target any, opts ...Options) error

Unmarshal decodes ASN.1 BER-encoded data into the provided target value. It handles decoding into various Go types such as basic types, structs, and slices.

The data parameter should contain the complete ASN.1 TLV (Tag-Length-Value) encoding. The target parameter should be a pointer to the value to decode into. The opts parameter allows customizing the decoding behavior.

Types

type BigInt

type BigInt big.Int

BigInt represents an ASN.1 INTEGER value for arbitrary precision integers.

func (*BigInt) Tag

func (v *BigInt) Tag() Tag

Tag returns the ASN.1 tag for BigInt values.

type BigReal

type BigReal big.Float

BigReal represents an ASN.1 REAL value for arbitrary precision floating point numbers.

func (*BigReal) Tag

func (v *BigReal) Tag() Tag

Tag returns the ASN.1 tag for BigReal values.

type Boolean

type Boolean bool

Boolean represents an ASN.1 BOOLEAN value.

func (Boolean) Tag

func (v Boolean) Tag() Tag

Tag returns the ASN.1 tag for Boolean values.

type Class

type Class byte

Class represents the class part of an ASN.1 tag.

func (Class) IsApplication

func (c Class) IsApplication() bool

IsApplication returns true if the class is Application.

func (Class) IsContextSpecific

func (c Class) IsContextSpecific() bool

IsContextSpecific returns true if the class is ContextSpecific.

func (Class) IsPrivate

func (c Class) IsPrivate() bool

IsPrivate returns true if the class is Private.

func (Class) IsUniversal

func (c Class) IsUniversal() bool

IsUniversal returns true if the class is Universal.

func (Class) String

func (c Class) String() string

String returns a string representation of the tag class.

type ConstructedValue

type ConstructedValue struct {
	Class  Class
	Number TagNumber
	Value  Sequence
}

ConstructedValue represents an ASN.1 constructed value.

func (*ConstructedValue) All

func (v *ConstructedValue) All() iter.Seq[Value]

func (*ConstructedValue) String

func (v *ConstructedValue) String() string

func (*ConstructedValue) Tag

func (v *ConstructedValue) Tag() Tag

Tag returns an ASN.1 constructed value tag.

type DateTime

type DateTime time.Time

DateTime represents an ASN.1 DATE-TIME value.

func (DateTime) Tag

func (v DateTime) Tag() Tag

Tag returns the ASN.1 tag for DateTime values.

type Decoder

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

Decoder is an ASN.1 decoder that can decode ASN.1 values.

func NewDecoder

func NewDecoder(opts ...Options) *Decoder

NewDecoder creates a new ASN.1 decoder with the specified options.

func (*Decoder) Decode

func (d *Decoder) Decode(v Value, target any) error

Decode decodes an ASN.1 value into the provided target. The target should be a pointer to the value to decode into.

func (*Decoder) ReadFrom

func (d *Decoder) ReadFrom(rd io.Reader, target any) error

ReadFrom reads the next ASN.1 BER-encoded value from rd and decodes it into target. The target should be a pointer to the value to decode into.

func (*Decoder) With

func (d *Decoder) With(opts ...Options) *Decoder

With creates a new Decoder with additional options. The returned decoder shares the same internal state as the original, but uses the merged options for decoding.

type EOC

type EOC struct{}

EOC represents an ASN.1 End-Of-Content value, used to terminate constructed values with indefinite length.

func (EOC) Tag

func (v EOC) Tag() Tag

Tag returns the ASN.1 tag for EOC values.

type Encoder

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

Encoder is an ASN.1 encoder that can encode Go values to ASN.1 values.

func NewEncoder

func NewEncoder(opts ...Options) *Encoder

NewEncoder creates a new ASN.1 encoder with the specified options.

func (*Encoder) Encode

func (e *Encoder) Encode(v any) (Value, error)

Encode converts a Go value to its ASN.1 representation. Various Go types are mapped to specific ASN.1 types as defined by the package.

func (*Encoder) With

func (e *Encoder) With(opts ...Options) *Encoder

With creates a new Encoder with additional options. The returned encoder merges the original options with the provided ones.

func (*Encoder) WriteTo

func (e *Encoder) WriteTo(wr io.Writer, v any) error

WriteTo encodes a Go value and writes the ASN.1 BER encoding to wr.

type Form

type Form byte

Form represents the form (primitive or constructed) part of an ASN.1 tag.

func (Form) IsConstructed

func (f Form) IsConstructed() bool

IsConstructed returns true if the form is Constructed.

func (Form) IsPrimitive

func (f Form) IsPrimitive() bool

IsPrimitive returns true if the form is Primitive.

func (Form) String

func (f Form) String() string

String returns a string representation of the tag form.

type Integer

type Integer int64

Integer represents an ASN.1 INTEGER value that fits within an int64.

func (Integer) Tag

func (v Integer) Tag() Tag

Tag returns the ASN.1 tag for Integer values.

type Marshaller

type Marshaller interface {
	// MarshalCATE returns the ASN.1 representation of the value.
	MarshalCATE(*Encoder) (Value, error)
}

Marshaller is the interface implemented by types that can marshal themselves into ASN.1 values.

type MarshallerTo

type MarshallerTo interface {
	MarshalToCATE(*Encoder, Writer) error
}

MarshallerTo is the interface implemented by types that can marshal themselves directly into a stream. It is the stream-based counterpart to Marshaller and avoids building an intermediate Value tree.

type Null

type Null struct{}

Null represents an ASN.1 NULL value.

func (Null) Tag

func (v Null) Tag() Tag

Tag returns the ASN.1 tag for Null values.

type OctetString

type OctetString []byte

OctetString represents an ASN.1 OCTET STRING, which is a sequence of bytes.

func (OctetString) String

func (v OctetString) String() string

func (OctetString) Tag

func (v OctetString) Tag() Tag

Tag returns the ASN.1 tag for OctetString values.

type Options

type Options interface {
	// contains filtered or unexported methods
}

Options represents a set of configuration options for encoding or decoding.

func EmitSchemas

func EmitSchemas(v bool) Options

EmitSchemas configures whether to include type schema information in the encoding. When enabled, values are encoded with their type schema, allowing for type checking during decoding.

func MatchFieldsByName

func MatchFieldsByName(v bool) Options

MatchFieldsByName enables matching fields by name when decoding, instead of by field number. Specifically, when decoding struct values tagged with a schema, allow T to be decoded into S if, for each field F of T there is a corresponding field G of S with the same name and a compatible type.

func MergeOptions

func MergeOptions(opts ...Options) Options

MergeOptions combines multiple Options into a single Options. Later options take precedence over earlier ones.

func OmitZero

func OmitZero(v bool) Options

OmitZero configures whether to omit zero values when encoding. When enabled, struct fields with zero values will not be included in the encoding.

func OrderedMaps

func OrderedMaps(v bool) Options

OrderedMaps enables ordering of map entries when encoding a map that has keys of a type that has a natural ordering, such as strings or numbers.

func PassByReference

func PassByReference[T Referenceable](tag TagNumber) Options

PassByReference enables pass-by-reference encoding for values of type T. When a value of type T is encoded or decoded, it is assigned a reference ID and tagged with the given application tag number. Subsequent occurrences of the same value are encoded as a reference to that ID rather than re-encoding the full value.

func ReserveAppSpecific

func ReserveAppSpecific(num TagNumber) Options

ReserveAppSpecific marks an application-specific tag number as in-use without associating it with a Go type. This prevents CATE from interpreting data tagged with that number as reference or schema framing. Use it when manually encoding data that uses a particular application tag and you need to protect it from being misinterpreted by CATE.

func UseImplicitTags

func UseImplicitTags(v bool) Options

UseImplicitTags configures whether to use implicit tagging for context-specific types. When enabled, primitive values are encoded directly with context-specific tags instead of being wrapped in a constructed value.

func WithExternalReferences

func WithExternalReferences[T Referenceable](tag TagNumber, refs map[uint64]T) Options

WithExternalReferences establishes a pre-agreed map of reference IDs to values of type T that are negotiated out-of-band. Unlike WithKnownReferences, the encoder will NOT emit these values into the stream — it encodes only the reference ID. The decoder resolves IDs from the provided map without expecting to see the full value definition. Implies PassByReference for T.

Use this when both sides have agreed on a reference dictionary in advance and the stream should contain only reference IDs, not full definitions.

func WithExternalSchemas

func WithExternalSchemas(refs map[uint64]Schema) Options

WithExternalSchemas is a convenience wrapper around WithExternalReferences for Schema values. The encoder will not emit these schemas into the stream — it encodes only their IDs. The decoder resolves IDs from the provided map. Implies EmitSchemas.

Use this when both sides have agreed on a schema dictionary in advance and the stream should contain only schema IDs, not full schema definitions.

func WithKnownReferences

func WithKnownReferences[T Referenceable](tag TagNumber, refs map[uint64]T) Options

WithKnownReferences establishes a pre-agreed map of reference IDs to values of type T. Both encoder and decoder share this map: the encoder emits each value with its assigned ID, and the decoder can resolve IDs that appear in the stream using the map. Implies PassByReference for T.

Use this when both sides share a reference dictionary and full value definitions should still be included in the stream.

func WithKnownSchemas

func WithKnownSchemas(refs map[uint64]Schema) Options

WithKnownSchemas is a convenience wrapper around WithKnownReferences for Schema values. The encoder emits each schema with its assigned ID; subsequent uses encode only the ID rather than the full schema. The decoder resolves schema IDs from the provided map. Implies EmitSchemas.

Use this when both sides share a schema dictionary and full schema definitions should still be included in the stream.

type PrimitiveValue

type PrimitiveValue struct {
	Class  Class
	Number TagNumber
	Value  []byte
}

PrimitiveValue represents an ASN.1 primitive value.

func (*PrimitiveValue) String

func (v *PrimitiveValue) String() string

func (*PrimitiveValue) Tag

func (v *PrimitiveValue) Tag() Tag

Tag returns an ASN.1 primitive value tag.

type Reader

type Reader interface {
	io.Reader
	io.ByteReader
	Next() error
	Tag() Tag
	Len() int
	Value() (Value, error)
}

Reader reads ASN.1 TLV-encoded data as a stream.

Next advances to the next TLV element, reading its tag and length. After a successful call to Next, Tag returns the element's tag, and the caller may consume its content in one of three ways:

  • Value returns the element as a fully-parsed Value. For constructed values this recursively parses all children.

  • Read and ReadByte read raw content bytes from a primitive element.

  • For a constructed element, the caller calls Next again to iterate over child elements. When all children have been consumed, Next returns an end-of-contents sentinel (Tag == 0, length == 0).

Read and ReadByte must only be called after Next advances to a primitive element; they return an error for constructed elements. Conversely, calling Next to descend into children is only valid for constructed elements. The element's length limits how many bytes Read and ReadByte may return.

Tag and Value must not be called before a successful call to Next.

func NewReader

func NewReader(rd io.Reader) Reader

type Real

type Real float64

Real represents an ASN.1 REAL value that fits within a float64.

func (Real) Tag

func (v Real) Tag() Tag

Tag returns the ASN.1 tag for Real values.

type Referenceable

type Referenceable interface {
	IsReferenceable() bool
}

Referenceable is implemented by types that may participate in pass-by-reference encoding. IsReferenceable returns true when a specific value should be tracked by reference; returning false excludes that value. Registering a type with PassByReference is necessary for its values to be encoded by reference.

type Schema

type Schema interface {
	Referenceable
	// contains filtered or unexported methods
}

Schema is the schema for a type. It is used internally to validate decoded values. Its only external use is for applications that wish to manage schema IDs externally.

func SchemaFor

func SchemaFor(typ reflect.Type) (Schema, error)

SchemaFor returns the schema for the given type.

type Sequence

type Sequence []Value

Sequence represents an ASN.1 SEQUENCE, which is an ordered collection of values.

func (Sequence) All

func (v Sequence) All() iter.Seq[Value]

func (Sequence) String

func (v Sequence) String() string

func (Sequence) Tag

func (v Sequence) Tag() Tag

Tag returns the ASN.1 tag for Sequence values.

type SequenceValue

type SequenceValue interface {
	Value

	All() iter.Seq[Value]
}

SequenceValue is a Value that contains an ordered sequence of child values. It is implemented by Sequence, Set, and ConstructedValue.

type Set

type Set []Value

Set represents an ASN.1 SET, which is an unordered collection of values.

func (Set) All

func (v Set) All() iter.Seq[Value]

func (Set) String

func (v Set) String() string

func (Set) Tag

func (v Set) Tag() Tag

Tag returns the ASN.1 tag for Set values.

type Tag

type Tag uint

Tag represents an ASN.1 tag, composed of class, form, and number components.

func (Tag) Class

func (t Tag) Class() Class

Class returns the class component of the tag.

func (Tag) Form

func (t Tag) Form() Form

Form returns the form component of the tag.

func (Tag) MayBeConstructed

func (t Tag) MayBeConstructed() bool

MayBeConstructed returns true if the tagged value may be represented in constructed form.

func (Tag) MayBePrimitive

func (t Tag) MayBePrimitive() bool

MayBePrimitive returns true if the tagged value may be represented in primitive form.

func (Tag) MayHaveIndefiniteLength

func (t Tag) MayHaveIndefiniteLength() bool

MayHaveIndefiniteLength returns true if the tagged value may have an indefinite length.

func (Tag) Number

func (t Tag) Number() TagNumber

Number returns the number component of the tag.

func (Tag) String

func (t Tag) String() string

String returns a string representation of the tag.

type TagNumber

type TagNumber uint

TagNumber represents the number part of an ASN.1 tag.

const (
	EOCTag              TagNumber = 0x00
	BooleanTag          TagNumber = 0x01
	IntegerTag          TagNumber = 0x02
	BitStringTag        TagNumber = 0x03
	OctetStringTag      TagNumber = 0x04
	NullTag             TagNumber = 0x05
	ObjectIdentifierTag TagNumber = 0x06
	ObjectDescriptorTag TagNumber = 0x07
	ExternalTag         TagNumber = 0x08
	RealTag             TagNumber = 0x09
	EnumeratedTag       TagNumber = 0x0A
	EmbeddedPdvTag      TagNumber = 0x0B
	UTF8StringTag       TagNumber = 0x0C
	RelativeOidTag      TagNumber = 0x0D
	TimeTag             TagNumber = 0x0E
	SequenceTag         TagNumber = 0x10
	SetTag              TagNumber = 0x11
	NumericStringTag    TagNumber = 0x12
	PrintableStringTag  TagNumber = 0x13
	TeletexStringTag    TagNumber = 0x14
	VideotexStringTag   TagNumber = 0x15
	IA5StringTag        TagNumber = 0x16
	UTCTimeTag          TagNumber = 0x17
	GeneralizedTimeTag  TagNumber = 0x18
	GraphicStringTag    TagNumber = 0x19
	VisibleStringTag    TagNumber = 0x1A
	GeneralStringTag    TagNumber = 0x1B
	UniversalStringTag  TagNumber = 0x1C
	CharacterStringTag  TagNumber = 0x1D
	BmpStringTag        TagNumber = 0x1E
	DateTag             TagNumber = 0x1F
	TimeOfDayTag        TagNumber = 0x20
	DateTimeTag         TagNumber = 0x21
	DurationTag         TagNumber = 0x22
	OidIriTag           TagNumber = 0x23
	RelativeOidIriTag   TagNumber = 0x24
)

ASN.1 universal tag numbers

func (TagNumber) IsKnown

func (n TagNumber) IsKnown() bool

IsKnown returns true if the tag number is a known universal tag.

func (TagNumber) String

func (n TagNumber) String() string

String returns a string representation of the tag number.

type UTF8String

type UTF8String string

UTF8String represents an ASN.1 UTF8String, which is a UTF-8 encoded string.

func (UTF8String) Tag

func (v UTF8String) Tag() Tag

Tag returns the ASN.1 tag for UTF8String values.

type Unmarshaller

type Unmarshaller interface {
	// UnmarshalCATE sets the value from the provided ASN.1 value.
	UnmarshalCATE(*Decoder, Value) error
}

Unmarshaller is the interface implemented by types that can unmarshal ASN.1 values into themselves.

type UnmarshallerFrom

type UnmarshallerFrom interface {
	UnmarshalFromCATE(*Decoder, Reader) error
}

UnmarshallerFrom is the interface implemented by types that can unmarshal themselves directly from a stream. It is the stream-based counterpart to Unmarshaller and avoids building an intermediate Value tree.

type Value

type Value interface {
	// Tag returns the ASN.1 tag for this value.
	Tag() Tag
}

Value is an ASN.1 value.

type Writer

type Writer interface {
	io.Writer
	io.StringWriter
	io.ByteWriter
	WriteTag(Tag, int) error
	WriteValue(Value) error
	End() error
}

Writer writes ASN.1 TLV-encoded data as a stream.

A value is written in one of two ways:

  • WriteValue writes a complete Value (tag, length, and content) in one call. No call to End is needed.

  • WriteTag opens a new value by writing its tag and length. For primitive values, the caller writes the content bytes via Write, WriteByte, or WriteString. No call to End is needed; the value is complete once all content bytes have been written. For constructed values, the caller writes child values (via WriteTag/End or WriteValue), then calls End.

For constructed values, WriteTag pushes a frame. End pops the frame: if the length was indefinite (n < 0) it emits an end-of-contents marker; if definite, it verifies that all bytes were written.

As a special case, WriteTag with n == 0 does not push a frame and the caller must not call Write, WriteByte, WriteString, or End for that value.

Write, WriteByte, and WriteString must only be called after WriteTag opens a primitive value. Conversely, WriteTag and WriteValue must not be called after opening a primitive value. In other words, primitive values accept only raw bytes; constructed values accept only child values.

func NewWriter

func NewWriter(wr io.Writer) Writer

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL