SnmpKit.SnmpLib.ASN1 (snmpkit v0.6.4)

Comprehensive ASN.1 BER (Basic Encoding Rules) encoding and decoding utilities.

Provides low-level ASN.1 operations that are used by the PDU module and can be used for other ASN.1 encoding/decoding needs. This module offers improved performance and better error handling compared to basic implementations.

Features

  • Complete BER encoding/decoding support
  • Optimized length handling for large values
  • Comprehensive error reporting
  • Support for constructed and primitive types
  • Memory-efficient implementations
  • Validation and constraint checking
  • RFC-compliant OID multibyte encoding (values ≥ 128)

ASN.1 Types Supported

  • INTEGER: Signed integers with arbitrary precision
  • OCTET STRING: Binary data of any length
  • NULL: Null value representation
  • OBJECT IDENTIFIER: Hierarchical object identifiers with multibyte subidentifiers
  • SEQUENCE: Constructed type for complex structures
  • Custom Tags: Application-specific and context-specific tags

Important: OID Encoding

OID subidentifiers use 7-bit encoding where values ≥ 128 require multibyte encoding:

  • Values 0-127: Single byte
  • Values 128+: Multibyte with continuation bits
  • Example: 200 → [0x81, 0x48] (not single byte 0xC8)

Examples

# Integer encoding/decoding
iex> {:ok, encoded} = SnmpKit.SnmpLib.ASN1.encode_integer(42)
iex> {:ok, {value, <<>>}} = SnmpKit.SnmpLib.ASN1.decode_integer(encoded)
iex> value
42

# OCTET STRING encoding/decoding
iex> {:ok, encoded} = SnmpKit.SnmpLib.ASN1.encode_octet_string("Hello")
iex> {:ok, {value, <<>>}} = SnmpKit.SnmpLib.ASN1.decode_octet_string(encoded)
iex> value
"Hello"

# OID encoding/decoding with multibyte values
iex> {:ok, encoded} = SnmpKit.SnmpLib.ASN1.encode_oid([1, 3, 6, 1, 4, 1, 200])
iex> {:ok, {oid, <<>>}} = SnmpKit.SnmpLib.ASN1.decode_oid(encoded)
iex> oid
[1, 3, 6, 1, 4, 1, 200]

# Length encoding for large values
iex> encoded_length = SnmpKit.SnmpLib.ASN1.encode_length(1000)
iex> {:ok, {length, <<>>}} = SnmpKit.SnmpLib.ASN1.decode_length(encoded_length)
iex> length
1000

# NULL encoding
iex> {:ok, encoded} = SnmpKit.SnmpLib.ASN1.encode_null()
iex> {:ok, {value, <<>>}} = SnmpKit.SnmpLib.ASN1.decode_null(encoded)
iex> value
:null

Summary

Functions

Calculates the total length of a BER-encoded structure.

Decodes an ASN.1 INTEGER value.

Decodes an ASN.1 length field.

Decodes an ASN.1 NULL value.

Decodes an ASN.1 OCTET STRING value.

Decodes an ASN.1 OBJECT IDENTIFIER value.

Decodes an ASN.1 SEQUENCE value.

Decodes a generic TLV structure.

Encodes a custom TLV (Tag-Length-Value) structure.

Encodes an ASN.1 INTEGER value using BER (Basic Encoding Rules).

Encodes an ASN.1 length field.

Encodes an ASN.1 NULL value.

Encodes an ASN.1 OCTET STRING value.

Encodes an ASN.1 OBJECT IDENTIFIER (OID) value using BER encoding.

Encodes an ASN.1 SEQUENCE with the given content.

Parses an ASN.1 tag byte.

Validates the structure of BER-encoded data.

Types

content()

@type content() :: binary()

length()

@type length() :: non_neg_integer()

oid()

@type oid() :: [non_neg_integer()]

tag()

@type tag() :: non_neg_integer()

tlv()

@type tlv() :: {tag(), length(), content()}

Functions

calculate_ber_length(arg1)

@spec calculate_ber_length(binary()) :: {:ok, non_neg_integer()} | {:error, atom()}

Calculates the total length of a BER-encoded structure.

Parameters

  • data: BER-encoded binary data

Returns

  • {:ok, total_length} on success
  • {:error, reason} on failure

Examples

{:ok, 10} = SnmpKit.SnmpLib.ASN1.calculate_ber_length(ber_data)

decode_integer(arg1)

