#!/usr/bin/env python
# coding: utf-8
-# PyDERASN -- Python ASN.1 DER codec with abstract structures
+# PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
# Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
#
# This program is free software: you can redistribute it and/or modify
# You should have received a copy of the GNU Lesser General Public
# License along with this program. If not, see
# <http://www.gnu.org/licenses/>.
-"""Python ASN.1 DER codec with abstract structures
+"""Python ASN.1 DER/BER codec with abstract structures
-This library allows you to marshal and unmarshal various structures in
-ASN.1 DER format, like this:
+This library allows you to marshal various structures in ASN.1 DER
+format, unmarshal them in BER/CER/DER ones.
>>> i = Integer(123)
>>> raw = i.encode()
lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
(that actually equals to ordinary ``tlvlen``).
-When error occurs, then :py:exc:`pyderasn.DecodeError` is raised.
+When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
.. _ctx:
Currently available context options:
+* :ref:`bered <bered_ctx>`
* :ref:`defines_by_path <defines_by_path_ctx>`
* :ref:`strict_default_existence <strict_default_existence_ctx>`
decoding is already done. ``any`` means literally any value it meet --
useful for SEQUENCE/SET OF-s.
+.. _bered_ctx:
+
+BER encoding
+------------
+
+.. warning::
+
+ Currently BER support is not extensively tested.
+
+By default PyDERASN accepts only DER encoded data. It always encodes to
+DER. But you can optionally enable BER decoding with setting ``bered``
+:ref:`context <ctx>` argument to True. Indefinite lengths and
+constructed primitive types should be parsed successfully.
+
+* If object is encoded in BER form (not the DER one), then ``bered``
+ attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
+ STRING`` can contain it.
+* If object has an indefinite length encoding, then its ``lenindef``
+ attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
+ ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
+ contain it.
+* If object has an indefinite length encoded explicit tag, then
+ ``expl_lenindef`` is set to True.
+
+EOC (end-of-contents) token's length is taken in advance in object's
+value length.
+
Primitive types
---------------
____________
.. autoclass:: pyderasn.CommonString
+NumericString
+_____________
+.. autoclass:: pyderasn.NumericString
+
UTCTime
_______
.. autoclass:: pyderasn.UTCTime
.. autofunction:: pyderasn.tag_ctxp
.. autofunction:: pyderasn.tag_ctxc
.. autoclass:: pyderasn.Obj
+.. autoclass:: pyderasn.DecodeError
+ :members: __init__
+.. autoclass:: pyderasn.NotEnoughData
+.. autoclass:: pyderasn.LenIndefForm
+.. autoclass:: pyderasn.TagMismatch
+.. autoclass:: pyderasn.InvalidLength
+.. autoclass:: pyderasn.InvalidOID
+.. autoclass:: pyderasn.ObjUnknown
+.. autoclass:: pyderasn.ObjNotReady
+.. autoclass:: pyderasn.InvalidValueType
+.. autoclass:: pyderasn.BoundsError
"""
from codecs import getdecoder
"InvalidOID",
"InvalidValueType",
"ISO646String",
+ "LenIndefForm",
"NotEnoughData",
"Null",
"NumericString",
['nonRepudiation', 'keyEncipherment']
>>> b.specs
{'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
+
+ .. note::
+
+ Pay attention that BIT STRING can be encoded both in primitive
+ and constructed forms. Decoder always checks constructed form tag
+ additionally to specified primitive one. If BER decoding is
+ :ref:`not enabled <bered_ctx>`, then decoder will fail, because
+ of DER restrictions.
"""
__slots__ = ("tag_constructed", "specs", "defined")
tag_default = tag_encode(3)
pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
>>> OctetString(b"hell", bounds=(4, 4))
OCTET STRING 4 bytes 68656c6c
+
+ .. note::
+
+ Pay attention that OCTET STRING can be encoded both in primitive
+ and constructed forms. Decoder always checks constructed form tag
+ additionally to specified primitive one. If BER decoding is
+ :ref:`not enabled <bered_ctx>`, then decoder will fail, because
+ of DER restrictions.
"""
__slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
tag_default = tag_encode(4)
class NumericString(CommonString):
+ """Numeric string
+
+ Its value is properly sanitized: only ASCII digits can be stored.
+ """
__slots__ = ()
tag_default = tag_encode(18)
encoding = "ascii"
def main(): # pragma: no cover
import argparse
- parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
+ parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
parser.add_argument(
"--skip",
type=int,
"--defines-by-path",
help="Python path to decoder's defines_by_path",
)
+ parser.add_argument(
+ "--nobered",
+ action='store_true',
+ help="Disallow BER encoding",
+ )
parser.add_argument(
"DERFile",
type=argparse.FileType("rb"),
pprinter = partial(pprint, big_blobs=True)
else:
schema, pprinter = generic_decoder()
- ctx = {"bered": True}
+ ctx = {"bered": not args.nobered}
if args.defines_by_path is not None:
ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
obj, tail = schema().decode(der, ctx=ctx)