#!/usr/bin/env python
# coding: utf-8
# PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
-# Copyright (C) 2017-2019 Sergey Matveev <stargrave@stargrave.org>
+# Copyright (C) 2017-2020 Sergey Matveev <stargrave@stargrave.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
This option should be used only for skipping some decode errors, just
to see the decoded structure somehow.
+Base Obj
+--------
+.. autoclass:: pyderasn.Obj
+ :members:
+
Primitive types
---------------
.. autofunction:: pyderasn.tag_decode
.. autofunction:: pyderasn.tag_ctxp
.. autofunction:: pyderasn.tag_ctxc
-.. autoclass:: pyderasn.Obj
.. autoclass:: pyderasn.DecodeError
:members: __init__
.. autoclass:: pyderasn.NotEnoughData
+.. autoclass:: pyderasn.ExceedingData
.. autoclass:: pyderasn.LenIndefForm
.. autoclass:: pyderasn.TagMismatch
.. autoclass:: pyderasn.InvalidLength
from os import environ
from string import ascii_letters
from string import digits
+from unicodedata import category as unicat
from six import add_metaclass
from six import binary_type
def colored(what, *args, **kwargs):
return what
+__version__ = "5.6"
__all__ = (
"Any",
"DecodeError",
"DecodePathDefBy",
"Enumerated",
+ "ExceedingData",
"GeneralizedTime",
"GeneralString",
"GraphicString",
pass
+class ExceedingData(ASN1Error):
+ def __init__(self, nbytes):
+ super(ExceedingData, self).__init__()
+ self.nbytes = nbytes
+
+ def __str__(self):
+ return "%d trailing bytes" % self.nbytes
+
+ def __repr__(self):
+ return "%s(%s)" % (self.__class__.__name__, self)
+
+
class LenIndefForm(DecodeError):
pass
########################################################################
class AutoAddSlots(type):
- def __new__(mcs, name, bases, _dict):
+ def __new__(cls, name, bases, _dict):
_dict["__slots__"] = _dict.get("__slots__", ())
- return type.__new__(mcs, name, bases, _dict)
+ return type.__new__(cls, name, bases, _dict)
@add_metaclass(AutoAddSlots)
@property
def tlen(self):
+ """See :ref:`decoding`
+ """
return len(self.tag)
@property
def tlvlen(self):
+ """See :ref:`decoding`
+ """
return self.tlen + self.llen + self.vlen
def __str__(self): # pragma: no cover
raise NotImplementedError()
def encode(self):
+ """Encode the structure
+
+ :returns: DER representation
+ """
raw = self._encode()
if self._expl is None:
return raw
determine if tag satisfies the scheme)
:param _ctx_immutable: do we need to copy ``ctx`` before using it
:returns: (Obj, remaining data)
+
+ .. seealso:: :ref:`decoding`
"""
if ctx is None:
ctx = {}
tag_only=tag_only,
)
if tag_only:
- return
+ return None
obj, tail = result
else:
try:
tag_only=tag_only,
)
if tag_only: # pragma: no cover
- return
+ return None
obj, tail = result
eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
if eoc_expected.tobytes() != EOC:
tag_only=tag_only,
)
if tag_only: # pragma: no cover
- return
+ return None
obj, tail = result
if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
raise DecodeError(
)
return obj, (tail if leavemm else tail.tobytes())
+ def decod(self, data, offset=0, decode_path=(), ctx=None):
+ """Decode the data, check that tail is empty
+
+ :raises ExceedingData: if tail is not empty
+
+ This is just a wrapper over :py:meth:`pyderasn.Obj.decode`
+ (decode without tail) that also checks that there is no
+ trailing data left.
+ """
+ obj, tail = self.decode(
+ data,
+ offset=offset,
+ decode_path=decode_path,
+ ctx=ctx,
+ leavemm=True,
+ )
+ if len(tail) > 0:
+ raise ExceedingData(len(tail))
+ return obj
+
@property
def expled(self):
+ """See :ref:`decoding`
+ """
return self._expl is not None
@property
def expl_tag(self):
+ """See :ref:`decoding`
+ """
return self._expl
@property
def expl_tlen(self):
+ """See :ref:`decoding`
+ """
return len(self._expl)
@property
def expl_llen(self):
+ """See :ref:`decoding`
+ """
if self.expl_lenindef:
return 1
return len(len_encode(self.tlvlen))
@property
def expl_offset(self):
+ """See :ref:`decoding`
+ """
return self.offset - self.expl_tlen - self.expl_llen
@property
def expl_vlen(self):
+ """See :ref:`decoding`
+ """
return self.tlvlen
@property
def expl_tlvlen(self):
+ """See :ref:`decoding`
+ """
return self.expl_tlen + self.expl_llen + self.expl_vlen
@property
def fulloffset(self):
+ """See :ref:`decoding`
+ """
return self.expl_offset if self.expled else self.offset
@property
def fulllen(self):
+ """See :ref:`decoding`
+ """
return self.expl_tlvlen if self.expled else self.tlvlen
def pps_lenindef(self, decode_path):
offset=offset,
)
if tag_only:
- return
+ return None
try:
l, _, v = len_decode(lv)
except DecodeError as err:
for name, value in iteritems(self.specs):
if value == self._value:
return name
+ return None
def __call__(
self,
offset=offset,
)
if tag_only:
- return
+ return None
try:
l, llen, v = len_decode(lv)
except DecodeError as err:
if not frozenset(value) <= SET01:
raise ValueError("B's coding contains unacceptable chars")
return self._bits2octets(value)
- elif value.endswith("'H"):
+ if value.endswith("'H"):
value = value[1:-2]
return (
len(value) * 4,
)
if isinstance(value, binary_type):
return (len(value) * 8, value)
- else:
- raise InvalidValueType((self.__class__, string_types, binary_type))
+ raise InvalidValueType((self.__class__, string_types, binary_type))
if isinstance(value, tuple):
if (
len(value) == 2 and
octets,
))
- def _decode_chunk(self, lv, offset, decode_path, ctx):
+ def _decode_chunk(self, lv, offset, decode_path):
try:
l, llen, v = len_decode(lv)
except DecodeError as err:
)
if t == self.tag:
if tag_only: # pragma: no cover
- return
- return self._decode_chunk(lv, offset, decode_path, ctx)
+ return None
+ return self._decode_chunk(lv, offset, decode_path)
if t == self.tag_constructed:
if not ctx.get("bered", False):
raise DecodeError(
offset=offset,
)
if tag_only: # pragma: no cover
- return
+ return None
lenindef = False
try:
l, llen, v = len_decode(lv)
:param default: set default value. Type same as in ``value``
:param bool optional: is object ``OPTIONAL`` in sequence
"""
- super(OctetString, self).__init__(
- impl,
- expl,
- default,
- optional,
- _decoded,
- )
+ super(OctetString, self).__init__(impl, expl, default, optional, _decoded)
self._value = value
self._bound_min, self._bound_max = getattr(
self,
self._value,
))
- def _decode_chunk(self, lv, offset, decode_path, ctx):
+ def _decode_chunk(self, lv, offset, decode_path):
try:
l, llen, v = len_decode(lv)
except DecodeError as err:
)
if t == self.tag:
if tag_only:
- return
- return self._decode_chunk(lv, offset, decode_path, ctx)
+ return None
+ return self._decode_chunk(lv, offset, decode_path)
if t == self.tag_constructed:
if not ctx.get("bered", False):
raise DecodeError(
offset=offset,
)
if tag_only:
- return
+ return None
lenindef = False
try:
l, llen, v = len_decode(lv)
offset=offset,
)
if tag_only: # pragma: no cover
- return
+ return None
try:
l, _, v = len_decode(lv)
except DecodeError as err:
:param default: set default value. Type same as in ``value``
:param bool optional: is object ``OPTIONAL`` in sequence
"""
- super(ObjectIdentifier, self).__init__(
- impl,
- expl,
- default,
- optional,
- _decoded,
- )
+ super(ObjectIdentifier, self).__init__(impl, expl, default, optional, _decoded)
self._value = value
if value is not None:
self._value = self._value_sanitize(value)
offset=offset,
)
if tag_only: # pragma: no cover
- return
+ return None
try:
l, llen, v = len_decode(lv)
except DecodeError as err:
bounds=None, # dummy argument, workability for Integer.decode
):
super(Enumerated, self).__init__(
- value=value,
- impl=impl,
- expl=expl,
- default=default,
- optional=optional,
- _specs=_specs,
- _decoded=_decoded,
+ value, bounds, impl, expl,default, optional, _specs, _decoded,
)
if len(self.specs) == 0:
raise ValueError("schema must be specified")
)
+def escape_control_unicode(c):
+ if unicat(c).startswith("C"):
+ c = repr(c).lstrip("u").strip("'")
+ return c
+
+
class CommonString(OctetString):
"""Common class for all strings
def pps(self, decode_path=(), no_unicode=False):
value = None
if self.ready:
- value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
+ value = (
+ hexenc(bytes(self)) if no_unicode else
+ "".join(escape_control_unicode(c) for c in self.__unicode__())
+ )
yield _pp(
obj=self,
asn1_type_name=self.asn1_type_name,
:param bool optional: is object ``OPTIONAL`` in sequence
"""
super(UTCTime, self).__init__(
- impl=impl,
- expl=expl,
- default=default,
- optional=optional,
- _decoded=_decoded,
+ None, None, impl, expl, default, optional, _decoded,
)
self._value = value
if value is not None:
offset=offset,
)
if tag_only: # pragma: no cover
- return
+ return None
value, tail = spec.decode(
tlv,
offset=offset,
if spec.optional:
continue
return False
- else:
- if not value.ready:
- return False
+ if not value.ready:
+ return False
return True
@property
offset=offset,
)
if tag_only: # pragma: no cover
- return
+ return None
lenindef = False
ctx_bered = ctx.get("bered", False)
try:
offset=offset,
)
if tag_only:
- return
+ return None
lenindef = False
ctx_bered = ctx.get("bered", False)
try:
optional=False,
_decoded=(0, 0, 0),
):
- super(SequenceOf, self).__init__(
- impl,
- expl,
- default,
- optional,
- _decoded,
- )
+ super(SequenceOf, self).__init__(impl, expl, default, optional, _decoded)
if schema is None:
schema = getattr(self, "schema", None)
if schema is None:
offset=offset,
)
if tag_only:
- return
+ return None
lenindef = False
ctx_bered = ctx.get("bered", False)
try:
print(pprinter(
obj,
oid_maps=oid_maps,
- with_colours=True if environ.get("NO_COLOR") is None else False,
+ with_colours=environ.get("NO_COLOR") is None,
with_decode_path=args.print_decode_path,
decode_path_only=(
() if args.decode_path_only is None else