x690 package¶
Submodules¶
x690.exc module¶
This module contains exceptions for the x.690 protocol
-
exception
x690.exc.
IncompleteDecoding
(message: str, remainder: bytes)¶ Bases:
x690.exc.X690Error
Raised when decoding did not consume all bytes.
The junk bytes are stored in the “remainder” attribute
-
exception
x690.exc.
UnexpectedType
¶ Bases:
x690.exc.X690Error
Raised when decoding resulted in an unexpected type.
-
exception
x690.exc.
X690Error
¶ Bases:
Exception
Top-Level exception for everything related to the X690 protocol
x690.types module¶
Overview¶
This module contains the encoding/decoding logic for data types as defined in X.690.
Each type is made available via a registry dictionary on X690Type
and
can be retrieved via get()
.
Additionally, given a bytes
object, the decode()
function can be used to parse the bytes object and return a typed instance
from it. See decode()
for details about it’s behaviour!
Note
The individual type classes in this module do not contain any additional
documentation. The bulk of this module is documented in X690Type
.
For the rest, the type classes simply define the type identifier tag.
Supporting Additional Classes¶
Just by subclassing X690Type
and setting correct TAG
and
TYPECLASS
values, most of the basic functionality will be covered by the
superclass. X690Type detection, and addition to the registry is automatic.
Subclassing is enough.
By default, a new type which does not override any methods will have it’s value
reported as bytes objects. You may want to override at least
decode_raw()
to convert the raw-bytes into your own data-type.
Example¶
Let’s assume you want to decode/encode a “Person” object with a first-name, last-name and age. Let’s also assume it will be an application-specific type of a “constructed” nature with our application-local tag 1. Let’s further assume that the value will be a UTF-8 encoded JSON string inside the x690 stream.
We specify the metadata as class-level variables TYPECLASS
, NATURE
and
TAG
. The decoding is handled by implementing a static-method
decode_raw
which gets the data-object containing the value and a slice
defining at which position the data is located. The encoding is handled by
implementing the instance-method encode_raw
. The instance contains the
Python value in self.pyvalue
.
So we can implement this as follows (including a named-tuple as our local type):
from typing import NamedTuple
from x690.types import X690Type
from json import loads, dumps
class Person(NamedTuple):
first_name: str
last_name: str
age: int
class PersonType(X690Type[Person]):
TYPECLASS = TypeClass.APPLICATION
NATURE = [TypeNature.CONSTRUCTED]
TAG = 1
@staticmethod
def decode_raw(data: bytes, slc: slice = slice(None)) -> Person:
values = loads(data[slc].decode("utf8"))
return Person(
values["first_name"], values["last_name"], values["age"]
)
def encode_raw(self) -> bytes:
return dumps(self.pyvalue._asdict()).encode("utf8")
-
class
x690.types.
BitString
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 3¶
-
pyvalue
¶
-
-
class
x690.types.
BmpString
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 30¶
-
pyvalue
¶
-
-
class
x690.types.
Boolean
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>]¶
-
TAG
= 1¶
-
static
decode_raw
(data: bytes, slc: slice = slice(None, None, None)) → bool¶ Converts the raw byte-value (without type & length header) into a pure Python type
Overrides
decode_raw()
-
encode_raw
() → bytes¶ Overrides
X690Type.encode_raw()
-
pyvalue
¶
-
classmethod
validate
(data: bytes) → None¶ Overrides
X690Type.validate()
-
-
class
x690.types.
CharacterString
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
TAG
= 29¶
-
pyvalue
¶
-
-
class
x690.types.
EOC
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>]¶
-
TAG
= 0¶
-
pyvalue
¶
-
-
class
x690.types.
EmbeddedPdv
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
TAG
= 11¶
-
pyvalue
¶
-
-
class
x690.types.
Enumerated
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>]¶
-
TAG
= 10¶
-
pyvalue
¶
-
-
class
x690.types.
External
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
TAG
= 8¶
-
pyvalue
¶
-
-
class
x690.types.
GeneralString
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 27¶
-
pyvalue
¶
-
-
class
x690.types.
GeneralizedTime
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 24¶
-
pyvalue
¶
-
-
class
x690.types.
GraphicString
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 25¶
-
static
decode_raw
(data: bytes, slc: slice = slice(None, None, None)) → str¶ Converts the raw byte-value (without type & length header) into a pure Python type
Overrides
decode_raw()
-
pyvalue
¶
-
-
class
x690.types.
IA5String
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 22¶
-
pyvalue
¶
-
-
class
x690.types.
Integer
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>]¶
-
SIGNED
= True¶
-
TAG
= 2¶
-
classmethod
decode_raw
(data: bytes, slc: slice = slice(None, None, None)) → int¶ Converts the raw byte-value (without type & length header) into a pure Python type
Overrides
decode_raw()
-
encode_raw
() → bytes¶ Overrides
X690Type.encode_raw()
-
pyvalue
¶
-
-
class
x690.types.
Null
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>]¶
-
TAG
= 5¶
-
static
decode_raw
(data: bytes, slc: slice = slice(None, None, None)) → None¶ Converts the raw byte-value (without type & length header) into a pure Python type
Overrides
decode_raw()
-
encode_raw
() → bytes¶ Overrides
X690Type.encode_raw()
>>> Null().encode_raw() b'\x00'
-
pyvalue
¶
-
classmethod
validate
(data: bytes) → None¶ Overrides
X690Type.validate()
-
-
class
x690.types.
NumericString
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 18¶
-
pyvalue
¶
-
-
class
x690.types.
ObjectDescriptor
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 7¶
-
pyvalue
¶
-
-
class
x690.types.
ObjectIdentifier
(value: Union[str, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
Represents an OID.
Instances of this class support containment checks to determine if one OID is a sub-item of another:
>>> ObjectIdentifier("1.2.3.4.5") in ObjectIdentifier("1.2.3") True >>> ObjectIdentifier("1.2.4.5.6") in ObjectIdentifier("1.2.3") False
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>]¶
-
TAG
= 6¶
-
childof
(other: x690.types.ObjectIdentifier) → bool¶ Convenience method to check whether this OID is a child of another OID
-
collapse_identifiers
()¶ Meld the first two octets into one octet as defined by x.690
In x.690 ObjectIdentifiers are a sequence of numbers. In the byte-representation the first two of those numbers are stored in the first byte.
This function takes a “human-readable” OID tuple and returns a new tuple with the first two elements merged (collapsed) together.
>>> ObjectIdentifier("1.3.6.1.4.1").collapse_identifiers() (43, 6, 1, 4, 1) >>> ObjectIdentifier().collapse_identifiers() ()
-
static
decode_large_value
()¶ If we encounter a value larger than 127, we have to consume from the stram until we encounter a value below 127 and recombine them.
See: https://msdn.microsoft.com/en-us/library/bb540809(v=vs.85).aspx
-
static
decode_raw
(data: bytes, slc: slice = slice(None, None, None)) → str¶ Converts the raw byte-value (without type & length header) into a pure Python type
Overrides
decode_raw()
-
static
encode_large_value
()¶ Inverse function of
decode_large_value()
-
encode_raw
() → bytes¶ Overrides
X690Type.encode_raw()
-
property
nodes
¶ Returns the numerical nodes for this instance as tuple
>>> ObjectIdentifier("1.2.3").nodes (1, 2, 3) >>> ObjectIdentifier().nodes ()
-
parentof
(other: x690.types.ObjectIdentifier) → bool¶ Convenience method to check whether this OID is a parent of another OID
-
pyvalue
¶
-
-
class
x690.types.
OctetString
(value: Union[str, bytes, x690.types._SENTINEL_UNINITIALISED] = b'')¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 4¶
-
pretty
(depth: int = 0) → str¶ Returns a prettified string with depth levels of indentation
See
pretty()
-
pyvalue
¶
-
-
class
x690.types.
PrintableString
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 19¶
-
pyvalue
¶
-
-
class
x690.types.
Real
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>]¶
-
TAG
= 9¶
-
pyvalue
¶
-
-
class
x690.types.
RelativeOid
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>]¶
-
TAG
= 13¶
-
pyvalue
¶
-
-
class
x690.types.
Sequence
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
Represents an X.690 sequence type. Instances of this class are iterable and indexable.
-
TAG
= 16¶
-
static
decode_raw
()¶ Converts the raw byte-value (without type & length header) into a pure Python type
Overrides
decode_raw()
-
encode_raw
() → bytes¶ Overrides
X690Type.encode_raw()
-
pretty
(depth: int = 0) → str¶ Returns a prettified string with depth levels of indentation
See
pretty()
-
pythonize
()¶ Overrides
pythonize()
-
pyvalue
¶
-
-
class
x690.types.
Set
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
TAG
= 17¶
-
pyvalue
¶
-
-
class
x690.types.
T61String
(value: Union[str, bytes] = '')¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 20¶
-
static
decode_raw
(data: bytes, slc: slice = slice(None, None, None)) → str¶ Converts the raw byte-value (without type & length header) into a pure Python type
Overrides
decode_raw()
-
encode_raw
() → bytes¶ Overrides
X690Type.encode_raw()
-
pyvalue
¶
-
-
x690.types.
UNINITIALISED
= <x690.types._SENTINEL_UNINITIALISED object>¶ sentinel value for uninitialised objects (used for lazy decoding)
-
class
x690.types.
UniversalString
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 28¶
-
pyvalue
¶
-
-
class
x690.types.
UnknownType
(value: bytes = b'', tag: int = -1)¶ Bases:
x690.types.X690Type
A fallback type for anything not in X.690.
Instances of this class contain the raw information as parsed from the bytes as the following attributes:
value
: The value without leading metadata (as bytes value)tag
: The unparsed “tag”. This is the type ID as defined in the reference document. SeeTypeInfo
for details.typeinfo
: unused (derived from tag and only here for consistency with__repr__
of this class).
-
TAG
= 153¶
-
pretty
(depth: int = 0) → str¶ Returns a prettified string with depth levels of indentation
See
pretty()
-
pyvalue
¶
-
class
x690.types.
UtcTime
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 23¶
-
pyvalue
¶
-
-
class
x690.types.
Utf8String
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 12¶
-
pyvalue
¶
-
-
class
x690.types.
VideotexString
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 21¶
-
pyvalue
¶
-
-
class
x690.types.
VisibleString
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
x690.types.X690Type
-
NATURE
= [<TypeNature.PRIMITIVE: 'primitive'>, <TypeNature.CONSTRUCTED: 'constructed'>]¶
-
TAG
= 26¶
-
pyvalue
¶
-
-
class
x690.types.
X690Type
(value: Union[TWrappedPyType, x690.types._SENTINEL_UNINITIALISED] = <x690.types._SENTINEL_UNINITIALISED object>)¶ Bases:
typing.Generic
The superclass for all supported types.
-
NATURE
= [<TypeNature.CONSTRUCTED: 'constructed'>]¶ The x690 “private/constructed” information
-
TAG
= -1¶ The x690 identifier for the type
-
TYPECLASS
= 'universal'¶ The x690 type-class (universal, application or context)
-
static
all
()¶ Returns all registered classes
-
bounds
= slice(None, None, None)¶ The location of the value within “raw_bytes”
-
classmethod
decode
(data: bytes) → TConcreteType¶ This method takes a bytes object which contains the raw content octets of the object. That means, the octets without the type information and length.
This function must be overridden by the concrete subclasses.
-
static
decode_raw
(data: bytes, slc: slice = slice(None, None, None)) → TWrappedPyType¶ Converts the raw byte-value (without type & length header) into a pure Python type
>>> Integer.decode_raw(b"\x05") 5
- Parameters
data – A data-block containing the byte-information
slc – A slice of the data-block that contains the exact raw-bytes.
- Returns
The value that should be wrapped by the current x690 type.
-
encode_raw
() → bytes¶ Convert this instance into raw x690 bytes (excluding the type and length header)
>>> import x690.types as t >>> Integer(5).encode_raw() b'\x05' >>> Boolean(True).encode_raw() b'\x01' >>> X690Type(t.UNINITIALISED).encode_raw() b''
-
classmethod
from_bytes
(data: bytes, slc: slice = slice(None, None, None)) → TConcreteType¶ Creates a new
x690.types.X690Type
instance from raw-bytes (without type nor length bytes)>>> Integer.from_bytes(b"\x01") Integer(1) >>> OctetString.from_bytes(b"hello-world") OctetString(b'hello-world') >>> Boolean.from_bytes(b"\x00") Boolean(False)
-
static
get
()¶ Retrieve a Python class by x690 type information
Classes can be registered by subclassing
x690.types.X690Type
-
property
length
¶ Return the x690 byte-length of this instance
-
pretty
(depth: int = 0) → str¶ Returns a readable representation (possibly multiline) of the value.
The value is indented by depth levels of indentation
By default this simply returns the string representation. But more complex values may override this.
-
pythonize
() → TWrappedPyType¶ Convert this instance to an appropriate pure Python object.
-
pyvalue
¶ The decoded (or to-be encoded) Python value
-
property
raw_bytes
¶
-
classmethod
validate
(data: bytes) → None¶ Given a bytes object, checks if the given class cls supports decoding this object. If not, raises a ValueError.
-
property
value
¶ Returns the value as a pure Python type
-
-
x690.types.
decode
()¶ Convert a X.690 bytes object into a Python instance, and the location of the next object.
Given a
bytes
object and any start-index, inspects and parses the octets starting at the given index (as many as required) to determine variable type (and corresponding Python class), and length. That class is then used to parse the object located indata
at the given index. The location of the start of the next (subsequent) object is also determined.The return value is a tuple with the decoded object and the start-index of the next object.
Example:
>>> data = b'\x02\x01\x05\x11' >>> decode(data) (Integer(5), 3) >>> data = b'some-skippable-bytes\x02\x01\x05\x11' >>> decode(data, 20) (Integer(5), 23)
x690.util module¶
Utility functions for working with the X.690 and related standards.
-
x690.util.
INDENT_STRING
= ' '¶ String to be used for indenting nested items during “pretty()” calls
-
class
x690.util.
Length
¶ Bases:
str
,enum.Enum
A simple “namespace” to avoid magic values for indefinite lengths.
-
INDEFINITE
= 'indefinite'¶
-
-
class
x690.util.
LengthInfo
(length, offset)¶ Bases:
tuple
-
length
¶ Alias for field number 0
-
offset
¶ Alias for field number 1
-
-
class
x690.util.
TypeClass
¶ Bases:
str
,enum.Enum
An enumeration.
-
APPLICATION
= 'application'¶
-
CONTEXT
= 'context'¶
-
PRIVATE
= 'private'¶
-
UNIVERSAL
= 'universal'¶
-
-
class
x690.util.
TypeInfo
(cls: x690.util.TypeClass, nature: x690.util.TypeNature, tag: int)¶ Bases:
object
Decoded structure for an X.690 “type” octet. Example:
>>> TypeInfo.from_bytes(b'\x30') TypeInfo(cls=<TypeClass.UNIVERSAL: 'universal'>, nature=<TypeNature.CONSTRUCTED: 'constructed'>, tag=16)
The structure contains 3 fields:
- cls
The typeclass (a value taken from
TypeClass
)- nature
A value taken from :py:`~.TypeNature`
- tag
The actual type identifier.
The instance also keeps the raw value as it was seen in the
_raw_value
attribute.-
static
from_bytes
(data)¶ Given one octet, extract the separate fields and return a TypeInfo instance:
>>> TypeInfo.from_bytes(b'\x30') TypeInfo(cls=<TypeClass.UNIVERSAL: 'universal'>, nature=<TypeNature.CONSTRUCTED: 'constructed'>, tag=16)
-
class
x690.util.
TypeNature
¶ Bases:
str
,enum.Enum
An enumeration.
-
CONSTRUCTED
= 'constructed'¶
-
PRIMITIVE
= 'primitive'¶
-
-
class
x690.util.
ValueMetaData
(bounds, next_value_index)¶ Bases:
tuple
-
bounds
¶ Alias for field number 0
-
next_value_index
¶ Alias for field number 1
-
-
x690.util.
decode_length
(data, index=0)¶ Given a bytes object, which starts with the length information of a TLV value, returns a namedtuple with the length and the number of bytes which contained the length information. So, given a TLV value, this function takes the “LV” part as input, parses the length information and returns the length plus the number of bytes which need to be skipped to arrive at the beginning of the value.
For values which are longer than 127 bytes, the length must be encoded into an unknown amount of “length” bytes. This function reads as many bytes as needed for the length. Consuming bytes for the value must therefore start after the bytes containing the length info. The second value returned from this function includes this information.
The second argument (index) tells the function at which position to look for the length information.
Examples:
>>> # length > 127, consume multiple length bytes >>> decode_length(b'\x81\xc8...') LengthInfo(length=200, offset=2) >>> # length <= 127, consume one length byte >>> decode_length(b'\x10...') LengthInfo(length=16, offset=1)
- TODO: Upon rereading this, I wonder if it would not make more sense to take
the complete TLV content as input.
-
x690.util.
encode_length
(value)¶ This function encodes the length of a variable into bytes conforming to the rules defined in X.690: The “length” field must be specially encoded for values above 127. Additionally, from X.690:
8.1.3.2 A sender shall:
use the definite form (see 8.1.3.3) if the encoding is primitive;
use either the definite form (see 8.1.3.3) or the indefinite form (see 8.1.3.6), a sender’s option, if the encoding is constructed and all immediately available;
use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all immediately available.
See also: https://en.wikipedia.org/wiki/X.690#Length_octets
Example:
>>> encode_length(16) # no need for special encoding. b'\x10' >>> encode_length(200) # > 127, needs to be specially encoded. b'\x81\xc8'
-
x690.util.
get_value_slice
(data: bytes, index: int = 0) → x690.util.ValueMetaData¶ Helper method to extract lightweight information about value locations in a data-stream.
The function returns both a slice at which a value can be found, and the index at which the next value can be found.
-
x690.util.
visible_octets
(data)¶ Returns a geek-friendly (hexdump) output of a bytes object.
- Developer note:
This is not super performant. But it’s not something that’s supposed to be run during normal operations (mostly for testing and debugging). So performance should not be an issue, and this is less obfuscated than existing solutions.
Example:
>>> from os import urandom >>> print(visible_octets(urandom(40))) 99 1f 56 a9 25 50 f7 9b 95 7e ff 80 16 14 88 c5 ..V.%P...~...... f3 b4 83 d4 89 b2 34 b4 71 4e 5a 69 aa 9f 1d f8 ......4.qNZi.... 1d 33 f9 8e f1 b9 12 e9 .3......
-
x690.util.
wrap
(text: str, header: str, depth: int) → str¶ Wraps text in a border with a header and indented by indent.
Module contents¶
x690 is a library allowing to decode and encode values according to the ITU X.690 standard.
For decoding, use the function decode provided by this module. When used as-is on a bytes object, it will return the first decoded value in the blob and the position of the next value:
>>> from x690 import decode
>>> data, next_index = decode(b"")
>>> data
Integer(1)
>>> next_index
3
For a stream of data with an unknown number of items, you can use the next-index to move over the data:
>>> from x690 import decode
>>> data = b"\x02\x01\x01\x02\x01\x02\x02\x01\x03"
>>> item, next_index = decode(data)
>>> item, next_index
(Integer(1), 3)
>>> while next_index < len(data):
... item, next_index = decode(data, next_index)
... item, next_index
(Integer(2), 6)
(Integer(3), 9)
If you expect exactly one element, it is possible to pass the “strict” argument. This will raise an error if the stream contains any “junk” data.
Finally, a x690 data-stream may contain various types. For this reason, the “decode” function is type-hinted to return a vague, imprecise type. If you know what your are decoding, you can pass the expected type into the function:
>>> from x690 import decode
>>> from x690.types import Integer
>>> decode(b"", enforce_type=Integer)
(Integer(1), 3)
This will do two things:
It runs a type-check upon decoding and will raise a
x690.exc.UnexpectedType
error if it does not match.Inform the type-checker of the return-type, improving type-checker output.
-
x690.
decode
()¶ Convert a X.690 bytes object into a Python instance, and the location of the next object.
Given a
bytes
object and any start-index, inspects and parses the octets starting at the given index (as many as required) to determine variable type (and corresponding Python class), and length. That class is then used to parse the object located indata
at the given index. The location of the start of the next (subsequent) object is also determined.The return value is a tuple with the decoded object and the start-index of the next object.
Example:
>>> data = b'\x02\x01\x05\x11' >>> decode(data) (Integer(5), 3) >>> data = b'some-skippable-bytes\x02\x01\x05\x11' >>> decode(data, 20) (Integer(5), 23)