View Source Nostr.TLV (Nostr Lib v0.1.1)

Provides functions to decode, encode and work with ASN.1 BER-TLV structures. It supports tags encoded on multiple bytes, length encoded on multiple bytes, indefinite length and both primitive and constructed TLVs.

Example:

iex> data = "nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p"
iex> bytes = Nostr.Bech32.nprofile_to_bytes(data)
iex> Nostr.TLV.decode(bytes)
{}

Summary

Types

t()

Represents a BER-TLV structure. The tag is represented as an integer and header bits are not separated from the tag. This is to accommodate common usages of the encoding. Primitive TLVs have a binary value, while constructed have a list of TLVs. The indefinite_length field is set by the decoder to reflect the original byte representation and also serves as instruction to the encoder on how to encode the length.

Functions

Decodes (recursively) the first binary encoded TLV in the given binary and returns a tuple with the parsed TLV structure and the remaining data as a binary in the form {parsed_tlv, remaining_data}. On error it returns the atom :no_tlv.

Encodes (recursively) the given TLV structure and returns its binary representation according to the BER-TLV specifications.

Types

@type t() :: %Nostr.TLV{
  indefinite_length: boolean(),
  tag: integer(),
  value: binary() | [t()]
}

Represents a BER-TLV structure. The tag is represented as an integer and header bits are not separated from the tag. This is to accommodate common usages of the encoding. Primitive TLVs have a binary value, while constructed have a list of TLVs. The indefinite_length field is set by the decoder to reflect the original byte representation and also serves as instruction to the encoder on how to encode the length.

Functions

@spec decode(binary()) :: {t(), binary()} | :no_tlv

Decodes (recursively) the first binary encoded TLV in the given binary and returns a tuple with the parsed TLV structure and the remaining data as a binary in the form {parsed_tlv, remaining_data}. On error it returns the atom :no_tlv.

The decoder tolerates lengths encoded on more bytes than strictly needed, although the ASN.1 specification require encoders to always generate the shortest possible encoding.

Examples

# Primitive TLV + data after its end
iex> Nostr.TLV.decode(<<0x80, 0x02, 0xCA, 0xFE, 0xAA, 0xBB>>)
{%Nostr.TLV{indefinite_length: false, tag: 0x80, value: <<0xCA, 0xFE>>}, <<0xAA, 0xBB>>}

# Constructed TLV
iex> Nostr.TLV.decode(<<0xE0, 0x03, 0x80, 0x01, 0xAA>>)
{%Nostr.TLV{indefinite_length: false, tag: 0xE0, value:
  [%Nostr.TLV{indefinite_length: false, tag: 0x80, value: <<0xAA>>}]
}, ""}

# Malformed TLV
iex> Nostr.TLV.decode(<<0x80, 0x02, 0x00>>)
:no_tlv
@spec encode(t()) :: binary()

Encodes (recursively) the given TLV structure and returns its binary representation according to the BER-TLV specifications.

If indefinite_length is set to true and the TLV is a constructed one, indefinite length encoding will be use. For primitive TLVs this field will be ignored, since for primitive TLVs only definite length is allowed.

The primitive/constructed bit of the tag field will not be checked for consistency with the value field.

Examples

# Primitive TLV
iex> Nostr.TLV.encode(%Nostr.TLV{tag: 0x80, value: <<0xAA, 0xBB>>})
<<0x80, 0x02, 0xAA, 0xBB>>

# Constructed TLV
iex> Nostr.TLV.encode(%Nostr.TLV{tag: 0xE0, value: [%Nostr.TLV{tag: 0x80, value: <<0xAA>>}]})
<<0xE0, 0x03, 0x80, 0x01, 0xAA>>