@spec decode_integer(binary()) :: {:ok, {integer(), binary()}} | {:error, atom()}

Decodes an ASN.1 INTEGER value.

Parameters

  • data: Binary data starting with an INTEGER TLV

Returns

  • {:ok, {value, remaining_data}} on success
  • {:error, reason} on failure

Examples

{:ok, {42, remaining}} = SnmpKit.SnmpLib.ASN1.decode_integer(<<2, 1, 42, 99, 100>>)
# Returns {42, <<99, 100>>}

decode_length(arg1)

@spec decode_length(binary()) :: {:ok, {length(), binary()}} | {:error, atom()}

Decodes an ASN.1 length field.

Parameters

  • data: Binary data starting with a length field

Returns

  • {:ok, {length, remaining_data}} on success
  • {:error, reason} on failure

Examples

{:ok, {42, remaining}} = SnmpKit.SnmpLib.ASN1.decode_length(<<42, 1, 2, 3>>)
{:ok, {300, remaining}} = SnmpKit.SnmpLib.ASN1.decode_length(<<0x82, 1, 44, 1, 2, 3>>)

decode_null(arg1)

@spec decode_null(binary()) :: {:ok, {:null, binary()}} | {:error, atom()}

Decodes an ASN.1 NULL value.

Examples

{:ok, {:null, remaining}} = SnmpKit.SnmpLib.ASN1.decode_null(<<5, 0, 1, 2, 3>>)
# Returns {:null, <<1, 2, 3>>}

decode_octet_string(arg1)

@spec decode_octet_string(binary()) :: {:ok, {binary(), binary()}} | {:error, atom()}

Decodes an ASN.1 OCTET STRING value.

Examples

{:ok, {"Hello", remaining}} = SnmpKit.SnmpLib.ASN1.decode_octet_string(encoded_data)

decode_oid(arg1)

@spec decode_oid(binary()) :: {:ok, {oid(), binary()}} | {:error, atom()}

Decodes an ASN.1 OBJECT IDENTIFIER value.

Examples

{:ok, {[1, 3, 6, 1], remaining}} = SnmpKit.SnmpLib.ASN1.decode_oid(encoded_data)

decode_sequence(arg1)

@spec decode_sequence(binary()) :: {:ok, {binary(), binary()}} | {:error, atom()}

Decodes an ASN.1 SEQUENCE value.

Returns the content of the sequence without further parsing.

Examples

{:ok, {sequence_content, remaining}} = SnmpKit.SnmpLib.ASN1.decode_sequence(encoded_data)

decode_tlv(arg1)

@spec decode_tlv(binary()) :: {:ok, {tag(), binary(), binary()}} | {:error, atom()}

Decodes a generic TLV structure.

Parameters

  • data: Binary data starting with a TLV

Returns

  • {:ok, {tag, content, remaining}} on success
  • {:error, reason} on failure

Examples

{:ok, {tag, content, remaining}} = SnmpKit.SnmpLib.ASN1.decode_tlv(binary_data)

encode_custom_tlv(tag, content)

@spec encode_custom_tlv(tag(), binary()) :: {:ok, binary()}

Encodes a custom TLV (Tag-Length-Value) structure.

Parameters

  • tag: ASN.1 tag value
  • content: Binary content

Examples

{:ok, tlv} = SnmpKit.SnmpLib.ASN1.encode_custom_tlv(0xA0, <<"custom_content">>)

encode_integer(value)

@spec encode_integer(integer()) :: {:ok, binary()}

Encodes an ASN.1 INTEGER value using BER (Basic Encoding Rules).

Supports arbitrary precision integers using two's complement representation. The encoded result includes the ASN.1 tag (0x02), length, and content bytes.

Parameters

  • value: Integer value to encode (any size)

Returns

  • {:ok, encoded_bytes} on success
  • {:error, reason} on failure

Examples

# Positive integers
iex> {:ok, encoded} = SnmpKit.SnmpLib.ASN1.encode_integer(42)
iex> encoded
<<2, 1, 42>>

# Negative integers (two's complement)
iex> {:ok, encoded} = SnmpKit.SnmpLib.ASN1.encode_integer(-1)
iex> encoded
<<2, 1, 255>>

# Zero
iex> {:ok, encoded} = SnmpKit.SnmpLib.ASN1.encode_integer(0)
iex> encoded
<<2, 1, 0>>

# Large integers
iex> {:ok, encoded} = SnmpKit.SnmpLib.ASN1.encode_integer(32767)
iex> byte_size(encoded) > 3
true

