]> Cypherpunks.ru repositories - pyderasn.git/commitdiff
Decode path printing
authorSergey Matveev <stargrave@stargrave.org>
Tue, 7 Aug 2018 20:01:01 +0000 (23:01 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Tue, 7 Aug 2018 20:01:01 +0000 (23:01 +0300)
VERSION
doc/examples.rst
doc/install.rst
doc/news.rst
pyderasn.py

diff --git a/VERSION b/VERSION
index e4fba2183587225f216eeada4c78dfab6b2e65f5..24ee5b1be9961e38a503c8e764b7385dbb6ba124 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.12
+3.13
index f5b58d9a1dabcec2a385927ed4dd0399d0f4d7c1..25e2cf4a2e740b04fde0bb4fbb8b9339697518b0 100644 (file)
@@ -321,6 +321,9 @@ good enough for the certificate above::
                         . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
     [...]
 
+Human readable OIDs
+___________________
+
 If you have got dictionaries with ObjectIdentifiers, like example one
 from ``tests/test_crts.py``::
 
@@ -350,6 +353,41 @@ then you can pass it to pretty printer to see human readable OIDs::
        79   [1,1,   9]  . . . . . . . . . . >: PrintableString PrintableString Barcelona
     [...]
 
+Decode paths
+____________
+
+Each decoded element has so-called decode path: sequence of structure
+names it is passing during the decode process. Each element has its own
+unique path inside the whole ASN.1 tree. You can print it out with
+``--print-decode-path`` option::
+
+    % python -m pyderasn --schema path.to:Certificate --print-decode-path path/to/file
+       0    [1,3,1604]  Certificate SEQUENCE []
+       4    [1,3,1453]   . tbsCertificate: TBSCertificate SEQUENCE [tbsCertificate]
+      10-2  [1,1,   1]   . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL [tbsCertificate:version]
+      13    [1,1,   3]   . . serialNumber: CertificateSerialNumber INTEGER 61595 [tbsCertificate:serialNumber]
+      18    [1,1,  13]   . . signature: AlgorithmIdentifier SEQUENCE [tbsCertificate:signature]
+      20    [1,1,   9]   . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 [tbsCertificate:signature:algorithm]
+      31    [0,0,   2]   . . . parameters: [UNIV 5] ANY OPTIONAL [tbsCertificate:signature:parameters]
+                         . . . . 05:00
+      33    [0,0, 278]   . . issuer: Name CHOICE rdnSequence [tbsCertificate:issuer]
+      33    [1,3, 274]   . . . rdnSequence: RDNSequence SEQUENCE OF [tbsCertificate:issuer:rdnSequence]
+      37    [1,1,  11]   . . . . 0: RelativeDistinguishedName SET OF [tbsCertificate:issuer:rdnSequence:0]
+      39    [1,1,   9]   . . . . . 0: AttributeTypeAndValue SEQUENCE [tbsCertificate:issuer:rdnSequence:0:0]
+      41    [1,1,   3]   . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6 [tbsCertificate:issuer:rdnSequence:0:0:type]
+      46    [0,0,   4]   . . . . . . value: [UNIV 19] AttributeValue ANY [tbsCertificate:issuer:rdnSequence:0:0:value]
+                         . . . . . . . 13:02:45:53
+      46    [1,1,   2]   . . . . . . . DEFINED BY 2.5.4.6: CountryName PrintableString ES [tbsCertificate:issuer:rdnSequence:0:0:value:DEFINED BY 2.5.4.6]
+    [...]
+
+Now you can print only the specified tree, for example signature algorithm::
+
+    % python -m pyderasn --schema path.to:Certificate --decode-path-only tbsCertificate:signature path/to/file
+      18    [1,1,  13]  AlgorithmIdentifier SEQUENCE
+      20    [1,1,   9]   . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
+      31    [0,0,   2]   . parameters: [UNIV 5] ANY OPTIONAL
+                         . . 05:00
+
 Descriptive errors
 ------------------
 
@@ -358,13 +396,13 @@ If you have bad DER/BER, then errors will show you where error occurred::
     % python -m pyderasn --schema tests.test_crts:Certificate path/to/bad/file
     Traceback (most recent call last):
     [...]
-    pyderasn.DecodeError: UTCTime (tbsCertificate.validity.notAfter.utcTime) (at 328) invalid UTCTime format
+    pyderasn.DecodeError: UTCTime (tbsCertificate:validity:notAfter:utcTime) (at 328) invalid UTCTime format
 
 ::
 
     % python -m pyderasn path/to/bad/file
     [...]
-    pyderasn.DecodeError: UTCTime (0.SequenceOf.4.SequenceOf.1.UTCTime) (at 328) invalid UTCTime format
+    pyderasn.DecodeError: UTCTime (0:SequenceOf:4:SequenceOf:1:UTCTime) (at 328) invalid UTCTime format
 
 You can see, so called, decode path inside the structures:
 ``tbsCertificate`` -> ``validity`` -> ``notAfter`` -> ``utcTime`` and
index 2e62b09fe0769ca4153983d4453200d5aa55989b..7ade15a48dba2b93fc2987247e3fb8391509885f 100644 (file)
@@ -4,11 +4,11 @@ Install
 Preferable way is to :ref:`download <download>` tarball with the
 signature from `official website <http://pyderasn.cypherpunks.ru/>`__::
 
-    % wget http://pyderasn.cypherpunks.ru/pyderasn-3.7.tar.xz
-    % wget http://pyderasn.cypherpunks.ru/pyderasn-3.7.tar.xz.sig
-    % gpg --verify pyderasn-3.7.tar.xz.sig pyderasn-3.7.tar.xz
-    % xz -d < pyderasn-3.7.tar.xz | tar xf -
-    % cd pyderasn-3.7
+    % wget http://pyderasn.cypherpunks.ru/pyderasn-3.13.tar.xz
+    % wget http://pyderasn.cypherpunks.ru/pyderasn-3.13.tar.xz.sig
+    % gpg --verify pyderasn-3.13.tar.xz.sig pyderasn-3.13.tar.xz
+    % xz -d < pyderasn-3.13.tar.xz | tar xf -
+    % cd pyderasn-3.13
     % python setup.py install
     # or copy pyderasn.py (+six.py, possibly termcolor.py) to your PYTHONPATH
 
index 7e5d52bd7888bd1b9518f43ab884995c75a3246e..6e2427c90021bb4c7913d42977c29b5a8365212d 100644 (file)
@@ -1,6 +1,16 @@
 News
 ====
 
+.. _release3.13:
+
+3.13
+----
+* DecodeError's decode paths are separated with ``:``, instead of ``.``,
+  because of colliding with dots in OIDs
+* Ability to print element decode paths with ``--print-decode-path``
+  command line option (and corresponding keyword argument)
+* Ability to print tree's branch specified with ``--decode-path-only``
+
 .. _release3.12:
 
 3.12
index 3f0e8caec4e8e33ca804dd34d1cf95656fe50d97..3b7d6cc75dcd5398cd5d440ee742fa26c9a4986c 100755 (executable)
@@ -274,7 +274,7 @@ You can specify multiple fields, that will be autodecoded -- that is why
 ``defines`` kwarg is a sequence. You can specify defined field
 relatively or absolutely to current decode path. For example ``defines``
 for AlgorithmIdentifier of X.509's
-``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
+``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
 
         (
             (("parameters",), {
@@ -302,8 +302,7 @@ Following types can be automatically decoded (DEFINED BY):
 When any of those fields is automatically decoded, then ``.defined``
 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
 was defined, ``value`` contains corresponding decoded value. For example
-above, ``content_info["content"].defined == (id_signedData,
-signed_data)``.
+above, ``content_info["content"].defined == (id_signedData, signed_data)``.
 
 .. _defines_by_path_ctx:
 
@@ -641,7 +640,7 @@ class DecodeError(Exception):
             c for c in (
                 "" if self.klass is None else self.klass.__name__,
                 (
-                    ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
+                    ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
                     if len(self.decode_path) > 0 else ""
                 ),
                 ("(at %d)" % self.offset) if self.offset > 0 else "",
@@ -1268,6 +1267,8 @@ def pp_console_row(
         with_offsets=False,
         with_blob=True,
         with_colours=False,
+        with_decode_path=False,
+        decode_path_len_decrease=0,
 ):
     cols = []
     if with_offsets:
@@ -1288,8 +1289,9 @@ def pp_console_row(
         )
         col = _colourize(col, "green", with_colours, ())
         cols.append(col)
-    if len(pp.decode_path) > 0:
-        cols.append(" ." * (len(pp.decode_path)))
+    decode_path_len = len(pp.decode_path) - decode_path_len_decrease
+    if decode_path_len > 0:
+        cols.append(" ." * decode_path_len)
         ent = pp.decode_path[-1]
         if isinstance(ent, DecodePathDefBy):
             cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
@@ -1336,13 +1338,20 @@ def pp_console_row(
         cols.append(_colourize("OPTIONAL", "red", with_colours))
     if pp.default:
         cols.append(_colourize("DEFAULT", "red", with_colours))
+    if with_decode_path:
+        cols.append(_colourize(
+            "[%s]" % ":".join(str(p) for p in pp.decode_path),
+            "grey",
+            with_colours,
+        ))
     return " ".join(cols)
 
 
-def pp_console_blob(pp):
+def pp_console_blob(pp, decode_path_len_decrease=0):
     cols = [" " * len("XXXXXYYZ [X,X,XXXX]Z")]
-    if len(pp.decode_path) > 0:
-        cols.append(" ." * (len(pp.decode_path) + 1))
+    decode_path_len = len(pp.decode_path) - decode_path_len_decrease
+    if decode_path_len > 0:
+        cols.append(" ." * (decode_path_len + 1))
     if isinstance(pp.blob, binary_type):
         blob = hexenc(pp.blob).upper()
         for i in range(0, len(blob), 32):
@@ -1354,7 +1363,14 @@ def pp_console_blob(pp):
         yield " ".join(cols + [", ".join(pp.blob)])
 
 
-def pprint(obj, oids=None, big_blobs=False, with_colours=False):
+def pprint(
+        obj,
+        oids=None,
+        big_blobs=False,
+        with_colours=False,
+        with_decode_path=False,
+        decode_path_only=(),
+):
     """Pretty print object
 
     :param Obj obj: object you want to pretty print
@@ -1365,10 +1381,19 @@ def pprint(obj, oids=None, big_blobs=False, with_colours=False):
                       lines
     :param with_colours: colourize output, if ``termcolor`` library
                          is available
+    :param with_decode_path: print decode path
+    :param decode_path_only: print only that specified decode path
     """
     def _pprint_pps(pps):
         for pp in pps:
             if hasattr(pp, "_fields"):
+                if (
+                    decode_path_only != () and
+                    tuple(
+                        str(p) for p in pp.decode_path[:len(decode_path_only)]
+                    ) != decode_path_only
+                ):
+                    continue
                 if big_blobs:
                     yield pp_console_row(
                         pp,
@@ -1376,8 +1401,13 @@ def pprint(obj, oids=None, big_blobs=False, with_colours=False):
                         with_offsets=True,
                         with_blob=False,
                         with_colours=with_colours,
+                        with_decode_path=with_decode_path,
+                        decode_path_len_decrease=len(decode_path_only),
                     )
-                    for row in pp_console_blob(pp):
+                    for row in pp_console_blob(
+                        pp,
+                        decode_path_len_decrease=len(decode_path_only),
+                    ):
                         yield row
                 else:
                     yield pp_console_row(
@@ -1386,6 +1416,8 @@ def pprint(obj, oids=None, big_blobs=False, with_colours=False):
                         with_offsets=True,
                         with_blob=True,
                         with_colours=with_colours,
+                        with_decode_path=with_decode_path,
+                        decode_path_len_decrease=len(decode_path_only),
                     )
             else:
                 for row in _pprint_pps(pp):
@@ -5138,10 +5170,21 @@ def generic_decoder():  # pragma: no cover
         __slots__ = ()
         schema = choice
 
-    def pprint_any(obj, oids=None, with_colours=False):
+    def pprint_any(
+            obj,
+            oids=None,
+            with_colours=False,
+            with_decode_path=False,
+            decode_path_only=(),
+    ):
         def _pprint_pps(pps):
             for pp in pps:
                 if hasattr(pp, "_fields"):
+                    if (
+                        decode_path_only != () and
+                        pp.decode_path[:len(decode_path_only)] != decode_path_only
+                    ):
+                        continue
                     if pp.asn1_type_name == Choice.asn1_type_name:
                         continue
                     pp_kwargs = pp._asdict()
@@ -5153,8 +5196,13 @@ def generic_decoder():  # pragma: no cover
                         with_offsets=True,
                         with_blob=False,
                         with_colours=with_colours,
+                        with_decode_path=with_decode_path,
+                        decode_path_len_decrease=len(decode_path_only),
                     )
-                    for row in pp_console_blob(pp):
+                    for row in pp_console_blob(
+                        pp,
+                        decode_path_len_decrease=len(decode_path_only),
+                    ):
                         yield row
                 else:
                     for row in _pprint_pps(pp):
@@ -5189,6 +5237,15 @@ def main():  # pragma: no cover
         action='store_true',
         help="Disallow BER encoding",
     )
+    parser.add_argument(
+        "--print-decode-path",
+        action='store_true',
+        help="Print decode paths",
+    )
+    parser.add_argument(
+        "--decode-path-only",
+        help="Print only specified decode path",
+    )
     parser.add_argument(
         "DERFile",
         type=argparse.FileType("rb"),
@@ -5213,6 +5270,11 @@ def main():  # pragma: no cover
         obj,
         oids=oids,
         with_colours=True if environ.get("NO_COLOR") is None else False,
+        with_decode_path=args.print_decode_path,
+        decode_path_only=(
+            () if args.decode_path_only is None else
+            tuple(args.decode_path_only.split(":"))
+        ),
     ))
     if tail != b"":
         print("\nTrailing data: %s" % hexenc(tail))