]> Cypherpunks.ru repositories - pyderasn.git/blobdiff - pyderasn.py
More test coverage
[pyderasn.git] / pyderasn.py
index 607ca50cf68331f8d62938b89947c7a6fba138a5..2e63b5a2320bc672bf7d01f35acb053d241b3145 100755 (executable)
@@ -382,7 +382,7 @@ 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``
+* If object is encoded in BER form (not the DER one), then ``ber_encoded``
   attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
   STRING``, ``SEQUENCE``, ``SET``, ``SET OF`` can contain it.
 * If object has an indefinite length encoding, then its ``lenindef``
@@ -391,6 +391,12 @@ constructed primitive types should be parsed successfully.
   contain it.
 * If object has an indefinite length encoded explicit tag, then
   ``expl_lenindef`` is set to True.
+* If object has either any of BER-related encoding (explicit tag
+  indefinite length, object's indefinite length, BER-encoding) or any
+  underlying component has that kind of encoding, then ``bered``
+  attribute is set to True. For example SignedData CMS can have
+  ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
+  ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
 
 EOC (end-of-contents) token's length is taken in advance in object's
 value length.
@@ -553,7 +559,7 @@ from six.moves import xrange as six_xrange
 
 try:
     from termcolor import colored
-except ImportError:
+except ImportError:  # pragma: no cover
     def colored(what, *args):
         return what
 
@@ -922,7 +928,7 @@ class Obj(object):
         "vlen",
         "expl_lenindef",
         "lenindef",
-        "bered",
+        "ber_encoded",
     )
 
     def __init__(
@@ -944,7 +950,7 @@ class Obj(object):
         self.default = None
         self.expl_lenindef = False
         self.lenindef = False
-        self.bered = False
+        self.ber_encoded = False
 
     @property
     def ready(self):  # pragma: no cover
@@ -956,6 +962,12 @@ class Obj(object):
         if not self.ready:
             raise ObjNotReady(self.__class__.__name__)
 
+    @property
+    def bered(self):
+        """Is either object or any elements inside is BER encoded?
+        """
+        return self.expl_lenindef or self.lenindef or self.ber_encoded
+
     @property
     def decoded(self):
         """Is object decoded?
@@ -1072,7 +1084,7 @@ class Obj(object):
                     ctx=ctx,
                     tag_only=tag_only,
                 )
-                if tag_only:
+                if tag_only:  # pragma: no cover
                     return
                 obj, tail = result
                 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
@@ -1107,7 +1119,7 @@ class Obj(object):
                     ctx=ctx,
                     tag_only=tag_only,
                 )
-                if tag_only:
+                if tag_only:  # pragma: no cover
                     return
                 obj, tail = result
                 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
@@ -1158,7 +1170,10 @@ class Obj(object):
         return self.expl_tlvlen if self.expled else self.tlvlen
 
     def pps_lenindef(self, decode_path):
-        if self.lenindef:
+        if self.lenindef and not (
+            getattr(self, "defined", None) is not None and
+            self.defined[1].lenindef
+        ):
             yield _pp(
                 asn1_type_name="EOC",
                 obj_name="",
@@ -1170,6 +1185,7 @@ class Obj(object):
                 tlen=1,
                 llen=1,
                 vlen=0,
+                ber_encoded=True,
                 bered=True,
             )
         if self.expl_lenindef:
@@ -1181,6 +1197,7 @@ class Obj(object):
                 tlen=1,
                 llen=1,
                 vlen=0,
+                ber_encoded=True,
                 bered=True,
             )
 
@@ -1232,6 +1249,7 @@ PP = namedtuple("PP", (
     "expl_vlen",
     "expl_lenindef",
     "lenindef",
+    "ber_encoded",
     "bered",
 ))
 
@@ -1256,6 +1274,7 @@ def _pp(
         expl_vlen=None,
         expl_lenindef=False,
         lenindef=False,
+        ber_encoded=False,
         bered=False,
 ):
     return PP(
@@ -1278,6 +1297,7 @@ def _pp(
         expl_vlen,
         expl_lenindef,
         lenindef,
+        ber_encoded,
         bered,
     )
 
@@ -1305,7 +1325,9 @@ def pp_console_row(
             ),
             LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
         )
-        cols.append(_colourize(col, "red", with_colours, ()))
+        col = _colourize(col, "red", with_colours, ())
+        col += _colourize("B", "red", with_colours) if pp.bered else " "
+        cols.append(col)
         col = "[%d,%d,%4d]%s" % (
             pp.tlen,
             pp.llen,
@@ -1342,7 +1364,7 @@ def pp_console_row(
         cols.append(_colourize(col, "blue", with_colours))
     if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
         cols.append(_colourize(pp.obj_name, "magenta", with_colours))
-    if pp.bered:
+    if pp.ber_encoded:
         cols.append(_colourize("BER", "red", with_colours))
     cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
     if pp.value is not None:
@@ -1373,7 +1395,7 @@ def pp_console_row(
 
 
 def pp_console_blob(pp, decode_path_len_decrease=0):
-    cols = [" " * len("XXXXXYYZ [X,X,XXXX]Z")]
+    cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
     decode_path_len = len(pp.decode_path) - decode_path_len_decrease
     if decode_path_len > 0:
         cols.append(" ." * (decode_path_len + 1))
@@ -1605,14 +1627,14 @@ class Boolean(Obj):
                 offset=offset,
             )
         first_octet = byte2int(v)
-        bered = False
+        ber_encoded = False
         if first_octet == 0:
             value = False
         elif first_octet == 0xFF:
             value = True
         elif ctx.get("bered", False):
             value = True
-            bered = True
+            ber_encoded = True
         else:
             raise DecodeError(
                 "unacceptable Boolean value",
@@ -1628,7 +1650,7 @@ class Boolean(Obj):
             optional=self.optional,
             _decoded=(offset, 1, 1),
         )
-        obj.bered = bered
+        obj.ber_encoded = ber_encoded
         return obj, v[1:]
 
     def __repr__(self):
@@ -1653,6 +1675,7 @@ class Boolean(Obj):
             expl_llen=self.expl_llen if self.expled else None,
             expl_vlen=self.expl_vlen if self.expled else None,
             expl_lenindef=self.expl_lenindef,
+            ber_encoded=self.ber_encoded,
             bered=self.bered,
         )
         for pp in self.pps_lenindef(decode_path):
@@ -1979,6 +2002,7 @@ class Integer(Obj):
             expl_llen=self.expl_llen if self.expled else None,
             expl_vlen=self.expl_vlen if self.expled else None,
             expl_lenindef=self.expl_lenindef,
+            bered=self.bered,
         )
         for pp in self.pps_lenindef(decode_path):
             yield pp
@@ -2293,7 +2317,7 @@ class BitString(Obj):
                 offset=offset,
             )
         if t == self.tag:
-            if tag_only:
+            if tag_only:  # pragma: no cover
                 return
             return self._decode_chunk(lv, offset, decode_path, ctx)
         if t == self.tag_constructed:
@@ -2304,7 +2328,7 @@ class BitString(Obj):
                     decode_path=decode_path,
                     offset=offset,
                 )
-            if tag_only:
+            if tag_only:  # pragma: no cover
                 return
             lenindef = False
             try:
@@ -2402,7 +2426,7 @@ class BitString(Obj):
                 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
             )
             obj.lenindef = lenindef
-            obj.bered = True
+            obj.ber_encoded = True
             return obj, (v[EOC_LEN:] if lenindef else v)
         raise TagMismatch(
             klass=self.__class__,
@@ -2441,6 +2465,7 @@ class BitString(Obj):
             expl_vlen=self.expl_vlen if self.expled else None,
             expl_lenindef=self.expl_lenindef,
             lenindef=self.lenindef,
+            ber_encoded=self.ber_encoded,
             bered=self.bered,
         )
         defined_by, defined = self.defined or (None, None)
@@ -2758,7 +2783,7 @@ class OctetString(Obj):
                     offset=offset,
                 )
             obj.lenindef = lenindef
-            obj.bered = True
+            obj.ber_encoded = True
             return obj, (v[EOC_LEN:] if lenindef else v)
         raise TagMismatch(
             klass=self.__class__,
@@ -2790,6 +2815,7 @@ class OctetString(Obj):
             expl_vlen=self.expl_vlen if self.expled else None,
             expl_lenindef=self.expl_lenindef,
             lenindef=self.lenindef,
+            ber_encoded=self.ber_encoded,
             bered=self.bered,
         )
         defined_by, defined = self.defined or (None, None)
@@ -2884,7 +2910,7 @@ class Null(Obj):
                 decode_path=decode_path,
                 offset=offset,
             )
-        if tag_only:
+        if tag_only:  # pragma: no cover
             return
         try:
             l, _, v = len_decode(lv)
@@ -2930,6 +2956,7 @@ class Null(Obj):
             expl_llen=self.expl_llen if self.expled else None,
             expl_vlen=self.expl_vlen if self.expled else None,
             expl_lenindef=self.expl_lenindef,
+            bered=self.bered,
         )
         for pp in self.pps_lenindef(decode_path):
             yield pp
@@ -3134,7 +3161,7 @@ class ObjectIdentifier(Obj):
                 decode_path=decode_path,
                 offset=offset,
             )
-        if tag_only:
+        if tag_only:  # pragma: no cover
             return
         try:
             l, llen, v = len_decode(lv)
@@ -3221,6 +3248,7 @@ class ObjectIdentifier(Obj):
             expl_llen=self.expl_llen if self.expled else None,
             expl_vlen=self.expl_vlen if self.expled else None,
             expl_lenindef=self.expl_lenindef,
+            bered=self.bered,
         )
         for pp in self.pps_lenindef(decode_path):
             yield pp
@@ -3446,6 +3474,8 @@ class CommonString(OctetString):
             expl_llen=self.expl_llen if self.expled else None,
             expl_vlen=self.expl_vlen if self.expled else None,
             expl_lenindef=self.expl_lenindef,
+            ber_encoded=self.ber_encoded,
+            bered=self.bered,
         )
         for pp in self.pps_lenindef(decode_path):
             yield pp
@@ -3648,6 +3678,8 @@ class UTCTime(CommonString):
             expl_llen=self.expl_llen if self.expled else None,
             expl_vlen=self.expl_vlen if self.expled else None,
             expl_lenindef=self.expl_lenindef,
+            ber_encoded=self.ber_encoded,
+            bered=self.bered,
         )
         for pp in self.pps_lenindef(decode_path):
             yield pp
@@ -3846,6 +3878,13 @@ class Choice(Obj):
     def ready(self):
         return self._value is not None and self._value[1].ready
 
+    @property
+    def bered(self):
+        return self.expl_lenindef or (
+            (self._value is not None) and
+            self._value[1].bered
+        )
+
     def copy(self):
         obj = self.__class__(schema=self.specs)
         obj._expl = self._expl
@@ -3945,7 +3984,7 @@ class Choice(Obj):
                 decode_path=decode_path,
                 offset=offset,
             )
-        if tag_only:
+        if tag_only:  # pragma: no cover
             return
         value, tail = spec.decode(
             tlv,
@@ -3985,6 +4024,7 @@ class Choice(Obj):
             llen=self.llen,
             vlen=self.vlen,
             expl_lenindef=self.expl_lenindef,
+            bered=self.bered,
         )
         if self.ready:
             yield self.value.pps(decode_path=decode_path + (self.choice,))
@@ -4073,6 +4113,14 @@ class Any(Obj):
     def ready(self):
         return self._value is not None
 
+    @property
+    def bered(self):
+        if self.expl_lenindef or self.lenindef:
+            return True
+        if self.defined is None:
+            return False
+        return self.defined[1].bered
+
     def copy(self):
         obj = self.__class__()
         obj._value = self._value
@@ -4207,6 +4255,7 @@ class Any(Obj):
             expl_vlen=self.expl_vlen if self.expled else None,
             expl_lenindef=self.expl_lenindef,
             lenindef=self.lenindef,
+            bered=self.bered,
         )
         defined_by, defined = self.defined or (None, None)
         if defined_by is not None:
@@ -4404,6 +4453,12 @@ class Sequence(Obj):
                     return False
         return True
 
+    @property
+    def bered(self):
+        if self.expl_lenindef or self.lenindef or self.ber_encoded:
+            return True
+        return any(value.bered for value in self._value.values())
+
     def copy(self):
         obj = self.__class__(schema=self.specs)
         obj.tag = self.tag
@@ -4503,7 +4558,7 @@ class Sequence(Obj):
                 decode_path=decode_path,
                 offset=offset,
             )
-        if tag_only:
+        if tag_only:  # pragma: no cover
             return
         lenindef = False
         ctx_bered = ctx.get("bered", False)
@@ -4538,7 +4593,7 @@ class Sequence(Obj):
         vlen = 0
         sub_offset = offset + tlen + llen
         values = {}
-        bered = False
+        ber_encoded = False
         ctx_allow_default_values = ctx.get("allow_default_values", False)
         for name, spec in self.specs.items():
             if spec.optional and (
@@ -4613,7 +4668,7 @@ class Sequence(Obj):
             v = v_tail
             if spec.default is not None and value == spec.default:
                 if ctx_bered or ctx_allow_default_values:
-                    bered = True
+                    ber_encoded = True
                 else:
                     raise DecodeError(
                         "DEFAULT value met",
@@ -4663,7 +4718,7 @@ class Sequence(Obj):
         )
         obj._value = values
         obj.lenindef = lenindef
-        obj.bered = bered
+        obj.ber_encoded = ber_encoded
         return obj, tail
 
     def __repr__(self):
@@ -4695,6 +4750,8 @@ class Sequence(Obj):
             expl_vlen=self.expl_vlen if self.expled else None,
             expl_lenindef=self.expl_lenindef,
             lenindef=self.lenindef,
+            ber_encoded=self.ber_encoded,
+            bered=self.bered,
         )
         for name in self.specs:
             value = self._value.get(name)
@@ -4778,7 +4835,7 @@ class Set(Sequence):
         vlen = 0
         sub_offset = offset + tlen + llen
         values = {}
-        bered = False
+        ber_encoded = False
         ctx_allow_default_values = ctx.get("allow_default_values", False)
         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
         value_prev = memoryview(v[:0])
@@ -4816,7 +4873,7 @@ class Set(Sequence):
             value_len = value.fulllen
             if value_prev.tobytes() > v[:value_len].tobytes():
                 if ctx_bered or ctx_allow_unordered_set:
-                    bered = True
+                    ber_encoded = True
                 else:
                     raise DecodeError(
                         "unordered " + self.asn1_type_name,
@@ -4827,7 +4884,7 @@ class Set(Sequence):
             if spec.default is None or value != spec.default:
                 pass
             elif ctx_bered or ctx_allow_default_values:
-                bered = True
+                ber_encoded = True
             else:
                 raise DecodeError(
                     "DEFAULT value met",
@@ -4866,7 +4923,7 @@ class Set(Sequence):
                 decode_path=decode_path,
                 offset=offset,
             )
-        obj.bered = bered
+        obj.ber_encoded = ber_encoded
         return obj, tail
 
 
@@ -4965,6 +5022,12 @@ class SequenceOf(Obj):
     def ready(self):
         return all(v.ready for v in self._value)
 
+    @property
+    def bered(self):
+        if self.expl_lenindef or self.lenindef or self.ber_encoded:
+            return True
+        return any(v.bered for v in self._value)
+
     def copy(self):
         obj = self.__class__(schema=self.spec)
         obj._bound_min = self._bound_min
@@ -5103,7 +5166,7 @@ class SequenceOf(Obj):
         _value = []
         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
         value_prev = memoryview(v[:0])
-        bered = False
+        ber_encoded = False
         spec = self.spec
         while len(v) > 0:
             if lenindef and v[:EOC_LEN].tobytes() == EOC:
@@ -5120,7 +5183,7 @@ class SequenceOf(Obj):
             if ordering_check:
                 if value_prev.tobytes() > v[:value_len].tobytes():
                     if ctx_bered or ctx_allow_unordered_set:
-                        bered = True
+                        ber_encoded = True
                     else:
                         raise DecodeError(
                             "unordered " + self.asn1_type_name,
@@ -5161,7 +5224,7 @@ class SequenceOf(Obj):
                 )
             obj.lenindef = True
             tail = v[EOC_LEN:]
-        obj.bered = bered
+        obj.ber_encoded = ber_encoded
         return obj, tail
 
     def __repr__(self):
@@ -5189,6 +5252,8 @@ class SequenceOf(Obj):
             expl_vlen=self.expl_vlen if self.expled else None,
             expl_lenindef=self.expl_lenindef,
             lenindef=self.lenindef,
+            ber_encoded=self.ber_encoded,
+            bered=self.bered,
         )
         for i, value in enumerate(self._value):
             yield value.pps(decode_path=decode_path + (str(i),))