class
ASN1::BER
Overview
A single ASN.1 Basic Encoding Rules (BER) TLV element: an identifier (tag), a length and a payload. Used to build and parse SNMP, LDAP, X.509 and similar protocols.
require "bindata/asn1"
ber = ASN1::BER.new
ber.set_integer(42)
io.write_bytes(ber)
ber = io.read_bytes(ASN1::BER)
ber.get_integer # => 42
Typed payload accessors live in data_types.cr (#get_integer/#set_integer,
#get_object_id/#set_object_id, #get_string, #get_boolean, ...). A
constructed element can be split into / built from sub-elements with
#children / #children=.
Defined in:
bindata/asn1.crbindata/asn1/data_types.cr
bindata/asn1/identifier.cr
bindata/asn1/length.cr
Constant Summary
-
AFTER_DESERIALIZE =
[] of Nil -
BEFORE_SERIALIZE =
[] of Nil -
DIRECT_STRING_TAGS =
{UniversalTags::UTF8String, UniversalTags::CharacterString, UniversalTags::PrintableString, UniversalTags::IA5String, UniversalTags::OctetString, UniversalTags::NumericString, UniversalTags::VisibleString, UniversalTags::GeneralString, UniversalTags::GraphicString} -
String types whose repertoire is ASCII-compatible, so the UTF-8
String.newdecodes them directly. (T61String / VideotexString use the T.61 / videotex character sets, which are neither ASCII nor UTF-8, so they are not decoded here rather than mis-decoded.) -
ENDIAN =
["big"] -
GENERALIZEDTIME_FORMAT =
/\A(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\.\d+)?(Z|[+-]\d{4})\z/ -
YYYYMMDDHHMMSS(.fff)?(Z|±HHMM). -
KLASS_NAME =
[ASN1::BER] -
MAX_OID_ARC_BYTES =
32 -
Largest sub-identifier we decode, in continuation octets. A base-128 arc of this many bytes holds ~224 bits — far beyond any real OID arc (a UUID-based
2.25arc is 128-bit, ~19 bytes). Bounds theBigIntbuild against the super-linear CPU cost of a hostile continuation run. -
PARTS =
[{type: "basic", name: identifier, cls: ASN1::BER::Identifier, onlyif: nil, verify: nil, value: nil}, {type: "basic", name: length, cls: ASN1::BER::Length, onlyif: nil, verify: nil, value: nil}] of Nil -
REMAINING =
[] of Nil -
UTCTIME_FORMAT =
/\A(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})?(Z|[+-]\d{4})\z/ -
YYMMDDHHMM[SS](Z|±HHMM)— the seconds are optional.
Class Method Summary
Instance Method Summary
-
#__format__ : IO::ByteFormat
A
grouporbit_fieldcaptures the endianness at its declaration point, so declaringendianafter one would silently leave it system-endian. -
#bitstring_unused_bits : UInt8
The number of unused (padding) bits in the final data byte (0..7).
-
#children
Parses the payload as a sequence of nested BER elements.
-
#children=(parts)
Encodes parts into the payload and marks this element constructed.
- #constructed
- #constructed=(custom : Bool)
-
#eoc?
Whether this is an end-of-contents marker (
00 00) — a primitive universal EndOfContent element with an empty payload, used to terminate indefinite content. - #extended
- #extended=(parts : Array(ExtendedIdentifier))
- #extended?
-
#get_bit_array : BitArray
The BIT STRING's significant bits as a
BitArray, numbered MSB-first from the first data byte (ASN.1 bit 0 is the high bit of the first byte). -
#get_bitstring : Bytes
Reads the BIT STRING data bytes (dropping the leading unused-bit count).
-
#get_boolean
Reads the payload as a BOOLEAN.
-
#get_bytes : Slice(UInt8)
Returns the raw bytes
-
#get_hexstring(universal = true, tag = UniversalTags::OctetString)
Gets a hex representation of the bytes
-
#get_integer(check_tags = {UniversalTags::Integer, UniversalTags::Enumerated}, check_class = TagClass::Universal) : Int64
Reads the payload as a two's-complement signed INTEGER (or ENUMERATED).
- #get_integer_bytes : Bytes
-
#get_object_id
Returns the object ID in string format.
-
#get_string
Decodes the payload to a
String. -
#get_time : Time
Reads the payload as a UTCTime or GeneralizedTime, normalised to UTC.
-
#identifier : Identifier
Components of a BER object
-
#identifier=(identifier : Identifier)
Components of a BER object
-
#inspect(io : IO) : Nil
Appends a String representation of this object which includes its class name, its object address and the values of all instance variables.
- #length : Length
- #length=(length : Length)
-
#max_depth : Int32
Maximum nesting depth that
#childrenand the indefinite-length reader will descend before raisingMaxDepthExceeded. -
#max_depth=(max_depth : Int32)
Maximum nesting depth that
#childrenand the indefinite-length reader will descend before raisingMaxDepthExceeded. -
#null?
Whether this is a well-formed universal NULL element: primitive, tag
05, empty payload (X.690 §8.8). - #payload : Bytes
- #payload=(payload : Bytes)
-
#read(io : IO) : IO
Reads the fields of this instance from io, in declaration order, and returns io.
-
#sequence?
Whether this is a constructed universal Sequence or Set, i.e.
-
#set_bit_array(bits : BitArray)
Sets a BIT STRING from a
BitArray(numbered MSB-first, as ASN.1 expects). -
#set_bitstring(bytes : Bytes, unused_bits : Int = 0)
Sets a BIT STRING from bytes, with unused_bits (0..7) of padding in the final byte.
-
#set_boolean(value)
Sets a BOOLEAN payload.
-
#set_bytes(data, tag = UniversalTags::OctetString, tag_class = TagClass::Universal)
Sets the raw payload bytes and the given tag.
-
#set_hexstring(string, tag = UniversalTags::OctetString, tag_class = TagClass::Universal)
Sets bytes from a hexstring
-
#set_integer(value, tag = UniversalTags::Integer, tag_class = TagClass::Universal)
Encodes value as a minimal two's-complement INTEGER payload.
-
#set_null
Sets an empty, primitive NULL payload (encodes as
05 00). -
#set_object_id(oid)
Sets a string representing an object ID.
-
#set_string(string, tag = UniversalTags::UTF8String, tag_class = TagClass::Universal)
Sets a string.
-
#set_time(time : Time, tag = UniversalTags::GeneralizedTime)
Encodes time (converted to UTC) as a GeneralizedTime (default) or UTCTime, in the canonical
…Zform. -
#size
The current payload length in bytes.
-
#strict=(strict : Bool)
When set, reject non-canonical (DER) encodings: non-minimal / indefinite length, non-
{00,FF}/ multi-byte BOOLEAN, empty / non-minimal INTEGER, non-minimal OID, embedded-NUL strings, and an out-of-order universal SET OF (tag 17). -
#strict? : Bool
When set, reject non-canonical (DER) encodings: non-minimal / indefinite length, non-
{00,FF}/ multi-byte BOOLEAN, empty / non-minimal INTEGER, non-minimal OID, embedded-NUL strings, and an out-of-order universal SET OF (tag 17). -
#tag
The universal tag as a
UniversalTagsenum. - #tag_class
- #tag_class=(tag : TagClass)
- #tag_number
- #tag_number=(tag_type : Int | UniversalTags)
-
#write(io : IO)
Writes the fields of this instance to io in declaration order.
Instance methods inherited from class BinData
__format__ : IO::ByteFormat
__format__,
max_content_length : Int32
max_content_length,
max_content_length=(max_content_length : Int32)
max_content_length=,
read(io : IO) : IO
read,
to_io(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
to_io,
to_s(io)
to_s,
to_slice
to_slice,
write(io : IO)
write
Class methods inherited from class BinData
bit_fields
bit_fields,
from_io(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
from_io,
from_slice(bytes : Slice, format : IO::ByteFormat = IO::ByteFormat::SystemEndian)
from_slice
Macros inherited from class BinData
__add_enum_field(name, cls, onlyif, verify, value, encoding, enum_type)
__add_enum_field,
__build_methods__
__build_methods__,
after_deserialize(&block)
after_deserialize,
array(name, length, onlyif = nil, verify = nil, value = nil)
array,
before_serialize(&block)
before_serialize,
bit_field(onlyif = nil, verify = nil, endian = nil, &block)
bit_field,
bits(size, name, value = nil, default = nil)
bits,
bool(name, default = false)
bool,
bytes(name, length, onlyif = nil, verify = nil, value = nil, default = nil)
bytes,
custom(name, onlyif = nil, verify = nil, value = nil)
custom,
endian(format)
endian,
enum_bits(size, name)
enum_bits,
enum_field(size, name, onlyif = nil, verify = nil, value = nil)
enum_field,
field(type_declaration, onlyif = nil, verify = nil, value = nil, length = nil, read_next = nil, encoding = nil, endian = nil)
field,
float32(name, onlyif = nil, verify = nil, value = nil, default = nil)
float32,
float32be(name, onlyif = nil, verify = nil, value = nil, default = nil)
float32be,
float32le(name, onlyif = nil, verify = nil, value = nil, default = nil)
float32le,
float64(name, onlyif = nil, verify = nil, value = nil, default = nil)
float64,
float64be(name, onlyif = nil, verify = nil, value = nil, default = nil)
float64be,
float64le(name, onlyif = nil, verify = nil, value = nil, default = nil)
float64le,
group(name, onlyif = nil, verify = nil, value = nil, &block)
group,
int128(name, onlyif = nil, verify = nil, value = nil, default = nil)
int128,
int128be(name, onlyif = nil, verify = nil, value = nil, default = nil)
int128be,
int128le(name, onlyif = nil, verify = nil, value = nil, default = nil)
int128le,
int16(name, onlyif = nil, verify = nil, value = nil, default = nil)
int16,
int16be(name, onlyif = nil, verify = nil, value = nil, default = nil)
int16be,
int16le(name, onlyif = nil, verify = nil, value = nil, default = nil)
int16le,
int32(name, onlyif = nil, verify = nil, value = nil, default = nil)
int32,
int32be(name, onlyif = nil, verify = nil, value = nil, default = nil)
int32be,
int32le(name, onlyif = nil, verify = nil, value = nil, default = nil)
int32le,
int64(name, onlyif = nil, verify = nil, value = nil, default = nil)
int64,
int64be(name, onlyif = nil, verify = nil, value = nil, default = nil)
int64be,
int64le(name, onlyif = nil, verify = nil, value = nil, default = nil)
int64le,
int8(name, onlyif = nil, verify = nil, value = nil, default = nil)
int8,
int8be(name, onlyif = nil, verify = nil, value = nil, default = nil)
int8be,
int8le(name, onlyif = nil, verify = nil, value = nil, default = nil)
int8le,
remaining_bytes(name, onlyif = nil, verify = nil, default = nil)
remaining_bytes,
skip(length, onlyif = nil, verify = nil)
skip,
string(name, onlyif = nil, verify = nil, length = nil, value = nil, encoding = nil, default = nil)
string,
uint128(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint128,
uint128be(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint128be,
uint128le(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint128le,
uint16(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint16,
uint16be(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint16be,
uint16le(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint16le,
uint32(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint32,
uint32be(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint32be,
uint32le(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint32le,
uint64(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint64,
uint64be(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint64be,
uint64le(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint64le,
uint8(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint8,
uint8be(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint8be,
uint8le(name, onlyif = nil, verify = nil, value = nil, default = nil)
uint8le,
variable_array(name, read_next, onlyif = nil, verify = nil, value = nil)
variable_array
Class Method Detail
Instance Method Detail
A group or bit_field captures the endianness at its declaration point, so
declaring endian after one would silently leave it system-endian. Fail loudly.
The number of unused (padding) bits in the final data byte (0..7).
Parses the payload as a sequence of nested BER elements. The
max_content_length and #max_depth caps propagate to each child.
Only valid for a constructed element; on a primitive the payload is raw
content, not a TLV list, so parsing it would yield garbage. Raises
ASN1::Error in that case.
NOTE this re-parses @payload on every call and returns a fresh array of
freshly-decoded children — it is not memoized (the payload is mutable, so a
cache would risk going stale). Bind the result to a local if you access the
children repeatedly on the same element.
Encodes parts into the payload and marks this element constructed. The
tag class/number are left untouched — set them yourself (e.g. to a universal
Sequence/Set, or a constructed context tag) so #sequence? reflects the
intended type; this accessor only guarantees the #constructed flag.
Whether this is an end-of-contents marker (00 00) — a primitive universal
EndOfContent element with an empty payload, used to terminate indefinite
content.
The BIT STRING's significant bits as a BitArray, numbered MSB-first from
the first data byte (ASN.1 bit 0 is the high bit of the first byte).
Reads the BIT STRING data bytes (dropping the leading unused-bit count). The
final byte's low #bitstring_unused_bits bits are padding. Any unused count
0..7 is accepted; use #get_bit_array for the exact significant bits.
Gets a hex representation of the bytes
Reads the payload as a two's-complement signed INTEGER (or ENUMERATED).
With the default universal check_class the tag is validated against
check_tags. With a non-universal check_class (e.g. an SNMP context-tagged
Counter/Gauge) only the class is checked — the universal-tag check is skipped,
since check_tags are universal tags and don't apply to a context tag.
Returns the object ID in string format.
Sub-identifiers are decoded as big-endian base-128 numbers per X.690 §8.19
(every octet but the last has its high bit set), so arcs of any size are
supported. BigInt is used because OID arcs are unbounded (e.g. UUID-based
OIDs under 2.25, ITU-T X.667).
Decodes the payload to a String. BMPString is transcoded from UTF-16BE and
UniversalString from UTF-32BE (leniently — surrogate pairs are accepted, not
strict UCS-2/UCS-4); the ASCII-repertoire types are read directly. An
incomplete/malformed byte sequence raises ASN1::InvalidPayload; individual
invalid code points may be substituted by the platform transcoder.
Reads the payload as a UTCTime or GeneralizedTime, normalised to UTC.
A time zone is required (Z or a numeric ±HHMM offset); a bare local time
is rejected. UTCTime's two-digit year uses the RFC 5280 pivot (>= 50 =>
19xx, < 50 => 20xx).
Appends a String representation of this object which includes its class name, its object address and the values of all instance variables.
class Person
def initialize(@name : String, @age : Int32)
end
end
Person.new("John", 32).inspect # => #<Person:0x10fd31f20 @name="John", @age=32>
Maximum nesting depth that #children and the indefinite-length reader will
descend before raising MaxDepthExceeded. The default (100) guards both a
recursive consumer #children walk and the eager recursion of #read over
nested indefinite-length elements against stack overflow (a few KB of
30 80 … / 24 80 … encodes thousands of levels); 0 disables the limit
(and with it the indefinite-read recursion bound). Propagated to each child
alongside max_content_length.
Maximum nesting depth that #children and the indefinite-length reader will
descend before raising MaxDepthExceeded. The default (100) guards both a
recursive consumer #children walk and the eager recursion of #read over
nested indefinite-length elements against stack overflow (a few KB of
30 80 … / 24 80 … encodes thousands of levels); 0 disables the limit
(and with it the indefinite-read recursion bound). Propagated to each child
alongside max_content_length.
Whether this is a well-formed universal NULL element: primitive, tag 05,
empty payload (X.690 §8.8).
Reads the fields of this instance from io, in declaration order, and
returns io. Raises BinData::ParseError (or BinData::VerificationException)
on malformed input.
Whether this is a constructed universal Sequence or Set, i.e. an element
whose payload is itself a list of BER elements (see #children).
Sets a BIT STRING from a BitArray (numbered MSB-first, as ASN.1 expects).
Sets a BIT STRING from bytes, with unused_bits (0..7) of padding in the final byte.
Sets the raw payload bytes and the given tag.
Sets bytes from a hexstring
Encodes value as a minimal two's-complement INTEGER payload. ameba:disable Metrics/CyclomaticComplexity
Sets a string representing an object ID.
Each arc is encoded as a big-endian base-128 number per X.690 §8.19. Arcs
are parsed as BigInt, so arbitrarily large values are supported.
Sets a string. BMPString is encoded to UTF-16BE and UniversalString to
UTF-32BE; every other type stores the string's UTF-8 bytes. tag may be a
UniversalTags or its integer value.
Encodes time (converted to UTC) as a GeneralizedTime (default) or UTCTime,
in the canonical …Z form. UTCTime can only represent years 1950..2049.
Sub-second precision is dropped (DER forbids fractional seconds), so a Time
with a fractional part does not round-trip exactly through #get_time.
The current payload length in bytes. Reads from the payload itself (not the
decoded Length, which is only refreshed on #write), so it is correct for
in-memory-built objects and for indefinite-length elements too.
When set, reject non-canonical (DER) encodings: non-minimal / indefinite
length, non-{00,FF} / multi-byte BOOLEAN, empty / non-minimal INTEGER,
non-minimal OID, embedded-NUL strings, and an out-of-order universal
SET OF (tag 17). Default false keeps the BER-permissive behaviour. Set it
before reading (the length checks fire during #read), and it propagates to
#children.
Not (yet) covered: an implicitly context-tagged SET OF (indistinguishable from a SEQUENCE without a schema), GeneralizedTime/UTCTime canonical form, BIT STRING padding bits, and the primitive-vs-constructed rule for strings.
When set, reject non-canonical (DER) encodings: non-minimal / indefinite
length, non-{00,FF} / multi-byte BOOLEAN, empty / non-minimal INTEGER,
non-minimal OID, embedded-NUL strings, and an out-of-order universal
SET OF (tag 17). Default false keeps the BER-permissive behaviour. Set it
before reading (the length checks fire during #read), and it propagates to
#children.
Not (yet) covered: an implicitly context-tagged SET OF (indistinguishable from a SEQUENCE without a schema), GeneralizedTime/UTCTime canonical form, BIT STRING padding bits, and the primitive-vs-constructed rule for strings.
The universal tag as a UniversalTags enum. Raises unless this is a
universal-class element.
Writes the fields of this instance to io in declaration order. Raises
BinData::WriteError (or BinData::VerificationException) on failure.