]> Cypherpunks.ru repositories - pyderasn.git/blobdiff - pyderasn.py
ber_encoded at ObjectIdentifier
[pyderasn.git] / pyderasn.py
index 4a9684fc0b9a6a9e000764fbbca938e223640f4b..516036862de84a150bd57bd8f29941da40985393 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # coding: utf-8
 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
-# Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
+# Copyright (C) 2017-2019 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
@@ -384,7 +384,8 @@ constructed primitive types should be parsed successfully.
 
 * 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.
+  STRING``, ``OBJECT IDENTIFIER``, ``SEQUENCE``, ``SET``, ``SET OF``
+  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
@@ -515,6 +516,7 @@ Various
 -------
 
 .. autofunction:: pyderasn.abs_decode_path
+.. autofunction:: pyderasn.colonize_hex
 .. autofunction:: pyderasn.hexenc
 .. autofunction:: pyderasn.hexdec
 .. autofunction:: pyderasn.tag_encode
@@ -539,6 +541,7 @@ from codecs import getdecoder
 from codecs import getencoder
 from collections import namedtuple
 from collections import OrderedDict
+from copy import copy
 from datetime import datetime
 from math import ceil
 from os import environ
@@ -646,6 +649,7 @@ LENINDEF_PP_CHAR = "I" if PY2 else "∞"
 class ASN1Error(ValueError):
     pass
 
+
 class DecodeError(ASN1Error):
     def __init__(self, msg="", klass=None, decode_path=(), offset=0):
         """
@@ -1027,6 +1031,7 @@ class Obj(object):
             decode_path=(),
             ctx=None,
             tag_only=False,
+            _ctx_immutable=True,
     ):
         """Decode the data
 
@@ -1034,14 +1039,17 @@ class Obj(object):
         :param int offset: initial data's offset
         :param bool leavemm: do we need to leave memoryview of remaining
                     data as is, or convert it to bytes otherwise
-        :param ctx: optional :ref:`context <ctx>` governing decoding process.
+        :param ctx: optional :ref:`context <ctx>` governing decoding process
         :param tag_only: decode only the tag, without length and contents
                          (used only in Choice and Set structures, trying to
                          determine if tag satisfies the scheme)
