X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=pyderasn.py;h=c2d8f4409ee9f8d7b50a75956ed5d03a72b08db5;hb=525b88f45577a5a63621e6621770752df8101c54;hp=2c3ec76a503d20eb41624fb8a6265f4313d3b94b;hpb=743702634d98df7b29d8cc50ec1834cebbc20548;p=pyderasn.git
diff --git a/pyderasn.py b/pyderasn.py
index 2c3ec76..c2d8f44 100755
--- a/pyderasn.py
+++ b/pyderasn.py
@@ -5,8 +5,7 @@
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
+# published by the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -14,8 +13,7 @@
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
-# License along with this program. If not, see
-# .
+# License along with this program. If not, see .
"""Python ASN.1 DER/BER codec with abstract structures
This library allows you to marshal various structures in ASN.1 DER
@@ -239,6 +237,105 @@ all object ``repr``. But it is easy to write custom formatters.
>>> print(pprint(obj))
0 [1,1, 2] INTEGER -12345
+.. _pprint_example:
+
+Example certificate::
+
+ >>> print(pprint(crt))
+ 0 [1,3,1604] Certificate SEQUENCE
+ 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE
+ 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
+ 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595
+ 18 [1,1, 13] . . signature: 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
+ 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
+ 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF
+ 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF
+ 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE
+ 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6
+ 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY
+ . . . . . . . 13:02:45:53
+ [...]
+ 1461 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
+ 1463 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
+ 1474 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
+ . . . 05:00
+ 1476 [1,2, 129] . signatureValue: BIT STRING 1024 bits
+ . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
+ . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
+ [...]
+
+ Trailing data: 0a
+
+Let's parse that output, human::
+
+ 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
+ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
+ 0 1 2 3 4 5 6 7 8 9 10 11
+
+::
+
+ 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
+ ^ ^ ^ ^ ^ ^ ^ ^
+ 0 2 3 4 5 6 9 10
+
+::
+
+ 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
+ ^ ^ ^ ^ ^ ^ ^ ^ ^
+ 0 2 3 4 5 6 8 9 10
+
+::
+
+ 52-2â B [1,1,1054]â . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
+ ^ ^ ^ ^ ^
+ 12 13 14 9 10
+
+:0:
+ Offset of the object, where its DER/BER encoding begins.
+ Pay attention that it does **not** include explicit tag.
+:1:
+ If explicit tag exists, then this is its length (tag + encoded length).
+:2:
+ Length of object's tag. For example CHOICE does not have its own tag,
+ so it is zero.
+:3:
+ Length of encoded length.
+:4:
+ Length of encoded value.
+:5:
+ Visual indentation to show the depth of object in the hierarchy.
+:6:
+ Object's name inside SEQUENCE/CHOICE.
+:7:
+ If either IMPLICIT or EXPLICIT tag is set, then it will be shown
+ here. "IMPLICIT" is omitted.
+:8:
+ Object's class name, if set. Omitted if it is just an ordinary simple
+ value (like with ``algorithm`` in example above).
+:9:
+ Object's ASN.1 type.
+:10:
+ Object's value, if set. Can consist of multiple words (like OCTET/BIT
+ STRINGs above). We see ``v3`` value in Version, because it is named.
+ ``rdnSequence`` is the choice of CHOICE type.
+:11:
+ Possible other flags like OPTIONAL and DEFAULT, if value equals to the
+ default one, specified in the schema.
+:12:
+ Shows does object contains any kind of BER encoded data (possibly
+ Sequence holding BER-encoded underlying value).
+:13:
+ Only applicable to BER encoded data. Indefinite length encoding mark.
+:14:
+ Only applicable to BER encoded data. If object has BER-specific
+ encoding, then ``BER`` will be shown. It does not depend on indefinite
+ length encoding. ``EOC``, ``BOOLEAN``, ``BIT STRING``, ``OCTET STRING``
+ (and its derivatives), ``SET``, ``SET OF`` could be BERed.
+
+
.. _definedby:
DEFINED BY
@@ -249,6 +346,8 @@ DEFINED BY some previously met ObjectIdentifier. This library provides
ability to specify mapping between some OID and field that must be
decoded with specific specification.
+.. _defines:
+
defines kwarg
_____________
@@ -322,15 +421,15 @@ value must be sequence of following tuples::
where ``decode_path`` is a tuple holding so-called decode path to the
exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
-``defines``, holding exactly the same value as accepted in its keyword
-argument.
+``defines``, holding exactly the same value as accepted in its
+:ref:`keyword argument `.
For example, again for CMS, you want to automatically decode
``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
structures it may hold. Also, automatically decode ``controlSequence``
of ``PKIResponse``::
- content_info, tail = ContentInfo().decode(data, defines_by_path=(
+ content_info, tail = ContentInfo().decode(data, ctx={"defines_by_path": (
(
("contentType",),
((("content",), {id_signedData: SignedData()}),),
@@ -365,7 +464,7 @@ of ``PKIResponse``::
id_cmc_transactionId: TransactionId(),
})),
),
- ))
+ )})
Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
First function is useful for path construction when some automatic
@@ -463,6 +562,10 @@ NumericString
_____________
.. autoclass:: pyderasn.NumericString
+PrintableString
+_______________
+.. autoclass:: pyderasn.PrintableString
+
UTCTime
_______
.. autoclass:: pyderasn.UTCTime
@@ -567,7 +670,7 @@ from six.moves import xrange as six_xrange
try:
from termcolor import colored
except ImportError: # pragma: no cover
- def colored(what, *args):
+ def colored(what, *args, **kwargs):
return what
@@ -1332,7 +1435,7 @@ def colonize_hex(hexed):
def pp_console_row(
pp,
- oids=None,
+ oid_maps=(),
with_offsets=False,
with_blob=True,
with_colours=False,
@@ -1367,14 +1470,18 @@ def pp_console_row(
if isinstance(ent, DecodePathDefBy):
cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
value = str(ent.defined_by)
+ oid_name = None
if (
- oids is not None and
+ len(oid_maps) > 0 and
ent.defined_by.asn1_type_name ==
- ObjectIdentifier.asn1_type_name and
- value in oids
+ ObjectIdentifier.asn1_type_name
):
- cols.append(_colourize("%s:" % oids[value], "green", with_colours))
- else:
+ for oid_map in oid_maps:
+ oid_name = oid_map.get(value)
+ if oid_name is not None:
+ cols.append(_colourize("%s:" % oid_name, "green", with_colours))
+ break
+ if oid_name is None:
cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
else:
cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
@@ -1395,11 +1502,14 @@ def pp_console_row(
value = pp.value
cols.append(_colourize(value, "white", with_colours, ("reverse",)))
if (
- oids is not None and
- pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
- value in oids
+ len(oid_maps) > 0 and
+ pp.asn1_type_name == ObjectIdentifier.asn1_type_name
):
- cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
+ for oid_map in oid_maps:
+ oid_name = oid_map.get(value)
+ if oid_name is not None:
+ cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
+ break
if pp.asn1_type_name == Integer.asn1_type_name:
hex_repr = hex(int(pp.obj._value))[2:].upper()
if len(hex_repr) % 2 != 0:
@@ -1443,7 +1553,7 @@ def pp_console_blob(pp, decode_path_len_decrease=0):
def pprint(
obj,
- oids=None,
+ oid_maps=(),
big_blobs=False,
with_colours=False,
with_decode_path=False,
@@ -1452,8 +1562,9 @@ def pprint(
"""Pretty print object
:param Obj obj: object you want to pretty print
- :param oids: ``OID <-> humand readable string`` dictionary. When OID
- from it is met, then its humand readable form is printed
+ :param oid_maps: list of ``OID <-> humand readable string`` dictionary.
+ When OID from it is met, then its humand readable form
+ is printed
:param big_blobs: if large binary objects are met (like OctetString
values), do we need to print them too, on separate
lines
@@ -1475,7 +1586,7 @@ def pprint(
if big_blobs:
yield pp_console_row(
pp,
- oids=oids,
+ oid_maps=oid_maps,
with_offsets=True,
with_blob=False,
with_colours=with_colours,
@@ -1490,7 +1601,7 @@ def pprint(
else:
yield pp_console_row(
pp,
- oids=oids,
+ oid_maps=oid_maps,
with_offsets=True,
with_blob=True,
with_colours=with_colours,
@@ -3579,7 +3690,7 @@ class NumericString(AllowableCharsMixin, CommonString):
be stored.
>>> NumericString().allowable_chars
- set(['3', '4', '7', '5', '1', '0', '8', '9', ' ', '6', '2'])
+ frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
"""
__slots__ = ()
tag_default = tag_encode(18)
@@ -3600,7 +3711,7 @@ class PrintableString(AllowableCharsMixin, CommonString):
Its value is properly sanitized: see X.680 41.4 table 10.
>>> PrintableString().allowable_chars
- >>> set([' ', "'", ..., 'z'])
+ frozenset([' ', "'", ..., 'z'])
"""
__slots__ = ()
tag_default = tag_encode(19)
@@ -3661,6 +3772,10 @@ class UTCTime(CommonString):
datetime.datetime(2017, 9, 30, 22, 7, 50)
>>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
datetime.datetime(1957, 9, 30, 22, 7, 50)
+
+ .. warning::
+
+ BER encoding is unsupported.
"""
__slots__ = ()
tag_default = tag_encode(23)
@@ -3725,7 +3840,7 @@ class UTCTime(CommonString):
try:
value_decoded = value.decode("ascii")
except (UnicodeEncodeError, UnicodeDecodeError) as err:
- raise DecodeError("invalid UTCTime encoding")
+ raise DecodeError("invalid UTCTime encoding: %r" % err)
try:
self._strptime(value_decoded)
except (TypeError, ValueError) as err:
@@ -3812,6 +3927,10 @@ class GeneralizedTime(UTCTime):
>>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
+ .. warning::
+
+ BER encoding is unsupported.
+
.. warning::
Only microsecond fractions are supported.
@@ -3866,7 +3985,7 @@ class GeneralizedTime(UTCTime):
try:
value_decoded = value.decode("ascii")
except (UnicodeEncodeError, UnicodeDecodeError) as err:
- raise DecodeError("invalid GeneralizedTime encoding")
+ raise DecodeError("invalid GeneralizedTime encoding: %r" % err)
try:
self._strptime(value_decoded)
except (TypeError, ValueError) as err:
@@ -5493,7 +5612,7 @@ def generic_decoder(): # pragma: no cover
def pprint_any(
obj,
- oids=None,
+ oid_maps=(),
with_colours=False,
with_decode_path=False,
decode_path_only=(),
@@ -5513,7 +5632,7 @@ def generic_decoder(): # pragma: no cover
pp = _pp(**pp_kwargs)
yield pp_console_row(
pp,
- oids=oids,
+ oid_maps=oid_maps,
with_offsets=True,
with_blob=False,
with_colours=with_colours,
@@ -5543,7 +5662,7 @@ def main(): # pragma: no cover
)
parser.add_argument(
"--oids",
- help="Python path to dictionary with OIDs",
+ help="Python paths to dictionary with OIDs, comma separated",
)
parser.add_argument(
"--schema",
@@ -5581,7 +5700,10 @@ def main(): # pragma: no cover
args.DERFile.seek(args.skip)
der = memoryview(args.DERFile.read())
args.DERFile.close()
- oids = obj_by_path(args.oids) if args.oids else {}
+ oid_maps = (
+ [obj_by_path(_path) for _path in (args.oids or "").split(",")]
+ if args.oids else ()
+ )
if args.schema:
schema = obj_by_path(args.schema)
from functools import partial
@@ -5597,7 +5719,7 @@ def main(): # pragma: no cover
obj, tail = schema().decode(der, ctx=ctx)
print(pprinter(
obj,
- oids=oids,
+ oid_maps=oid_maps,
with_colours=True if environ.get("NO_COLOR") is None else False,
with_decode_path=args.print_decode_path,
decode_path_only=(