asnkit
This is the documentation of the Python runtime and of the formats of the
files. For the repository as a whole (the runtimes in Python and in Rust,
the compiler, the plugins, the codecs, the commands and the known
limitations) see the README.
The Rust crates are documented by cargo doc. ASN.1 is compiled to .asni
files by the compiler asnkitcc, which has no Python API.
- File formats: the
.asniand.asnstfiles. - Guidelines for third-party runtimes.
- Plugins: how they are run, and the two there are.
- Playground: compile ASN.1 and encode a value with every codec, in the browser, with the compiler and the runtime as WebAssembly.
Basic usage
asnkitrt.load_asni_file(filename, codec='ber', numeric_enums=False, nonce=None, accept_any_nonce=False, bound_to=None, accept_any_binding=False, verification_keys=None, accept_any_signature=False, decryption_keys=None, lenient=False)
Same as load(), but for the .asni file with given
name.
>>> foo = asnkitrt.runtime.load_asni_file('foo.asni')
asnkitrt.load_asni(data, codec='ber', numeric_enums=False, nonce=None, accept_any_nonce=False, bound_to=None, accept_any_binding=False, verification_keys=None, accept_any_signature=False, decryption_keys=None, lenient=False)
Same as load(), but for given contents of an .asni
file only.
>>> foo = asnkitrt.runtime.load_asni(data)
asnkitrt.runtime.Specification
Bases: object
This class is used to encode and decode ASN.1 types found in an ASN.1 specification.
Instances of this class are created by the functions load(),
load_asni() and load_asni_file().
_decode_length = decode_length
instance-attribute
_modules = modules
instance-attribute
_types = {}
instance-attribute
modules
property
A dictionary of all modules in the specification. Unlike
types, this attribute contains every type, even if
the type name was found in two or more modules.
>>> question = foo.modules['Foo']['Question']
>>> question
Sequence(Question, [Integer(id), IA5String(question)])
>>> question.encode({'id': 1, 'question': 'Is 1+1=3?'})
b'0\x0e\x02\x01\x01\x16\x09Is 1+1=3?'
types
property
A dictionary of all unique types in the specification. Types found in two or more modules are not part of this dictionary.
>>> question = foo.types['Question']
>>> question
Sequence(Question, [Integer(id), IA5String(question)])
>>> question.encode({'id': 1, 'question': 'Is 1+1=3?'})
b'0\x0e\x02\x01\x01\x16\x09Is 1+1=3?'
__init__(modules, decode_length, type_checkers, constraints_checkers)
decode(name, data, check_constraints=False)
Decode given bytes object data as given type name and return
the decoded data as a dictionary.
If check_constraints is True all objects in data are
checked against their ASN.1 type constraints. A
ConstraintsError exception is raised if the constraints are
not fulfilled. Set check_constraints to False to skip
the constraints check and minimize the runtime overhead, but
instead allow decoding of values not fulfilling the
constraints.
>>> foo.decode('Question', b'0\x0e\x02\x01\x01\x16\x09Is 1+1=3?')
{'id': 1, 'question': 'Is 1+1=3?'}
decode_length(data)
Decode the length of given data data. Returns None if not enough
data was given to decode the length.
This method only works for BER and DER codecs with definite length in the first data encoding. Other codecs and combinations lacks length information in the data.
>>> foo.decode_length(b'\x30\x0e\x02\x01\x01')
16
decode_with_length(name, data, check_constraints=False)
Same as decode, but also
returns the byte length of the decoded data.
Use to get the length of indefinite-length BER encoded data.
>>> foo.decode_with_length('Question',
b'0\x0e\x02\x01\x01\x16\x09Is 1+1=3?')
({'id': 1, 'question': 'Is 1+1=3?'}, 16)
encode(name, data, check_types=True, check_constraints=False, **kwargs)
Encode given dictionary data as given type name and return the
encoded data as a bytes object.
If check_types is True all objects in data are checked
against the expected Python type for its ASN.1 type. Set
check_types to False to minimize the runtime overhead,
but instead get less informative error messages.
See Types (in the documentation) for a mapping table from ASN.1 types to Python types.
If check_constraints is True all objects in data are
checked against their ASN.1 type constraints. A
ConstraintsError exception is raised if the constraints are
not fulfilled. Set check_constraints to False to skip
the constraints check and minimize the runtime overhead, but
instead get less informative error messages and allow encoding
of values not fulfilling the constraints.
>>> foo.encode('Question', {'id': 1, 'question': 'Is 1+1=3?'})
b'0\x0e\x02\x01\x01\x16\x09Is 1+1=3?'
Types
ASN.1 types are mapped to Python types as shown in the table below.
| ASN.1 type | Python type | Example |
|---|---|---|
| BOOLEAN | bool |
True |
| INTEGER | int (2) |
87 |
| REAL | float |
33.12 |
| NULL | -- | None |
| BIT STRING | tuple(bytes, int) |
(b'\x50', 4) |
| OCTET STRING | bytes |
b'\x44\x1e\xff' |
| OBJECT IDENTIFIER | str |
'1.33.2' |
| ENUMERATED | str or int (1) |
'one' or 1 |
| SEQUENCE | dict |
{'a': 52, 'b': 1} |
| SEQUENCE OF | list |
[1, 3] |
| SET | dict |
{'foo': 'bar'} |
| SET OF | list |
[3, 0, 7] |
| CHOICE | tuple(str, object) |
('a', 5) |
| UTF8String | str |
'hello' |
| NumericString | str |
'234359' |
| PrintableString | str |
'goo' |
| IA5String | str |
'name' |
| VisibleString | str |
'gle' |
| GeneralString | str |
'abc' |
| BMPString | str |
'ko' |
| GraphicString | str |
'a b' |
| TeletexString | str |
'ßø' |
| UniversalString | str |
'åäö' |
| UTCTime | datetime.datetime |
datetime(2018, 6, 11, 11, 4, 59) |
| GeneralizedTime | datetime.datetime |
datetime(2018, 1, 31, 5, 0, 47) |
| DATE | datetime.date |
date(1985, 4, 12) |
| TIME-OF-DAY | datetime.time |
time(15, 27, 46) |
| DATE-TIME | datetime.datetime |
datetime(1985, 4, 12, 15, 27, 30) |
| DURATION | str |
'P1Y2M3DT4H5M6S' |
| ObjectDescriptor | -- | -- |
-
Compile with
numeric_enums=Truefor numeric enumeration values instead of strings. -
An INTEGER with named numbers can be given as the name of any of them when encoding. It is always an
intwhen decoded.
Advanced usage
asnkitrt.load(compiled, codec='ber', numeric_enums=False, nonce=None, accept_any_nonce=False, bound_to=None, accept_any_binding=False, verification_keys=None, accept_any_signature=False, decryption_keys=None, lenient=False)
Return a Specification object that can be used to
encode and decode data structures of given compiled specification
compiled with given codec codec. codec may be one of
'ber', 'der', 'gser', 'jer', oer, 'per',
'uper' and 'xer'.
compiled is a CompiledSpecification
or the contents of an .asni file as bytes. Nothing else is
loaded. A syntax tree, or an .asnst file, has to be compiled
first.
Give numeric_enums as True for numeric enumeration values
instead of strings.
Give lenient as True to have the 'der' codec decode what
BER allows and DER does not, as it only does if it is told to.
DER wants a BOOLEAN of 0x00 or 0xff, an INTEGER, an ENUMERATED
and an OBJECT IDENTIFIER in as few bytes as possible, a length in
as few bytes as possible and never indefinite, a BIT STRING with
unused bits of zero and no trailing zero bits in a list of named
bits, a SET OF in ascending order of its elements, members in the
order they are declared in, nothing encoded that is a DEFAULT, a
REAL in its canonical form, and a UTCTime and a GeneralizedTime in
UTC with seconds and no trailing zeros. What the codec encodes is
DER whatever lenient is, and no other codec looks at it. What is
left after the data, as in decode, is never accepted.
nonce, accept_any_nonce, bound_to, accept_any_binding,
verification_keys, accept_any_signature and decryption_keys
tell which files to read, as for
from_asni(). A file that is signed,
encrypted, bound to something or has a nonce to check is not read
unless told what to expect.
>>> with open('foo.asni', 'rb') as fin:
... foo = asnkitrt.runtime.load(fin.read())
Reading and writing the files
asnkitrt.syntax.from_asni(data, nonce=None, accept_any_nonce=False, bound_to=None, accept_any_binding=False, verification_keys=None, accept_any_signature=False, decryption_keys=None)
Decode given contents of an .asni file, as returned by
:func:to_asni(), to a compiled specification.
nonce is what the nonce of the file has to be: a number it has
to be the same as, or a function that is given it and returns
True if it will do, as lambda nonce: nonce > last. A file
without a nonce to check is then not read. A file with a nonce to
check is not read if nonce is not given. With accept_any_nonce
as True a file is read whatever its nonce, and whether it has
one to check or not.
A function given as nonce must not keep the nonce it is given,
as the file may still be found to be bad. Keep that of
:func:read_header() once the file has been read instead.
bound_to is what the file has to be bound to. A file that is
not bound is then not read. A bound file is not read if bound_to
is not given. With accept_any_binding as True a file is read
whatever it is bound to, and whether it is bound or not.
verification_keys are the Ed25519 public keys of those trusted
to sign files, as a dictionary of the identifier of each key, of
16 bytes, to the key, of 32 bytes. A file is then only read if
signed with one of them. A signed file is not read if
verification_keys is not given. With accept_any_signature as
True a file is read without its signature being checked, and
whether it is signed or not.
decryption_keys are the AES-256 keys to decrypt files with, as
a dictionary of the identifier of each key, of 16 bytes, to the
key, of 32 bytes. A file is then only read if encrypted with one
of them. An encrypted file is not read if decryption_keys is not
given.
Signed and encrypted files need the cryptography package.
compiled = asnkitrt.syntax.from_asni(data)
asnkitrt.syntax.from_asnst(data)
Decode given contents of an .asnst file, as returned by
:func:to_asnst(), to a syntax tree.
tree = asnkitrt.syntax.from_asnst(data)
asnkitrt.syntax.to_asni(compiled, metadata=None, nonce=None, signing_key=None, encryption_key=None)
Encode given compiled specification as the contents of an
.asni file, with given metadata.
Give nonce as a number of at most 24 bits for a file with a
nonce to check.
Give signing_key for a signed file, as the identifier of the
key, of 16 bytes, and the Ed25519 private key, of 32 bytes. Give
encryption_key for an encrypted file, as the identifier of the
key, of 16 bytes, and the AES-256 key, of 32 bytes. Both need the
cryptography package.
with open('foo.asni', 'wb') as fout: ... fout.write(asnkitrt.syntax.to_asni(compiled))
asnkitrt.syntax.to_asnst(tree, metadata=None)
Encode given syntax tree as the contents of an .asnst file,
with given metadata.
with open('foo.asnst', 'wb') as fout: ... fout.write(asnkitrt.syntax.to_asnst(tree))