encode_length(length)

@spec encode_length(length()) :: binary()

Encodes an ASN.1 length field.

Supports both short form (< 128) and long form encoding.

Parameters

  • length: Length value to encode

Returns

  • Binary representation of the length

Examples

<<42>> = SnmpKit.SnmpLib.ASN1.encode_length(42)
<<0x81, 200>> = SnmpKit.SnmpLib.ASN1.encode_length(200)
<<0x82, 1, 44>> = SnmpKit.SnmpLib.ASN1.encode_length(300)

encode_null()

@spec encode_null() :: {:ok, binary()}

Encodes an ASN.1 NULL value.

Examples

{:ok, <<5, 0>>} = SnmpKit.SnmpLib.ASN1.encode_null()

encode_octet_string(value)

@spec encode_octet_string(binary()) :: {:ok, binary()}

Encodes an ASN.1 OCTET STRING value.

Parameters

  • value: Binary data to encode

Examples

{:ok, encoded} = SnmpKit.SnmpLib.ASN1.encode_octet_string("Hello")
{:ok, encoded} = SnmpKit.SnmpLib.ASN1.encode_octet_string(<<1, 2, 3, 4>>)

encode_oid(oid_list)

@spec encode_oid(oid()) :: {:ok, binary()} | {:error, atom()}

Encodes an ASN.1 OBJECT IDENTIFIER (OID) value using BER encoding.

OIDs are hierarchical identifiers where each component is encoded using 7-bit subidentifiers. Values ≥ 128 require multibyte encoding with continuation bits, which is correctly handled by this implementation.

Parameters

  • oid_list: List of non-negative integers representing the OID (minimum 2 components)

Returns

  • {:ok, encoded_bytes} on success
  • {:error, reason} on failure (invalid OID format)

Encoding Rules

  • First two components are combined: first * 40 + second
  • Remaining components use 7-bit encoding with continuation bits
  • Values 0-127: single byte
  • Values 128+: multibyte with high bit indicating continuation

Examples

# Standard SNMP OID (sysDescr.0)
iex> {:ok, encoded} = SnmpKit.SnmpLib.ASN1.encode_oid([1, 3, 6, 1, 2, 1, 1, 1, 0])
iex> {:ok, {decoded, <<>>}} = SnmpKit.SnmpLib.ASN1.decode_oid(encoded)
iex> decoded
[1, 3, 6, 1, 2, 1, 1, 1, 0]

# OID with multibyte values (≥ 128)
iex> {:ok, encoded} = SnmpKit.SnmpLib.ASN1.encode_oid([1, 3, 6, 1, 4, 1, 200])
iex> {:ok, {decoded, <<>>}} = SnmpKit.SnmpLib.ASN1.decode_oid(encoded)
iex> decoded
[1, 3, 6, 1, 4, 1, 200]

# Invalid OIDs
iex> SnmpKit.SnmpLib.ASN1.encode_oid([])
{:error, :invalid_oid}

iex> SnmpKit.SnmpLib.ASN1.encode_oid([1])
{:ok, <<6, 1, 1>>}

encode_sequence(content)

@spec encode_sequence(binary()) :: {:ok, binary()}

Encodes an ASN.1 SEQUENCE with the given content.

Parameters

  • content: Pre-encoded content for the sequence

Examples

content = encode_integer_content(42) <> encode_octet_string_content("test")
{:ok, sequence} = SnmpKit.SnmpLib.ASN1.encode_sequence(content)

parse_tag(tag_byte)

@spec parse_tag(tag()) :: map()

Parses an ASN.1 tag byte.

Returns information about the tag including class, constructed bit, and tag number.

Parameters

  • tag_byte: Single byte representing the tag

Returns

  • Map with tag information

Examples

info = SnmpKit.SnmpLib.ASN1.parse_tag(0x30)
# Returns %{class: :universal, constructed: true, tag_number: 16}

validate_ber_structure(data)

@spec validate_ber_structure(binary()) :: :ok | {:error, atom()}

Validates the structure of BER-encoded data.

Performs basic validation without full decoding.

Parameters

  • data: BER-encoded binary data

Returns

  • :ok if structure is valid
  • {:error, reason} if structure is invalid

Examples

:ok = SnmpKit.SnmpLib.ASN1.validate_ber_structure(valid_ber_data)
{:error, :invalid_length} = SnmpKit.SnmpLib.ASN1.validate_ber_structure(malformed_data)