x690
x690 is a pure Python library that implements the X.690 standard for Basic Encoding Rules (BER) encoding and decoding of ASN.1 data structures. It provides functionalities to encode Python objects into BER-encoded bytes and decode BER-encoded bytes back into Python objects. The current version is 1.0.0.post1, with the last update in September 2022, suggesting a stable but infrequently updated codebase.
Common errors
-
x690.exc.IncompleteDecoding: Strict decoding still had X remaining bytes!
cause This error occurs when `x690.decode` is called with `strict=True`, but the input `bytes` object contains more BER-encoded data after the first object has been successfully decoded. It indicates that the entire byte stream was not consumed by a single decoding operation.fixIf you expect multiple objects, remove `strict=True` and loop through the data, passing the `next_offset` returned by `decode` to subsequent calls. If you genuinely expect only one object and this is an error, check your input data for unexpected trailing bytes. -
x690.exc.UnexpectedType: Expected type 'Integer' but got 'OctetString'
cause This error is raised when `x690.decode` is called with the `expected_type` argument, and the actual BER-encoded object's type identifier (tag) does not match the provided `expected_type`.fixVerify that the `expected_type` passed to `decode` accurately reflects the type of the BER object you are trying to parse. If the type is unknown or can vary, omit `expected_type` and check the type of the returned object dynamically. -
AttributeError: 'UnknownType' object has no attribute 'my_custom_field'
cause This typically happens when you define a custom X.690 type (by subclassing `x690.types.Type`) but fail to import the module where it's defined before attempting to decode data containing that type. The library defaults to `UnknownType` if it can't find a registered class for the tag.fixEnsure that the Python module where your custom `x690.types.Type` subclass is defined is imported somewhere in your application's startup code. This allows the class to be registered with the `x690` library.
Warnings
- gotcha When decoding, the `decode` function returns a tuple: `(decoded_object, next_offset)`. It does not automatically consume all bytes if there are multiple BER-encoded objects concatenated. You must loop and pass `next_offset` to decode subsequent objects.
- breaking The library's last update was in September 2022. While functional for X.690 BER, it may not actively incorporate new Python language features or security patches. Users should be aware of this if integrating into highly active or security-sensitive projects.
- gotcha To support custom X.690 types not defined in the standard, you must subclass `x690.types.Type` and define `TYPECLASS`, `NATURE`, and `TAG`. Crucially, this custom type *must be imported* somewhere in your application code for the library to automatically register it for decoding. If not imported, it will be treated as an `UnknownType`.
Install
-
pip install x690
Imports
- decode
from x690 import decode
- types
import x690.types as t
- exc
from x690 import exc
- Type
from x690.types import Type
Quickstart
import x690.types as t
from x690 import decode
# 1. Encoding a value
my_integer = t.Integer(123)
encoded_bytes = bytes(my_integer)
print(f"Encoded Integer: {encoded_bytes.hex()}")
# 2. Encoding a composite value (Sequence)
my_sequence = t.Sequence([t.Integer(1), t.OctetString(b'hello')])
encoded_sequence_bytes = bytes(my_sequence)
print(f"Encoded Sequence: {encoded_sequence_bytes.hex()}")
# 3. Decoding bytes
decoded_value, next_offset = decode(encoded_bytes)
print(f"Decoded value: {decoded_value.pyvalue} (Type: {type(decoded_value).__name__})")
# 4. Decoding a sequence (multiple values)
data_to_decode = b'0\x06\x02\x01\x01\x04\x01a'
decoded_obj_1, offset_1 = decode(data_to_decode)
decoded_obj_2, offset_2 = decode(data_to_decode, offset_1)
print(f"Decoded obj 1: {decoded_obj_1.pretty()}")
print(f"Decoded obj 2: {decoded_obj_2.pretty()}")
# 5. Decoding with an expected type (optional but good for type checking)
# This helps ensure the decoded type matches expectations and aids type checkers.
from x690.exc import UnexpectedType
try:
expected_int_value, _ = decode(encoded_bytes, expected_type=t.Integer)
print(f"Decoded with expected type: {expected_int_value.pyvalue}")
# This would raise UnexpectedType if encoded_bytes was not an Integer
# decode(encoded_bytes, expected_type=t.OctetString)
except UnexpectedType as e:
print(f"Error decoding with unexpected type: {e}")