+        :param _ctx_immutable: do we need to copy ``ctx`` before using it
         :returns: (Obj, remaining data)
         """
         if ctx is None:
             ctx = {}
+        elif _ctx_immutable:
+            ctx = copy(ctx)
         tlv = memoryview(data)
         if self._expl is None:
             result = self._decode(
@@ -1235,6 +1243,7 @@ class DecodePathDefBy(object):
 ########################################################################
 
 PP = namedtuple("PP", (
+    "obj",
     "asn1_type_name",
     "obj_name",
     "decode_path",
@@ -1260,6 +1269,7 @@ PP = namedtuple("PP", (
 
 
 def _pp(
+        obj=None,
         asn1_type_name="unknown",
         obj_name="unknown",
         decode_path=(),
@@ -1283,6 +1293,7 @@ def _pp(
         bered=False,
 ):
     return PP(
+        obj,
         asn1_type_name,
         obj_name,
         decode_path,
@@ -1311,6 +1322,12 @@ def _colourize(what, colour, with_colours, attrs=("bold",)):
     return colored(what, colour, attrs=attrs) if with_colours else what
 
 
+def colonize_hex(hexed):
+    """Separate hexadecimal string with colons
+    """
+    return ":".join(hexed[i:i + 2] for i in range(0, len(hexed), 2))
+
+
 def pp_console_row(
         pp,
         oids=None,
@@ -1381,6 +1398,15 @@ def pp_console_row(
                 value in oids
         ):
             cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
+        if pp.asn1_type_name == Integer.asn1_type_name:
+            hex_repr = hex(int(pp.obj._value))[2:].upper()
+            if len(hex_repr) % 2 != 0:
+                hex_repr = "0" + hex_repr
+            cols.append(_colourize(
+                "(%s)" % colonize_hex(hex_repr),
+                "green",
+                with_colours,
+            ))
     if with_blob:
         if isinstance(pp.blob, binary_type):
             cols.append(hexenc(pp.blob))
@@ -1408,9 +1434,7 @@ def pp_console_blob(pp, decode_path_len_decrease=0):
         blob = hexenc(pp.blob).upper()
         for i in range(0, len(blob), 32):
             chunk = blob[i:i + 32]
-            yield " ".join(cols + [":".join(
-                chunk[j:j + 2] for j in range(0, len(chunk), 2)
-            )])
+            yield " ".join(cols + [colonize_hex(chunk)])
     elif isinstance(pp.blob, tuple):
         yield " ".join(cols + [", ".join(pp.blob)])
 
@@ -1663,6 +1687,7 @@ class Boolean(Obj):
 
     def pps(self, decode_path=()):
         yield _pp(
+            obj=self,
             asn1_type_name=self.asn1_type_name,
             obj_name=self.__class__.__name__,
             decode_path=decode_path,
@@ -1990,6 +2015,7 @@ class Integer(Obj):
 
     def pps(self, decode_path=()):
         yield _pp(
+            obj=self,
             asn1_type_name=self.asn1_type_name,
             obj_name=self.__class__.__name__,
             decode_path=decode_path,
@@ -2387,6 +2413,7 @@ class BitString(Obj):
                         decode_path=sub_decode_path,
                         leavemm=True,
                         ctx=ctx,
+                        _ctx_immutable=False,
                     )
                 except TagMismatch:
                     raise DecodeError(
@@ -2451,6 +2478,7 @@ class BitString(Obj):
             if len(self.specs) > 0:
                 blob = tuple(self.named)
         yield _pp(
+            obj=self,
             asn1_type_name=self.asn1_type_name,
             obj_name=self.__class__.__name__,
             decode_path=decode_path,
@@ -2751,6 +2779,7 @@ class OctetString(Obj):
                         decode_path=sub_decode_path,
                         leavemm=True,
                         ctx=ctx,
+                        _ctx_immutable=False,
                     )
                 except TagMismatch:
                     raise DecodeError(
@@ -2801,6 +2830,7 @@ class OctetString(Obj):
 
     def pps(self, decode_path=()):
         yield _pp(
+            obj=self,
             asn1_type_name=self.asn1_type_name,
             obj_name=self.__class__.__name__,
             decode_path=decode_path,
@@ -2946,6 +2976,7 @@ class Null(Obj):
 
     def pps(self, decode_path=()):
         yield _pp(
+            obj=self,
             asn1_type_name=self.asn1_type_name,
             obj_name=self.__class__.__name__,
             decode_path=decode_path,
@@ -3193,11 +3224,17 @@ class ObjectIdentifier(Obj):
             )
         v, tail = v[:l], v[l:]
         arcs = []
+        ber_encoded = False
         while len(v) > 0:
             i = 0
             arc = 0
             while True:
                 octet = indexbytes(v, i)
+                if i == 0 and octet == 0x80:
+                    if ctx.get("bered", False):
+                        ber_encoded = True
+                    else:
+                        raise DecodeError("non normalized arc encoding")
                 arc = (arc << 7) | (octet & 0x7F)
                 if octet & 0x80 == 0:
                     arcs.append(arc)
@@ -3229,6 +3266,8 @@ class ObjectIdentifier(Obj):
             optional=self.optional,
             _decoded=(offset, llen, l),
         )
+        if ber_encoded:
+            obj.ber_encoded = True
         return obj, tail
 
     def __repr__(self):
@@ -3236,6 +3275,7 @@ class ObjectIdentifier(Obj):
 
     def pps(self, decode_path=()):
         yield _pp(
+            obj=self,
             asn1_type_name=self.asn1_type_name,
             obj_name=self.__class__.__name__,
             decode_path=decode_path,
@@ -3253,6 +3293,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,
+            ber_encoded=self.ber_encoded,
             bered=self.bered,
         )
         for pp in self.pps_lenindef(decode_path):
@@ -3462,6 +3503,7 @@ class CommonString(OctetString):
         if self.ready:
             value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
         yield _pp(
+            obj=self,
             asn1_type_name=self.asn1_type_name,
             obj_name=self.__class__.__name__,
             decode_path=decode_path,
@@ -3694,6 +3736,7 @@ class UTCTime(CommonString):
 
     def pps(self, decode_path=()):
         yield _pp(
+            obj=self,
             asn1_type_name=self.asn1_type_name,
             obj_name=self.__class__.__name__,
             decode_path=decode_path,
@@ -4007,6 +4050,7 @@ class Choice(Obj):
                     decode_path=sub_decode_path,
                     ctx=ctx,
                     tag_only=True,
+                    _ctx_immutable=False,
                 )
             except TagMismatch:
                 continue
@@ -4025,6 +4069,7 @@ class Choice(Obj):
             leavemm=True,
             decode_path=sub_decode_path,
             ctx=ctx,
+            _ctx_immutable=False,
         )
         obj = self.__class__(
             schema=self.specs,
@@ -4044,6 +4089,7 @@ class Choice(Obj):
 
     def pps(self, decode_path=()):
         yield _pp(
+            obj=self,
             asn1_type_name=self.asn1_type_name,
             obj_name=self.__class__.__name__,
             decode_path=decode_path,
@@ -4226,6 +4272,7 @@ class Any(Obj):
                     decode_path=decode_path + (str(chunk_i),),
                     leavemm=True,
                     ctx=ctx,
+                    _ctx_immutable=False,
                 )
                 vlen += chunk.tlvlen
                 sub_offset += chunk.tlvlen
@@ -4270,6 +4317,7 @@ class Any(Obj):
 
     def pps(self, decode_path=()):
         yield _pp(
+            obj=self,
             asn1_type_name=self.asn1_type_name,
             obj_name=self.__class__.__name__,
             decode_path=decode_path,
@@ -4319,8 +4367,7 @@ def get_def_by_path(defines_by_path, sub_decode_path):
 def abs_decode_path(decode_path, rel_path):
     """Create an absolute decode path from current and relative ones
 
-    :param decode_path: current decode path, starting point.
-                        Tuple of strings
+    :param decode_path: current decode path, starting point. Tuple of strings
     :param rel_path: relative path to ``decode_path``. Tuple of strings.
                      If first tuple's element is "/", then treat it as
                      an absolute path, ignoring ``decode_path`` as
@@ -4642,6 +4689,7 @@ class Sequence(Obj):
                     leavemm=True,
                     decode_path=sub_decode_path,
                     ctx=ctx,
+                    _ctx_immutable=False,
                 )
             except TagMismatch:
                 if spec.optional:
@@ -4666,6 +4714,7 @@ class Sequence(Obj):
                             leavemm=True,
                             decode_path=sub_sub_decode_path,
                             ctx=ctx,
+                            _ctx_immutable=False,
                         )
                         if len(defined_tail) > 0:
                             raise DecodeError(
@@ -4685,6 +4734,7 @@ class Sequence(Obj):
                         leavemm=True,
                         decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
                         ctx=ctx,
+                        _ctx_immutable=False,
                     )
                     if len(defined_tail) > 0:
                         raise DecodeError(
@@ -4766,6 +4816,7 @@ class Sequence(Obj):
 
     def pps(self, decode_path=()):
         yield _pp(
+            obj=self,
             asn1_type_name=self.asn1_type_name,
             obj_name=self.__class__.__name__,
             decode_path=decode_path,
@@ -4886,6 +4937,7 @@ class Set(Sequence):
                         decode_path=sub_decode_path,
                         ctx=ctx,
                         tag_only=True,
+                        _ctx_immutable=False,
                     )
                 except TagMismatch:
                     continue
@@ -4902,6 +4954,7 @@ class Set(Sequence):
                 leavemm=True,
                 decode_path=sub_decode_path,
                 ctx=ctx,
+                _ctx_immutable=False,
             )
             value_len = value.fulllen
             if value_prev.tobytes() > v[:value_len].tobytes():
@@ -5211,6 +5264,7 @@ class SequenceOf(Obj):
                 leavemm=True,
                 decode_path=sub_decode_path,
                 ctx=ctx,
+                _ctx_immutable=False,
             )
             value_len = value.fulllen
             if ordering_check:
@@ -5268,6 +5322,7 @@ class SequenceOf(Obj):
 
     def pps(self, decode_path=()):
         yield _pp(
+            obj=self,
             asn1_type_name=self.asn1_type_name,
             obj_name=self.__class__.__name__,
             decode_path=decode_path,