3 # PyDERASN -- Python ASN.1 DER codec with abstract structures
4 # Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 """Python ASN.1 DER codec with abstract structures
21 This library allows you to marshal and unmarshal various structures in
22 ASN.1 DER format, like this:
26 >>> Integer().decode(raw) == i
29 There are primitive types, holding single values
30 (:py:class:`pyderasn.BitString`,
31 :py:class:`pyderasn.Boolean`,
32 :py:class:`pyderasn.Enumerated`,
33 :py:class:`pyderasn.GeneralizedTime`,
34 :py:class:`pyderasn.Integer`,
35 :py:class:`pyderasn.Null`,
36 :py:class:`pyderasn.ObjectIdentifier`,
37 :py:class:`pyderasn.OctetString`,
38 :py:class:`pyderasn.UTCTime`,
39 :py:class:`various strings <pyderasn.CommonString>`
40 (:py:class:`pyderasn.BMPString`,
41 :py:class:`pyderasn.GeneralString`,
42 :py:class:`pyderasn.GraphicString`,
43 :py:class:`pyderasn.IA5String`,
44 :py:class:`pyderasn.ISO646String`,
45 :py:class:`pyderasn.NumericString`,
46 :py:class:`pyderasn.PrintableString`,
47 :py:class:`pyderasn.T61String`,
48 :py:class:`pyderasn.TeletexString`,
49 :py:class:`pyderasn.UniversalString`,
50 :py:class:`pyderasn.UTF8String`,
51 :py:class:`pyderasn.VideotexString`,
52 :py:class:`pyderasn.VisibleString`)),
53 constructed types, holding multiple primitive types
54 (:py:class:`pyderasn.Sequence`,
55 :py:class:`pyderasn.SequenceOf`,
56 :py:class:`pyderasn.Set`,
57 :py:class:`pyderasn.SetOf`),
58 and special types like
59 :py:class:`pyderasn.Any` and
60 :py:class:`pyderasn.Choice`.
68 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
69 the default tag used during coding process. You can override it with
70 either ``IMPLICIT`` (using ``impl`` keyword argument), or
71 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments take
72 raw binary string, containing that tag. You can **not** set implicit and
73 explicit tags simultaneously.
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
78 number. Pay attention that explicit tags always have *constructed* tag
79 (``tag_ctxc``), but implicit tags for primitive types are primitive
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two objects of the same type, but with different implicit/explicit tags
94 You can get object's effective tag (either default or implicited) through
95 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
112 Many objects in sequences could be ``OPTIONAL`` and could have
113 ``DEFAULT`` value. You can specify that object's property using
114 corresponding keyword arguments.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
119 Those specifications do not play any role in primitive value encoding,
120 but are taken into account when dealing with sequences holding them. For
121 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
143 Some objects give ability to set value size constraints. This is either
144 possible integer value, or allowed length of various strings and
145 sequences. Constraints are set in the following way::
150 And values satisfaction is checked as: ``MIN <= X <= MAX``.
152 For simplicity you can also set bounds the following way::
154 bounded_x = X(bounds=(MIN, MAX))
156 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
162 All objects have ``ready`` boolean property, that tells if object is
163 ready to be encoded. If that kind of action is performed on unready
164 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
166 All objects have ``copy()`` method, that returns their copy, that can be
174 Decoding is performed using ``decode()`` method. ``offset`` optional
175 argument could be used to set initial object's offset in the binary
176 data, for convenience. It returns decoded object and remaining
177 unmarshalled data (tail). Internally all work is done on
178 ``memoryview(data)``, and you can leave returning tail as a memoryview,
179 by specifying ``leavemm=True`` argument.
181 When object is decoded, ``decoded`` property is true and you can safely
182 use following properties:
184 * ``offset`` -- position including initial offset where object's tag starts
185 * ``tlen`` -- length of object's tag
186 * ``llen`` -- length of object's length value
187 * ``vlen`` -- length of object's value
188 * ``tlvlen`` -- length of the whole object
190 Pay attention that those values do **not** include anything related to
191 explicit tag. If you want to know information about it, then use:
192 ``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
193 lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
194 (that actually equals to ordinary ``tlvlen``).
196 When error occurs, then :py:exc:`pyderasn.DecodeError` is raised.
203 You can specify so called context keyword argument during ``decode()``
204 invocation. It is dictionary containing various options governing
207 Currently available context options:
209 * :ref:`defines_by_path <defines_by_path_ctx>`
210 * :ref:`strict_default_existence <strict_default_existence_ctx>`
217 All objects have ``pps()`` method, that is a generator of
218 :py:class:`pyderasn.PP` namedtuple, holding various raw information
219 about the object. If ``pps`` is called on sequences, then all underlying
220 ``PP`` will be yielded.
222 You can use :py:func:`pyderasn.pp_console_row` function, converting
223 those ``PP`` to human readable string. Actually exactly it is used for
224 all object ``repr``. But it is easy to write custom formatters.
226 >>> from pyderasn import pprint
227 >>> encoded = Integer(-12345).encode()
228 >>> obj, tail = Integer().decode(encoded)
229 >>> print(pprint(obj))
230 0 [1,1, 2] INTEGER -12345
237 ASN.1 structures often have ANY and OCTET STRING fields, that are
238 DEFINED BY some previously met ObjectIdentifier. This library provides
239 ability to specify mapping between some OID and field that must be
240 decoded with specific specification.
245 :py:class:`pyderasn.ObjectIdentifier` field inside
246 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
247 necessary for decoding structures. For example, CMS (:rfc:`5652`)
250 class ContentInfo(Sequence):
252 ("contentType", ContentType(defines=((("content",), {
253 id_digestedData: DigestedData(),
254 id_signedData: SignedData(),
256 ("content", Any(expl=tag_ctxc(0))),
259 ``contentType`` field tells that it defines that ``content`` must be
260 decoded with ``SignedData`` specification, if ``contentType`` equals to
261 ``id-signedData``. The same applies to ``DigestedData``. If
262 ``contentType`` contains unknown OID, then no automatic decoding is
265 You can specify multiple fields, that will be autodecoded -- that is why
266 ``defines`` kwarg is a sequence. You can specify defined field
267 relatively or absolutely to current decode path. For example ``defines``
268 for AlgorithmIdentifier of X.509's
269 ``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
273 id_ecPublicKey: ECParameters(),
274 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
276 (('..', 'subjectPublicKey'), {
277 id_rsaEncryption: RSAPublicKey(),
278 id_GostR3410_2001: OctetString(),
282 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
283 autodecode its parameters inside SPKI's algorithm and its public key
286 Following types can be automatically decoded (DEFINED BY):
288 * :py:class:`pyderasn.Any`
289 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
290 * :py:class:`pyderasn.OctetString`
291 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
292 ``Any``/``OctetString``-s
294 When any of those fields is automatically decoded, then ``.defined``
295 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
296 was defined, ``value`` contains corresponding decoded value. For example
297 above, ``content_info["content"].defined == (id_signedData,
300 .. _defines_by_path_ctx:
302 defines_by_path context option
303 ______________________________
305 Sometimes you either can not or do not want to explicitly set *defines*
306 in the scheme. You can dynamically apply those definitions when calling
307 ``.decode()`` method.
309 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
310 value must be sequence of following tuples::
312 (decode_path, defines)
314 where ``decode_path`` is a tuple holding so-called decode path to the
315 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
316 ``defines``, holding exactly the same value as accepted in its keyword
319 For example, again for CMS, you want to automatically decode
320 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
321 structures it may hold. Also, automatically decode ``controlSequence``
324 content_info, tail = ContentInfo().decode(data, defines_by_path=(
327 ((("content",), {id_signedData: SignedData()}),),
332 decode_path_defby(id_signedData),
337 id_cct_PKIData: PKIData(),
338 id_cct_PKIResponse: PKIResponse(),
344 decode_path_defby(id_signedData),
347 decode_path_defby(id_cct_PKIResponse),
353 id_cmc_recipientNonce: RecipientNonce(),
354 id_cmc_senderNonce: SenderNonce(),
355 id_cmc_statusInfoV2: CMCStatusInfoV2(),
356 id_cmc_transactionId: TransactionId(),
361 Pay attention for :py:func:`pyderasn.decode_path_defby` and ``any``.
362 First function is useful for path construction when some automatic
363 decoding is already done. ``any`` means literally any value it meet --
364 useful for SEQUENCE/SET OF-s.
371 .. autoclass:: pyderasn.Boolean
376 .. autoclass:: pyderasn.Integer
381 .. autoclass:: pyderasn.BitString
386 .. autoclass:: pyderasn.OctetString
391 .. autoclass:: pyderasn.Null
396 .. autoclass:: pyderasn.ObjectIdentifier
401 .. autoclass:: pyderasn.Enumerated
405 .. autoclass:: pyderasn.CommonString
409 .. autoclass:: pyderasn.UTCTime
410 :members: __init__, todatetime
414 .. autoclass:: pyderasn.GeneralizedTime
421 .. autoclass:: pyderasn.Choice
426 .. autoclass:: PrimitiveTypes
430 .. autoclass:: pyderasn.Any
438 .. autoclass:: pyderasn.Sequence
443 .. autoclass:: pyderasn.Set
448 .. autoclass:: pyderasn.SequenceOf
453 .. autoclass:: pyderasn.SetOf
459 .. autofunction:: pyderasn.abs_decode_path
460 .. autofunction:: pyderasn.hexenc
461 .. autofunction:: pyderasn.hexdec
462 .. autofunction:: pyderasn.tag_encode
463 .. autofunction:: pyderasn.tag_decode
464 .. autofunction:: pyderasn.tag_ctxp
465 .. autofunction:: pyderasn.tag_ctxc
466 .. autoclass:: pyderasn.Obj
469 from codecs import getdecoder
470 from codecs import getencoder
471 from collections import namedtuple
472 from collections import OrderedDict
473 from datetime import datetime
474 from math import ceil
476 from six import add_metaclass
477 from six import binary_type
478 from six import byte2int
479 from six import indexbytes
480 from six import int2byte
481 from six import integer_types
482 from six import iterbytes
484 from six import string_types
485 from six import text_type
486 from six.moves import xrange as six_xrange
528 "TagClassApplication",
532 "TagFormConstructed",
543 TagClassUniversal = 0
544 TagClassApplication = 1 << 6
545 TagClassContext = 1 << 7
546 TagClassPrivate = 1 << 6 | 1 << 7
548 TagFormConstructed = 1 << 5
551 TagClassApplication: "APPLICATION ",
552 TagClassPrivate: "PRIVATE ",
553 TagClassUniversal: "UNIV ",
557 ########################################################################
559 ########################################################################
561 class DecodeError(Exception):
562 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
564 :param str msg: reason of decode failing
565 :param klass: optional exact DecodeError inherited class (like
566 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
567 :py:exc:`InvalidLength`)
568 :param decode_path: tuple of strings. It contains human
569 readable names of the fields through which
570 decoding process has passed
571 :param int offset: binary offset where failure happened
573 super(DecodeError, self).__init__()
576 self.decode_path = decode_path
582 "" if self.klass is None else self.klass.__name__,
584 ("(%s)" % ".".join(self.decode_path))
585 if len(self.decode_path) > 0 else ""
587 ("(at %d)" % self.offset) if self.offset > 0 else "",
593 return "%s(%s)" % (self.__class__.__name__, self)
596 class NotEnoughData(DecodeError):
600 class TagMismatch(DecodeError):
604 class InvalidLength(DecodeError):
608 class InvalidOID(DecodeError):
612 class ObjUnknown(ValueError):
613 def __init__(self, name):
614 super(ObjUnknown, self).__init__()
618 return "object is unknown: %s" % self.name
621 return "%s(%s)" % (self.__class__.__name__, self)
624 class ObjNotReady(ValueError):
625 def __init__(self, name):
626 super(ObjNotReady, self).__init__()
630 return "object is not ready: %s" % self.name
633 return "%s(%s)" % (self.__class__.__name__, self)
636 class InvalidValueType(ValueError):
637 def __init__(self, expected_types):
638 super(InvalidValueType, self).__init__()
639 self.expected_types = expected_types
642 return "invalid value type, expected: %s" % ", ".join(
643 [repr(t) for t in self.expected_types]
647 return "%s(%s)" % (self.__class__.__name__, self)
650 class BoundsError(ValueError):
651 def __init__(self, bound_min, value, bound_max):
652 super(BoundsError, self).__init__()
653 self.bound_min = bound_min
655 self.bound_max = bound_max
658 return "unsatisfied bounds: %s <= %s <= %s" % (
665 return "%s(%s)" % (self.__class__.__name__, self)
668 ########################################################################
670 ########################################################################
672 _hexdecoder = getdecoder("hex")
673 _hexencoder = getencoder("hex")
677 """Binary data to hexadecimal string convert
679 return _hexdecoder(data)[0]
683 """Hexadecimal string to binary data convert
685 return _hexencoder(data)[0].decode("ascii")
688 def int_bytes_len(num, byte_len=8):
691 return int(ceil(float(num.bit_length()) / byte_len))
694 def zero_ended_encode(num):
695 octets = bytearray(int_bytes_len(num, 7))
697 octets[i] = num & 0x7F
701 octets[i] = 0x80 | (num & 0x7F)
707 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
708 """Encode tag to binary form
710 :param int num: tag's number
711 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
712 :py:data:`pyderasn.TagClassContext`,
713 :py:data:`pyderasn.TagClassApplication`,
714 :py:data:`pyderasn.TagClassPrivate`)
715 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
716 :py:data:`pyderasn.TagFormConstructed`)
720 return int2byte(klass | form | num)
721 # [XX|X|11111][1.......][1.......] ... [0.......]
722 return int2byte(klass | form | 31) + zero_ended_encode(num)
726 """Decode tag from binary form
730 No validation is performed, assuming that it has already passed.
732 It returns tuple with three integers, as
733 :py:func:`pyderasn.tag_encode` accepts.
735 first_octet = byte2int(tag)
736 klass = first_octet & 0xC0
737 form = first_octet & 0x20
738 if first_octet & 0x1F < 0x1F:
739 return (klass, form, first_octet & 0x1F)
741 for octet in iterbytes(tag[1:]):
744 return (klass, form, num)
748 """Create CONTEXT PRIMITIVE tag
750 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
754 """Create CONTEXT CONSTRUCTED tag
756 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
760 """Take off tag from the data
762 :returns: (encoded tag, tag length, remaining data)
765 raise NotEnoughData("no data at all")
766 if byte2int(data) & 0x1F < 31:
767 return data[:1], 1, data[1:]
772 raise DecodeError("unfinished tag")
773 if indexbytes(data, i) & 0x80 == 0:
776 return data[:i], i, data[i:]
782 octets = bytearray(int_bytes_len(l) + 1)
783 octets[0] = 0x80 | (len(octets) - 1)
784 for i in six_xrange(len(octets) - 1, 0, -1):
790 def len_decode(data):
792 raise NotEnoughData("no data at all")
793 first_octet = byte2int(data)
794 if first_octet & 0x80 == 0:
795 return first_octet, 1, data[1:]
796 octets_num = first_octet & 0x7F
797 if octets_num + 1 > len(data):
798 raise NotEnoughData("encoded length is longer than data")
800 raise DecodeError("long form instead of short one")
801 if byte2int(data[1:]) == 0:
802 raise DecodeError("leading zeros")
804 for v in iterbytes(data[1:1 + octets_num]):
807 raise DecodeError("long form instead of short one")
808 return l, 1 + octets_num, data[1 + octets_num:]
811 ########################################################################
813 ########################################################################
815 class AutoAddSlots(type):
816 def __new__(mcs, name, bases, _dict):
817 _dict["__slots__"] = _dict.get("__slots__", ())
818 return type.__new__(mcs, name, bases, _dict)
821 @add_metaclass(AutoAddSlots)
823 """Common ASN.1 object class
825 All ASN.1 types are inherited from it. It has metaclass that
826 automatically adds ``__slots__`` to all inherited classes.
847 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
848 self._expl = getattr(self, "expl", None) if expl is None else expl
849 if self.tag != self.tag_default and self._expl is not None:
851 "implicit and explicit tags can not be set simultaneously"
853 if default is not None:
855 self.optional = optional
856 self.offset, self.llen, self.vlen = _decoded
860 def ready(self): # pragma: no cover
861 """Is object ready to be encoded?
863 raise NotImplementedError()
865 def _assert_ready(self):
867 raise ObjNotReady(self.__class__.__name__)
871 """Is object decoded?
873 return (self.llen + self.vlen) > 0
875 def copy(self): # pragma: no cover
876 """Make a copy of object, safe to be mutated
878 raise NotImplementedError()
886 return self.tlen + self.llen + self.vlen
888 def __str__(self): # pragma: no cover
889 return self.__bytes__() if PY2 else self.__unicode__()
891 def __ne__(self, their):
892 return not(self == their)
894 def __gt__(self, their): # pragma: no cover
895 return not(self < their)
897 def __le__(self, their): # pragma: no cover
898 return (self == their) or (self < their)
900 def __ge__(self, their): # pragma: no cover
901 return (self == their) or (self > their)
903 def _encode(self): # pragma: no cover
904 raise NotImplementedError()
906 def _decode(self, tlv, offset, decode_path, ctx): # pragma: no cover
907 raise NotImplementedError()
911 if self._expl is None:
913 return b"".join((self._expl, len_encode(len(raw)), raw))
915 def decode(self, data, offset=0, leavemm=False, decode_path=(), ctx=None):
918 :param data: either binary or memoryview
919 :param int offset: initial data's offset
920 :param bool leavemm: do we need to leave memoryview of remaining
921 data as is, or convert it to bytes otherwise
922 :param ctx: optional :ref:`context <ctx>` governing decoding process.
923 :returns: (Obj, remaining data)
927 tlv = memoryview(data)
928 if self._expl is None:
929 obj, tail = self._decode(
932 decode_path=decode_path,
937 t, tlen, lv = tag_strip(tlv)
938 except DecodeError as err:
941 klass=self.__class__,
942 decode_path=decode_path,
947 klass=self.__class__,
948 decode_path=decode_path,
952 l, llen, v = len_decode(lv)
953 except DecodeError as err:
956 klass=self.__class__,
957 decode_path=decode_path,
962 "encoded length is longer than data",
963 klass=self.__class__,
964 decode_path=decode_path,
967 obj, tail = self._decode(
969 offset=offset + tlen + llen,
970 decode_path=decode_path,
973 return obj, (tail if leavemm else tail.tobytes())
977 return self._expl is not None
985 return len(self._expl)
989 return len(len_encode(self.tlvlen))
992 def expl_offset(self):
993 return self.offset - self.expl_tlen - self.expl_llen
1000 def expl_tlvlen(self):
1001 return self.expl_tlen + self.expl_llen + self.expl_vlen
1004 def decode_path_defby(defined_by):
1005 """DEFINED BY representation inside decode path
1007 return "DEFINED BY (%s)" % defined_by
1010 ########################################################################
1012 ########################################################################
1014 PP = namedtuple("PP", (
1036 asn1_type_name="unknown",
1075 def pp_console_row(pp, oids=None, with_offsets=False, with_blob=True):
1078 cols.append("%5d%s [%d,%d,%4d]" % (
1081 " " if pp.expl_offset is None else
1082 ("-%d" % (pp.offset - pp.expl_offset))
1088 if len(pp.decode_path) > 0:
1089 cols.append(" ." * (len(pp.decode_path)))
1090 cols.append("%s:" % pp.decode_path[-1])
1091 if pp.expl is not None:
1092 klass, _, num = pp.expl
1093 cols.append("[%s%d] EXPLICIT" % (TagClassReprs[klass], num))
1094 if pp.impl is not None:
1095 klass, _, num = pp.impl
1096 cols.append("[%s%d]" % (TagClassReprs[klass], num))
1097 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1098 cols.append(pp.obj_name)
1099 cols.append(pp.asn1_type_name)
1100 if pp.value is not None:
1103 oids is not None and
1104 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1107 value = "%s (%s)" % (oids[value], pp.value)
1110 if isinstance(pp.blob, binary_type):
1111 cols.append(hexenc(pp.blob))
1112 elif isinstance(pp.blob, tuple):
1113 cols.append(", ".join(pp.blob))
1115 cols.append("OPTIONAL")
1117 cols.append("DEFAULT")
1118 return " ".join(cols)
1121 def pp_console_blob(pp):
1122 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
1123 if len(pp.decode_path) > 0:
1124 cols.append(" ." * (len(pp.decode_path) + 1))
1125 if isinstance(pp.blob, binary_type):
1126 blob = hexenc(pp.blob).upper()
1127 for i in range(0, len(blob), 32):
1128 chunk = blob[i:i + 32]
1129 yield " ".join(cols + [":".join(
1130 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1132 elif isinstance(pp.blob, tuple):
1133 yield " ".join(cols + [", ".join(pp.blob)])
1136 def pprint(obj, oids=None, big_blobs=False):
1137 """Pretty print object
1139 :param Obj obj: object you want to pretty print
1140 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1141 from it is met, then its humand readable form is printed
1142 :param big_blobs: if large binary objects are met (like OctetString
1143 values), do we need to print them too, on separate
1146 def _pprint_pps(pps):
1148 if hasattr(pp, "_fields"):
1150 yield pp_console_row(
1156 for row in pp_console_blob(pp):
1159 yield pp_console_row(pp, oids=oids, with_offsets=True)
1161 for row in _pprint_pps(pp):
1163 return "\n".join(_pprint_pps(obj.pps()))
1166 ########################################################################
1167 # ASN.1 primitive types
1168 ########################################################################
1171 """``BOOLEAN`` boolean type
1173 >>> b = Boolean(True)
1175 >>> b == Boolean(True)
1181 tag_default = tag_encode(1)
1182 asn1_type_name = "BOOLEAN"
1194 :param value: set the value. Either boolean type, or
1195 :py:class:`pyderasn.Boolean` object
1196 :param bytes impl: override default tag with ``IMPLICIT`` one
1197 :param bytes expl: override default tag with ``EXPLICIT`` one
1198 :param default: set default value. Type same as in ``value``
1199 :param bool optional: is object ``OPTIONAL`` in sequence
1201 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1202 self._value = None if value is None else self._value_sanitize(value)
1203 if default is not None:
1204 default = self._value_sanitize(default)
1205 self.default = self.__class__(
1211 self._value = default
1213 def _value_sanitize(self, value):
1214 if issubclass(value.__class__, Boolean):
1216 if isinstance(value, bool):
1218 raise InvalidValueType((self.__class__, bool))
1222 return self._value is not None
1225 obj = self.__class__()
1226 obj._value = self._value
1228 obj._expl = self._expl
1229 obj.default = self.default
1230 obj.optional = self.optional
1231 obj.offset = self.offset
1232 obj.llen = self.llen
1233 obj.vlen = self.vlen
1236 def __nonzero__(self):
1237 self._assert_ready()
1241 self._assert_ready()
1244 def __eq__(self, their):
1245 if isinstance(their, bool):
1246 return self._value == their
1247 if not issubclass(their.__class__, Boolean):
1250 self._value == their._value and
1251 self.tag == their.tag and
1252 self._expl == their._expl
1263 return self.__class__(
1265 impl=self.tag if impl is None else impl,
1266 expl=self._expl if expl is None else expl,
1267 default=self.default if default is None else default,
1268 optional=self.optional if optional is None else optional,
1272 self._assert_ready()
1276 (b"\xFF" if self._value else b"\x00"),
1279 def _decode(self, tlv, offset, decode_path, ctx):
1281 t, _, lv = tag_strip(tlv)
1282 except DecodeError as err:
1283 raise err.__class__(
1285 klass=self.__class__,
1286 decode_path=decode_path,
1291 klass=self.__class__,
1292 decode_path=decode_path,
1296 l, _, v = len_decode(lv)
1297 except DecodeError as err:
1298 raise err.__class__(
1300 klass=self.__class__,
1301 decode_path=decode_path,
1305 raise InvalidLength(
1306 "Boolean's length must be equal to 1",
1307 klass=self.__class__,
1308 decode_path=decode_path,
1312 raise NotEnoughData(
1313 "encoded length is longer than data",
1314 klass=self.__class__,
1315 decode_path=decode_path,
1318 first_octet = byte2int(v)
1319 if first_octet == 0:
1321 elif first_octet == 0xFF:
1325 "unacceptable Boolean value",
1326 klass=self.__class__,
1327 decode_path=decode_path,
1330 obj = self.__class__(
1334 default=self.default,
1335 optional=self.optional,
1336 _decoded=(offset, 1, 1),
1341 return pp_console_row(next(self.pps()))
1343 def pps(self, decode_path=()):
1345 asn1_type_name=self.asn1_type_name,
1346 obj_name=self.__class__.__name__,
1347 decode_path=decode_path,
1348 value=str(self._value) if self.ready else None,
1349 optional=self.optional,
1350 default=self == self.default,
1351 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1352 expl=None if self._expl is None else tag_decode(self._expl),
1357 expl_offset=self.expl_offset if self.expled else None,
1358 expl_tlen=self.expl_tlen if self.expled else None,
1359 expl_llen=self.expl_llen if self.expled else None,
1360 expl_vlen=self.expl_vlen if self.expled else None,
1365 """``INTEGER`` integer type
1367 >>> b = Integer(-123)
1369 >>> b == Integer(-123)
1374 >>> Integer(2, bounds=(1, 3))
1376 >>> Integer(5, bounds=(1, 3))
1377 Traceback (most recent call last):
1378 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1382 class Version(Integer):
1389 >>> v = Version("v1")
1396 {'v3': 2, 'v1': 0, 'v2': 1}
1398 __slots__ = ("specs", "_bound_min", "_bound_max")
1399 tag_default = tag_encode(2)
1400 asn1_type_name = "INTEGER"
1414 :param value: set the value. Either integer type, named value
1415 (if ``schema`` is specified in the class), or
1416 :py:class:`pyderasn.Integer` object
1417 :param bounds: set ``(MIN, MAX)`` value constraint.
1418 (-inf, +inf) by default
1419 :param bytes impl: override default tag with ``IMPLICIT`` one
1420 :param bytes expl: override default tag with ``EXPLICIT`` one
1421 :param default: set default value. Type same as in ``value``
1422 :param bool optional: is object ``OPTIONAL`` in sequence
1424 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1426 specs = getattr(self, "schema", {}) if _specs is None else _specs
1427 self.specs = specs if isinstance(specs, dict) else dict(specs)
1428 self._bound_min, self._bound_max = getattr(
1431 (float("-inf"), float("+inf")),
1432 ) if bounds is None else bounds
1433 if value is not None:
1434 self._value = self._value_sanitize(value)
1435 if default is not None:
1436 default = self._value_sanitize(default)
1437 self.default = self.__class__(
1443 if self._value is None:
1444 self._value = default
1446 def _value_sanitize(self, value):
1447 if issubclass(value.__class__, Integer):
1448 value = value._value
1449 elif isinstance(value, integer_types):
1451 elif isinstance(value, str):
1452 value = self.specs.get(value)
1454 raise ObjUnknown("integer value: %s" % value)
1456 raise InvalidValueType((self.__class__, int, str))
1457 if not self._bound_min <= value <= self._bound_max:
1458 raise BoundsError(self._bound_min, value, self._bound_max)
1463 return self._value is not None
1466 obj = self.__class__(_specs=self.specs)
1467 obj._value = self._value
1468 obj._bound_min = self._bound_min
1469 obj._bound_max = self._bound_max
1471 obj._expl = self._expl
1472 obj.default = self.default
1473 obj.optional = self.optional
1474 obj.offset = self.offset
1475 obj.llen = self.llen
1476 obj.vlen = self.vlen
1480 self._assert_ready()
1481 return int(self._value)
1484 self._assert_ready()
1487 bytes(self._expl or b"") +
1488 str(self._value).encode("ascii"),
1491 def __eq__(self, their):
1492 if isinstance(their, integer_types):
1493 return self._value == their
1494 if not issubclass(their.__class__, Integer):
1497 self._value == their._value and
1498 self.tag == their.tag and
1499 self._expl == their._expl
1502 def __lt__(self, their):
1503 return self._value < their._value
1507 for name, value in self.specs.items():
1508 if value == self._value:
1520 return self.__class__(
1523 (self._bound_min, self._bound_max)
1524 if bounds is None else bounds
1526 impl=self.tag if impl is None else impl,
1527 expl=self._expl if expl is None else expl,
1528 default=self.default if default is None else default,
1529 optional=self.optional if optional is None else optional,
1534 self._assert_ready()
1538 octets = bytearray([0])
1542 octets = bytearray()
1544 octets.append((value & 0xFF) ^ 0xFF)
1546 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1549 octets = bytearray()
1551 octets.append(value & 0xFF)
1553 if octets[-1] & 0x80 > 0:
1556 octets = bytes(octets)
1558 bytes_len = ceil(value.bit_length() / 8) or 1
1561 octets = value.to_bytes(
1566 except OverflowError:
1570 return b"".join((self.tag, len_encode(len(octets)), octets))
1572 def _decode(self, tlv, offset, decode_path, ctx):
1574 t, _, lv = tag_strip(tlv)
1575 except DecodeError as err:
1576 raise err.__class__(
1578 klass=self.__class__,
1579 decode_path=decode_path,
1584 klass=self.__class__,
1585 decode_path=decode_path,
1589 l, llen, v = len_decode(lv)
1590 except DecodeError as err:
1591 raise err.__class__(
1593 klass=self.__class__,
1594 decode_path=decode_path,
1598 raise NotEnoughData(
1599 "encoded length is longer than data",
1600 klass=self.__class__,
1601 decode_path=decode_path,
1605 raise NotEnoughData(
1607 klass=self.__class__,
1608 decode_path=decode_path,
1611 v, tail = v[:l], v[l:]
1612 first_octet = byte2int(v)
1614 second_octet = byte2int(v[1:])
1616 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1617 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1620 "non normalized integer",
1621 klass=self.__class__,
1622 decode_path=decode_path,
1627 if first_octet & 0x80 > 0:
1628 octets = bytearray()
1629 for octet in bytearray(v):
1630 octets.append(octet ^ 0xFF)
1631 for octet in octets:
1632 value = (value << 8) | octet
1636 for octet in bytearray(v):
1637 value = (value << 8) | octet
1639 value = int.from_bytes(v, byteorder="big", signed=True)
1641 obj = self.__class__(
1643 bounds=(self._bound_min, self._bound_max),
1646 default=self.default,
1647 optional=self.optional,
1649 _decoded=(offset, llen, l),
1651 except BoundsError as err:
1654 klass=self.__class__,
1655 decode_path=decode_path,
1661 return pp_console_row(next(self.pps()))
1663 def pps(self, decode_path=()):
1665 asn1_type_name=self.asn1_type_name,
1666 obj_name=self.__class__.__name__,
1667 decode_path=decode_path,
1668 value=(self.named or str(self._value)) if self.ready else None,
1669 optional=self.optional,
1670 default=self == self.default,
1671 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1672 expl=None if self._expl is None else tag_decode(self._expl),
1677 expl_offset=self.expl_offset if self.expled else None,
1678 expl_tlen=self.expl_tlen if self.expled else None,
1679 expl_llen=self.expl_llen if self.expled else None,
1680 expl_vlen=self.expl_vlen if self.expled else None,
1684 class BitString(Obj):
1685 """``BIT STRING`` bit string type
1687 >>> BitString(b"hello world")
1688 BIT STRING 88 bits 68656c6c6f20776f726c64
1691 >>> b == b"hello world"
1696 >>> b = BitString("'010110000000'B")
1697 BIT STRING 12 bits 5800
1700 >>> b[0], b[1], b[2], b[3]
1701 (False, True, False, True)
1705 [False, True, False, True, True, False, False, False, False, False, False, False]
1709 class KeyUsage(BitString):
1711 ('digitalSignature', 0),
1712 ('nonRepudiation', 1),
1713 ('keyEncipherment', 2),
1716 >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1717 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1719 ['nonRepudiation', 'keyEncipherment']
1721 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1723 __slots__ = ("specs", "defined")
1724 tag_default = tag_encode(3)
1725 asn1_type_name = "BIT STRING"
1738 :param value: set the value. Either binary type, tuple of named
1739 values (if ``schema`` is specified in the class),
1740 string in ``'XXX...'B`` form, or
1741 :py:class:`pyderasn.BitString` object
1742 :param bytes impl: override default tag with ``IMPLICIT`` one
1743 :param bytes expl: override default tag with ``EXPLICIT`` one
1744 :param default: set default value. Type same as in ``value``
1745 :param bool optional: is object ``OPTIONAL`` in sequence
1747 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1748 specs = getattr(self, "schema", {}) if _specs is None else _specs
1749 self.specs = specs if isinstance(specs, dict) else dict(specs)
1750 self._value = None if value is None else self._value_sanitize(value)
1751 if default is not None:
1752 default = self._value_sanitize(default)
1753 self.default = self.__class__(
1759 self._value = default
1762 def _bits2octets(self, bits):
1763 if len(self.specs) > 0:
1764 bits = bits.rstrip("0")
1766 bits += "0" * ((8 - (bit_len % 8)) % 8)
1767 octets = bytearray(len(bits) // 8)
1768 for i in six_xrange(len(octets)):
1769 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1770 return bit_len, bytes(octets)
1772 def _value_sanitize(self, value):
1773 if issubclass(value.__class__, BitString):
1775 if isinstance(value, (string_types, binary_type)):
1777 isinstance(value, string_types) and
1778 value.startswith("'") and
1779 value.endswith("'B")
1782 if not set(value) <= set(("0", "1")):
1783 raise ValueError("B's coding contains unacceptable chars")
1784 return self._bits2octets(value)
1785 elif isinstance(value, binary_type):
1786 return (len(value) * 8, value)
1788 raise InvalidValueType((
1793 if isinstance(value, tuple):
1796 isinstance(value[0], integer_types) and
1797 isinstance(value[1], binary_type)
1802 bit = self.specs.get(name)
1804 raise ObjUnknown("BitString value: %s" % name)
1807 return self._bits2octets("")
1809 return self._bits2octets("".join(
1810 ("1" if bit in bits else "0")
1811 for bit in six_xrange(max(bits) + 1)
1813 raise InvalidValueType((self.__class__, binary_type, string_types))
1817 return self._value is not None
1820 obj = self.__class__(_specs=self.specs)
1822 if value is not None:
1823 value = (value[0], value[1])
1826 obj._expl = self._expl
1827 obj.default = self.default
1828 obj.optional = self.optional
1829 obj.offset = self.offset
1830 obj.llen = self.llen
1831 obj.vlen = self.vlen
1835 self._assert_ready()
1836 for i in six_xrange(self._value[0]):
1841 self._assert_ready()
1842 return self._value[0]
1844 def __bytes__(self):
1845 self._assert_ready()
1846 return self._value[1]
1848 def __eq__(self, their):
1849 if isinstance(their, bytes):
1850 return self._value[1] == their
1851 if not issubclass(their.__class__, BitString):
1854 self._value == their._value and
1855 self.tag == their.tag and
1856 self._expl == their._expl
1861 return [name for name, bit in self.specs.items() if self[bit]]
1871 return self.__class__(
1873 impl=self.tag if impl is None else impl,
1874 expl=self._expl if expl is None else expl,
1875 default=self.default if default is None else default,
1876 optional=self.optional if optional is None else optional,
1880 def __getitem__(self, key):
1881 if isinstance(key, int):
1882 bit_len, octets = self._value
1886 byte2int(memoryview(octets)[key // 8:]) >>
1889 if isinstance(key, string_types):
1890 value = self.specs.get(key)
1892 raise ObjUnknown("BitString value: %s" % key)
1894 raise InvalidValueType((int, str))
1897 self._assert_ready()
1898 bit_len, octets = self._value
1901 len_encode(len(octets) + 1),
1902 int2byte((8 - bit_len % 8) % 8),
1906 def _decode(self, tlv, offset, decode_path, ctx):
1908 t, _, lv = tag_strip(tlv)
1909 except DecodeError as err:
1910 raise err.__class__(
1912 klass=self.__class__,
1913 decode_path=decode_path,
1918 klass=self.__class__,
1919 decode_path=decode_path,
1923 l, llen, v = len_decode(lv)
1924 except DecodeError as err:
1925 raise err.__class__(
1927 klass=self.__class__,
1928 decode_path=decode_path,
1932 raise NotEnoughData(
1933 "encoded length is longer than data",
1934 klass=self.__class__,
1935 decode_path=decode_path,
1939 raise NotEnoughData(
1941 klass=self.__class__,
1942 decode_path=decode_path,
1945 pad_size = byte2int(v)
1946 if l == 1 and pad_size != 0:
1948 "invalid empty value",
1949 klass=self.__class__,
1950 decode_path=decode_path,
1956 klass=self.__class__,
1957 decode_path=decode_path,
1960 if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
1963 klass=self.__class__,
1964 decode_path=decode_path,
1967 v, tail = v[:l], v[l:]
1968 obj = self.__class__(
1969 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
1972 default=self.default,
1973 optional=self.optional,
1975 _decoded=(offset, llen, l),
1980 return pp_console_row(next(self.pps()))
1982 def pps(self, decode_path=()):
1986 bit_len, blob = self._value
1987 value = "%d bits" % bit_len
1988 if len(self.specs) > 0:
1989 blob = tuple(self.named)
1991 asn1_type_name=self.asn1_type_name,
1992 obj_name=self.__class__.__name__,
1993 decode_path=decode_path,
1996 optional=self.optional,
1997 default=self == self.default,
1998 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1999 expl=None if self._expl is None else tag_decode(self._expl),
2004 expl_offset=self.expl_offset if self.expled else None,
2005 expl_tlen=self.expl_tlen if self.expled else None,
2006 expl_llen=self.expl_llen if self.expled else None,
2007 expl_vlen=self.expl_vlen if self.expled else None,
2009 defined_by, defined = self.defined or (None, None)
2010 if defined_by is not None:
2012 decode_path=decode_path + (decode_path_defby(defined_by),)
2016 class OctetString(Obj):
2017 """``OCTET STRING`` binary string type
2019 >>> s = OctetString(b"hello world")
2020 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2021 >>> s == OctetString(b"hello world")
2026 >>> OctetString(b"hello", bounds=(4, 4))
2027 Traceback (most recent call last):
2028 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2029 >>> OctetString(b"hell", bounds=(4, 4))
2030 OCTET STRING 4 bytes 68656c6c
2032 __slots__ = ("_bound_min", "_bound_max", "defined")
2033 tag_default = tag_encode(4)
2034 asn1_type_name = "OCTET STRING"
2047 :param value: set the value. Either binary type, or
2048 :py:class:`pyderasn.OctetString` object
2049 :param bounds: set ``(MIN, MAX)`` value size constraint.
2050 (-inf, +inf) by default
2051 :param bytes impl: override default tag with ``IMPLICIT`` one
2052 :param bytes expl: override default tag with ``EXPLICIT`` one
2053 :param default: set default value. Type same as in ``value``
2054 :param bool optional: is object ``OPTIONAL`` in sequence
2056 super(OctetString, self).__init__(
2064 self._bound_min, self._bound_max = getattr(
2068 ) if bounds is None else bounds
2069 if value is not None:
2070 self._value = self._value_sanitize(value)
2071 if default is not None:
2072 default = self._value_sanitize(default)
2073 self.default = self.__class__(
2078 if self._value is None:
2079 self._value = default
2082 def _value_sanitize(self, value):
2083 if issubclass(value.__class__, OctetString):
2084 value = value._value
2085 elif isinstance(value, binary_type):
2088 raise InvalidValueType((self.__class__, bytes))
2089 if not self._bound_min <= len(value) <= self._bound_max:
2090 raise BoundsError(self._bound_min, len(value), self._bound_max)
2095 return self._value is not None
2098 obj = self.__class__()
2099 obj._value = self._value
2100 obj._bound_min = self._bound_min
2101 obj._bound_max = self._bound_max
2103 obj._expl = self._expl
2104 obj.default = self.default
2105 obj.optional = self.optional
2106 obj.offset = self.offset
2107 obj.llen = self.llen
2108 obj.vlen = self.vlen
2111 def __bytes__(self):
2112 self._assert_ready()
2115 def __eq__(self, their):
2116 if isinstance(their, binary_type):
2117 return self._value == their
2118 if not issubclass(their.__class__, OctetString):
2121 self._value == their._value and
2122 self.tag == their.tag and
2123 self._expl == their._expl
2126 def __lt__(self, their):
2127 return self._value < their._value
2138 return self.__class__(
2141 (self._bound_min, self._bound_max)
2142 if bounds is None else bounds
2144 impl=self.tag if impl is None else impl,
2145 expl=self._expl if expl is None else expl,
2146 default=self.default if default is None else default,
2147 optional=self.optional if optional is None else optional,
2151 self._assert_ready()
2154 len_encode(len(self._value)),
2158 def _decode(self, tlv, offset, decode_path, ctx):
2160 t, _, lv = tag_strip(tlv)
2161 except DecodeError as err:
2162 raise err.__class__(
2164 klass=self.__class__,
2165 decode_path=decode_path,
2170 klass=self.__class__,
2171 decode_path=decode_path,
2175 l, llen, v = len_decode(lv)
2176 except DecodeError as err:
2177 raise err.__class__(
2179 klass=self.__class__,
2180 decode_path=decode_path,
2184 raise NotEnoughData(
2185 "encoded length is longer than data",
2186 klass=self.__class__,
2187 decode_path=decode_path,
2190 v, tail = v[:l], v[l:]
2192 obj = self.__class__(
2194 bounds=(self._bound_min, self._bound_max),
2197 default=self.default,
2198 optional=self.optional,
2199 _decoded=(offset, llen, l),
2201 except BoundsError as err:
2204 klass=self.__class__,
2205 decode_path=decode_path,
2211 return pp_console_row(next(self.pps()))
2213 def pps(self, decode_path=()):
2215 asn1_type_name=self.asn1_type_name,
2216 obj_name=self.__class__.__name__,
2217 decode_path=decode_path,
2218 value=("%d bytes" % len(self._value)) if self.ready else None,
2219 blob=self._value if self.ready else None,
2220 optional=self.optional,
2221 default=self == self.default,
2222 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2223 expl=None if self._expl is None else tag_decode(self._expl),
2228 expl_offset=self.expl_offset if self.expled else None,
2229 expl_tlen=self.expl_tlen if self.expled else None,
2230 expl_llen=self.expl_llen if self.expled else None,
2231 expl_vlen=self.expl_vlen if self.expled else None,
2233 defined_by, defined = self.defined or (None, None)
2234 if defined_by is not None:
2236 decode_path=decode_path + (decode_path_defby(defined_by),)
2241 """``NULL`` null object
2249 tag_default = tag_encode(5)
2250 asn1_type_name = "NULL"
2254 value=None, # unused, but Sequence passes it
2261 :param bytes impl: override default tag with ``IMPLICIT`` one
2262 :param bytes expl: override default tag with ``EXPLICIT`` one
2263 :param bool optional: is object ``OPTIONAL`` in sequence
2265 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2273 obj = self.__class__()
2275 obj._expl = self._expl
2276 obj.default = self.default
2277 obj.optional = self.optional
2278 obj.offset = self.offset
2279 obj.llen = self.llen
2280 obj.vlen = self.vlen
2283 def __eq__(self, their):
2284 if not issubclass(their.__class__, Null):
2287 self.tag == their.tag and
2288 self._expl == their._expl
2298 return self.__class__(
2299 impl=self.tag if impl is None else impl,
2300 expl=self._expl if expl is None else expl,
2301 optional=self.optional if optional is None else optional,
2305 return self.tag + len_encode(0)
2307 def _decode(self, tlv, offset, decode_path, ctx):
2309 t, _, lv = tag_strip(tlv)
2310 except DecodeError as err:
2311 raise err.__class__(
2313 klass=self.__class__,
2314 decode_path=decode_path,
2319 klass=self.__class__,
2320 decode_path=decode_path,
2324 l, _, v = len_decode(lv)
2325 except DecodeError as err:
2326 raise err.__class__(
2328 klass=self.__class__,
2329 decode_path=decode_path,
2333 raise InvalidLength(
2334 "Null must have zero length",
2335 klass=self.__class__,
2336 decode_path=decode_path,
2339 obj = self.__class__(
2342 optional=self.optional,
2343 _decoded=(offset, 1, 0),
2348 return pp_console_row(next(self.pps()))
2350 def pps(self, decode_path=()):
2352 asn1_type_name=self.asn1_type_name,
2353 obj_name=self.__class__.__name__,
2354 decode_path=decode_path,
2355 optional=self.optional,
2356 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2357 expl=None if self._expl is None else tag_decode(self._expl),
2362 expl_offset=self.expl_offset if self.expled else None,
2363 expl_tlen=self.expl_tlen if self.expled else None,
2364 expl_llen=self.expl_llen if self.expled else None,
2365 expl_vlen=self.expl_vlen if self.expled else None,
2369 class ObjectIdentifier(Obj):
2370 """``OBJECT IDENTIFIER`` OID type
2372 >>> oid = ObjectIdentifier((1, 2, 3))
2373 OBJECT IDENTIFIER 1.2.3
2374 >>> oid == ObjectIdentifier("1.2.3")
2380 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2381 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2383 >>> str(ObjectIdentifier((3, 1)))
2384 Traceback (most recent call last):
2385 pyderasn.InvalidOID: unacceptable first arc value
2387 __slots__ = ("defines",)
2388 tag_default = tag_encode(6)
2389 asn1_type_name = "OBJECT IDENTIFIER"
2402 :param value: set the value. Either tuples of integers,
2403 string of "."-concatenated integers, or
2404 :py:class:`pyderasn.ObjectIdentifier` object
2405 :param defines: sequence of tuples. Each tuple has two elements.
2406 First one is relative to current one decode
2407 path, aiming to the field defined by that OID.
2408 Read about relative path in
2409 :py:func:`pyderasn.abs_decode_path`. Second
2410 tuple element is ``{OID: pyderasn.Obj()}``
2411 dictionary, mapping between current OID value
2412 and structure applied to defined field.
2413 :ref:`Read about DEFINED BY <definedby>`
2414 :param bytes impl: override default tag with ``IMPLICIT`` one
2415 :param bytes expl: override default tag with ``EXPLICIT`` one
2416 :param default: set default value. Type same as in ``value``
2417 :param bool optional: is object ``OPTIONAL`` in sequence
2419 super(ObjectIdentifier, self).__init__(
2427 if value is not None:
2428 self._value = self._value_sanitize(value)
2429 if default is not None:
2430 default = self._value_sanitize(default)
2431 self.default = self.__class__(
2436 if self._value is None:
2437 self._value = default
2438 self.defines = defines
2440 def __add__(self, their):
2441 if isinstance(their, self.__class__):
2442 return self.__class__(self._value + their._value)
2443 if isinstance(their, tuple):
2444 return self.__class__(self._value + their)
2445 raise InvalidValueType((self.__class__, tuple))
2447 def _value_sanitize(self, value):
2448 if issubclass(value.__class__, ObjectIdentifier):
2450 if isinstance(value, string_types):
2452 value = tuple(int(arc) for arc in value.split("."))
2454 raise InvalidOID("unacceptable arcs values")
2455 if isinstance(value, tuple):
2457 raise InvalidOID("less than 2 arcs")
2458 first_arc = value[0]
2459 if first_arc in (0, 1):
2460 if not (0 <= value[1] <= 39):
2461 raise InvalidOID("second arc is too wide")
2462 elif first_arc == 2:
2465 raise InvalidOID("unacceptable first arc value")
2467 raise InvalidValueType((self.__class__, str, tuple))
2471 return self._value is not None
2474 obj = self.__class__()
2475 obj._value = self._value
2476 obj.defines = self.defines
2478 obj._expl = self._expl
2479 obj.default = self.default
2480 obj.optional = self.optional
2481 obj.offset = self.offset
2482 obj.llen = self.llen
2483 obj.vlen = self.vlen
2487 self._assert_ready()
2488 return iter(self._value)
2491 return ".".join(str(arc) for arc in self._value or ())
2494 self._assert_ready()
2497 bytes(self._expl or b"") +
2498 str(self._value).encode("ascii"),
2501 def __eq__(self, their):
2502 if isinstance(their, tuple):
2503 return self._value == their
2504 if not issubclass(their.__class__, ObjectIdentifier):
2507 self.tag == their.tag and
2508 self._expl == their._expl and
2509 self._value == their._value
2512 def __lt__(self, their):
2513 return self._value < their._value
2524 return self.__class__(
2526 defines=self.defines if defines is None else defines,
2527 impl=self.tag if impl is None else impl,
2528 expl=self._expl if expl is None else expl,
2529 default=self.default if default is None else default,
2530 optional=self.optional if optional is None else optional,
2534 self._assert_ready()
2536 first_value = value[1]
2537 first_arc = value[0]
2540 elif first_arc == 1:
2542 elif first_arc == 2:
2544 else: # pragma: no cover
2545 raise RuntimeError("invalid arc is stored")
2546 octets = [zero_ended_encode(first_value)]
2547 for arc in value[2:]:
2548 octets.append(zero_ended_encode(arc))
2549 v = b"".join(octets)
2550 return b"".join((self.tag, len_encode(len(v)), v))
2552 def _decode(self, tlv, offset, decode_path, ctx):
2554 t, _, lv = tag_strip(tlv)
2555 except DecodeError as err:
2556 raise err.__class__(
2558 klass=self.__class__,
2559 decode_path=decode_path,
2564 klass=self.__class__,
2565 decode_path=decode_path,
2569 l, llen, v = len_decode(lv)
2570 except DecodeError as err:
2571 raise err.__class__(
2573 klass=self.__class__,
2574 decode_path=decode_path,
2578 raise NotEnoughData(
2579 "encoded length is longer than data",
2580 klass=self.__class__,
2581 decode_path=decode_path,
2585 raise NotEnoughData(
2587 klass=self.__class__,
2588 decode_path=decode_path,
2591 v, tail = v[:l], v[l:]
2597 octet = indexbytes(v, i)
2598 arc = (arc << 7) | (octet & 0x7F)
2599 if octet & 0x80 == 0:
2607 klass=self.__class__,
2608 decode_path=decode_path,
2612 second_arc = arcs[0]
2613 if 0 <= second_arc <= 39:
2615 elif 40 <= second_arc <= 79:
2621 obj = self.__class__(
2622 value=tuple([first_arc, second_arc] + arcs[1:]),
2625 default=self.default,
2626 optional=self.optional,
2627 _decoded=(offset, llen, l),
2632 return pp_console_row(next(self.pps()))
2634 def pps(self, decode_path=()):
2636 asn1_type_name=self.asn1_type_name,
2637 obj_name=self.__class__.__name__,
2638 decode_path=decode_path,
2639 value=str(self) if self.ready else None,
2640 optional=self.optional,
2641 default=self == self.default,
2642 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2643 expl=None if self._expl is None else tag_decode(self._expl),
2648 expl_offset=self.expl_offset if self.expled else None,
2649 expl_tlen=self.expl_tlen if self.expled else None,
2650 expl_llen=self.expl_llen if self.expled else None,
2651 expl_vlen=self.expl_vlen if self.expled else None,
2655 class Enumerated(Integer):
2656 """``ENUMERATED`` integer type
2658 This type is identical to :py:class:`pyderasn.Integer`, but requires
2659 schema to be specified and does not accept values missing from it.
2662 tag_default = tag_encode(10)
2663 asn1_type_name = "ENUMERATED"
2674 bounds=None, # dummy argument, workability for Integer.decode
2676 super(Enumerated, self).__init__(
2685 if len(self.specs) == 0:
2686 raise ValueError("schema must be specified")
2688 def _value_sanitize(self, value):
2689 if isinstance(value, self.__class__):
2690 value = value._value
2691 elif isinstance(value, integer_types):
2692 if value not in list(self.specs.values()):
2694 "unknown integer value: %s" % value,
2695 klass=self.__class__,
2697 elif isinstance(value, string_types):
2698 value = self.specs.get(value)
2700 raise ObjUnknown("integer value: %s" % value)
2702 raise InvalidValueType((self.__class__, int, str))
2706 obj = self.__class__(_specs=self.specs)
2707 obj._value = self._value
2708 obj._bound_min = self._bound_min
2709 obj._bound_max = self._bound_max
2711 obj._expl = self._expl
2712 obj.default = self.default
2713 obj.optional = self.optional
2714 obj.offset = self.offset
2715 obj.llen = self.llen
2716 obj.vlen = self.vlen
2728 return self.__class__(
2730 impl=self.tag if impl is None else impl,
2731 expl=self._expl if expl is None else expl,
2732 default=self.default if default is None else default,
2733 optional=self.optional if optional is None else optional,
2738 class CommonString(OctetString):
2739 """Common class for all strings
2741 Everything resembles :py:class:`pyderasn.OctetString`, except
2742 ability to deal with unicode text strings.
2744 >>> hexenc("привет мир".encode("utf-8"))
2745 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2746 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2748 >>> s = UTF8String("привет мир")
2749 UTF8String UTF8String привет мир
2751 'привет мир'
2752 >>> hexenc(bytes(s))
2753 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2755 >>> PrintableString("привет мир")
2756 Traceback (most recent call last):
2757 UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2759 >>> BMPString("ада", bounds=(2, 2))
2760 Traceback (most recent call last):
2761 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2762 >>> s = BMPString("ад", bounds=(2, 2))
2765 >>> hexenc(bytes(s))
2773 * - :py:class:`pyderasn.UTF8String`
2775 * - :py:class:`pyderasn.NumericString`
2777 * - :py:class:`pyderasn.PrintableString`
2779 * - :py:class:`pyderasn.TeletexString`
2781 * - :py:class:`pyderasn.T61String`
2783 * - :py:class:`pyderasn.VideotexString`
2785 * - :py:class:`pyderasn.IA5String`
2787 * - :py:class:`pyderasn.GraphicString`
2789 * - :py:class:`pyderasn.VisibleString`
2791 * - :py:class:`pyderasn.ISO646String`
2793 * - :py:class:`pyderasn.GeneralString`
2795 * - :py:class:`pyderasn.UniversalString`
2797 * - :py:class:`pyderasn.BMPString`
2800 __slots__ = ("encoding",)
2802 def _value_sanitize(self, value):
2804 value_decoded = None
2805 if isinstance(value, self.__class__):
2806 value_raw = value._value
2807 elif isinstance(value, text_type):
2808 value_decoded = value
2809 elif isinstance(value, binary_type):
2812 raise InvalidValueType((self.__class__, text_type, binary_type))
2814 value_decoded.encode(self.encoding)
2815 if value_raw is None else value_raw
2818 value_raw.decode(self.encoding)
2819 if value_decoded is None else value_decoded
2821 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2829 def __eq__(self, their):
2830 if isinstance(their, binary_type):
2831 return self._value == their
2832 if isinstance(their, text_type):
2833 return self._value == their.encode(self.encoding)
2834 if not isinstance(their, self.__class__):
2837 self._value == their._value and
2838 self.tag == their.tag and
2839 self._expl == their._expl
2842 def __unicode__(self):
2844 return self._value.decode(self.encoding)
2845 return text_type(self._value)
2848 return pp_console_row(next(self.pps(no_unicode=PY2)))
2850 def pps(self, decode_path=(), no_unicode=False):
2853 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2855 asn1_type_name=self.asn1_type_name,
2856 obj_name=self.__class__.__name__,
2857 decode_path=decode_path,
2859 optional=self.optional,
2860 default=self == self.default,
2861 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2862 expl=None if self._expl is None else tag_decode(self._expl),
2870 class UTF8String(CommonString):
2872 tag_default = tag_encode(12)
2874 asn1_type_name = "UTF8String"
2877 class NumericString(CommonString):
2879 tag_default = tag_encode(18)
2881 asn1_type_name = "NumericString"
2884 class PrintableString(CommonString):
2886 tag_default = tag_encode(19)
2888 asn1_type_name = "PrintableString"
2891 class TeletexString(CommonString):
2893 tag_default = tag_encode(20)
2895 asn1_type_name = "TeletexString"
2898 class T61String(TeletexString):
2900 asn1_type_name = "T61String"
2903 class VideotexString(CommonString):
2905 tag_default = tag_encode(21)
2906 encoding = "iso-8859-1"
2907 asn1_type_name = "VideotexString"
2910 class IA5String(CommonString):
2912 tag_default = tag_encode(22)
2914 asn1_type_name = "IA5"
2917 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
2918 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
2919 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
2922 class UTCTime(CommonString):
2923 """``UTCTime`` datetime type
2925 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2926 UTCTime UTCTime 2017-09-30T22:07:50
2932 datetime.datetime(2017, 9, 30, 22, 7, 50)
2933 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2934 datetime.datetime(1957, 9, 30, 22, 7, 50)
2937 tag_default = tag_encode(23)
2939 asn1_type_name = "UTCTime"
2941 fmt = "%y%m%d%H%M%SZ"
2951 bounds=None, # dummy argument, workability for OctetString.decode
2954 :param value: set the value. Either datetime type, or
2955 :py:class:`pyderasn.UTCTime` object
2956 :param bytes impl: override default tag with ``IMPLICIT`` one
2957 :param bytes expl: override default tag with ``EXPLICIT`` one
2958 :param default: set default value. Type same as in ``value``
2959 :param bool optional: is object ``OPTIONAL`` in sequence
2961 super(UTCTime, self).__init__(
2969 if value is not None:
2970 self._value = self._value_sanitize(value)
2971 if default is not None:
2972 default = self._value_sanitize(default)
2973 self.default = self.__class__(
2978 if self._value is None:
2979 self._value = default
2981 def _value_sanitize(self, value):
2982 if isinstance(value, self.__class__):
2984 if isinstance(value, datetime):
2985 return value.strftime(self.fmt).encode("ascii")
2986 if isinstance(value, binary_type):
2987 value_decoded = value.decode("ascii")
2988 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
2990 datetime.strptime(value_decoded, self.fmt)
2992 raise DecodeError("invalid UTCTime format")
2995 raise DecodeError("invalid UTCTime length")
2996 raise InvalidValueType((self.__class__, datetime))
2998 def __eq__(self, their):
2999 if isinstance(their, binary_type):
3000 return self._value == their
3001 if isinstance(their, datetime):
3002 return self.todatetime() == their
3003 if not isinstance(their, self.__class__):
3006 self._value == their._value and
3007 self.tag == their.tag and
3008 self._expl == their._expl
3011 def todatetime(self):
3012 """Convert to datetime
3016 Pay attention that UTCTime can not hold full year, so all years
3017 having < 50 years are treated as 20xx, 19xx otherwise, according
3018 to X.509 recomendation.
3020 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3021 year = value.year % 100
3023 year=(2000 + year) if year < 50 else (1900 + year),
3027 minute=value.minute,
3028 second=value.second,
3032 return pp_console_row(next(self.pps()))
3034 def pps(self, decode_path=()):
3036 asn1_type_name=self.asn1_type_name,
3037 obj_name=self.__class__.__name__,
3038 decode_path=decode_path,
3039 value=self.todatetime().isoformat() if self.ready else None,
3040 optional=self.optional,
3041 default=self == self.default,
3042 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3043 expl=None if self._expl is None else tag_decode(self._expl),
3051 class GeneralizedTime(UTCTime):
3052 """``GeneralizedTime`` datetime type
3054 This type is similar to :py:class:`pyderasn.UTCTime`.
3056 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3057 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3059 '20170930220750.000123Z'
3060 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3061 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3064 tag_default = tag_encode(24)
3065 asn1_type_name = "GeneralizedTime"
3067 fmt = "%Y%m%d%H%M%SZ"
3068 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3070 def _value_sanitize(self, value):
3071 if isinstance(value, self.__class__):
3073 if isinstance(value, datetime):
3074 return value.strftime(
3075 self.fmt_ms if value.microsecond > 0 else self.fmt
3077 if isinstance(value, binary_type):
3078 value_decoded = value.decode("ascii")
3079 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3081 datetime.strptime(value_decoded, self.fmt)
3084 "invalid GeneralizedTime (without ms) format",
3087 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3089 datetime.strptime(value_decoded, self.fmt_ms)
3092 "invalid GeneralizedTime (with ms) format",
3097 "invalid GeneralizedTime length",
3098 klass=self.__class__,
3100 raise InvalidValueType((self.__class__, datetime))
3102 def todatetime(self):
3103 value = self._value.decode("ascii")
3104 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3105 return datetime.strptime(value, self.fmt)
3106 return datetime.strptime(value, self.fmt_ms)
3109 class GraphicString(CommonString):
3111 tag_default = tag_encode(25)
3112 encoding = "iso-8859-1"
3113 asn1_type_name = "GraphicString"
3116 class VisibleString(CommonString):
3118 tag_default = tag_encode(26)
3120 asn1_type_name = "VisibleString"
3123 class ISO646String(VisibleString):
3125 asn1_type_name = "ISO646String"
3128 class GeneralString(CommonString):
3130 tag_default = tag_encode(27)
3131 encoding = "iso-8859-1"
3132 asn1_type_name = "GeneralString"
3135 class UniversalString(CommonString):
3137 tag_default = tag_encode(28)
3138 encoding = "utf-32-be"
3139 asn1_type_name = "UniversalString"
3142 class BMPString(CommonString):
3144 tag_default = tag_encode(30)
3145 encoding = "utf-16-be"
3146 asn1_type_name = "BMPString"
3150 """``CHOICE`` special type
3154 class GeneralName(Choice):
3156 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
3157 ('dNSName', IA5String(impl=tag_ctxp(2))),
3160 >>> gn = GeneralName()
3162 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3163 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3164 >>> gn["dNSName"] = IA5String("bar.baz")
3165 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3166 >>> gn["rfc822Name"]
3169 [2] IA5String IA5 bar.baz
3172 >>> gn.value == gn["dNSName"]
3175 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3177 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3178 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3180 __slots__ = ("specs",)
3182 asn1_type_name = "CHOICE"
3195 :param value: set the value. Either ``(choice, value)`` tuple, or
3196 :py:class:`pyderasn.Choice` object
3197 :param bytes impl: can not be set, do **not** use it
3198 :param bytes expl: override default tag with ``EXPLICIT`` one
3199 :param default: set default value. Type same as in ``value``
3200 :param bool optional: is object ``OPTIONAL`` in sequence
3202 if impl is not None:
3203 raise ValueError("no implicit tag allowed for CHOICE")
3204 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3206 schema = getattr(self, "schema", ())
3207 if len(schema) == 0:
3208 raise ValueError("schema must be specified")
3210 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3213 if value is not None:
3214 self._value = self._value_sanitize(value)
3215 if default is not None:
3216 default_value = self._value_sanitize(default)
3217 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3218 default_obj.specs = self.specs
3219 default_obj._value = default_value
3220 self.default = default_obj
3222 self._value = default_obj.copy()._value
3224 def _value_sanitize(self, value):
3225 if isinstance(value, self.__class__):
3227 if isinstance(value, tuple) and len(value) == 2:
3229 spec = self.specs.get(choice)
3231 raise ObjUnknown(choice)
3232 if not isinstance(obj, spec.__class__):
3233 raise InvalidValueType((spec,))
3234 return (choice, spec(obj))
3235 raise InvalidValueType((self.__class__, tuple))
3239 return self._value is not None and self._value[1].ready
3242 obj = self.__class__(schema=self.specs)
3243 obj._expl = self._expl
3244 obj.default = self.default
3245 obj.optional = self.optional
3246 obj.offset = self.offset
3247 obj.llen = self.llen
3248 obj.vlen = self.vlen
3250 if value is not None:
3251 obj._value = (value[0], value[1].copy())
3254 def __eq__(self, their):
3255 if isinstance(their, tuple) and len(their) == 2:
3256 return self._value == their
3257 if not isinstance(their, self.__class__):
3260 self.specs == their.specs and
3261 self._value == their._value
3271 return self.__class__(
3274 expl=self._expl if expl is None else expl,
3275 default=self.default if default is None else default,
3276 optional=self.optional if optional is None else optional,
3281 self._assert_ready()
3282 return self._value[0]
3286 self._assert_ready()
3287 return self._value[1]
3289 def __getitem__(self, key):
3290 if key not in self.specs:
3291 raise ObjUnknown(key)
3292 if self._value is None:
3294 choice, value = self._value
3299 def __setitem__(self, key, value):
3300 spec = self.specs.get(key)
3302 raise ObjUnknown(key)
3303 if not isinstance(value, spec.__class__):
3304 raise InvalidValueType((spec.__class__,))
3305 self._value = (key, spec(value))
3313 return self._value[1].decoded if self.ready else False
3316 self._assert_ready()
3317 return self._value[1].encode()
3319 def _decode(self, tlv, offset, decode_path, ctx):
3320 for choice, spec in self.specs.items():
3322 value, tail = spec.decode(
3326 decode_path=decode_path + (choice,),
3331 obj = self.__class__(
3334 default=self.default,
3335 optional=self.optional,
3336 _decoded=(offset, 0, value.tlvlen),
3338 obj._value = (choice, value)
3341 klass=self.__class__,
3342 decode_path=decode_path,
3347 value = pp_console_row(next(self.pps()))
3349 value = "%s[%r]" % (value, self.value)
3352 def pps(self, decode_path=()):
3354 asn1_type_name=self.asn1_type_name,
3355 obj_name=self.__class__.__name__,
3356 decode_path=decode_path,
3357 value=self.choice if self.ready else None,
3358 optional=self.optional,
3359 default=self == self.default,
3360 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3361 expl=None if self._expl is None else tag_decode(self._expl),
3368 yield self.value.pps(decode_path=decode_path + (self.choice,))
3371 class PrimitiveTypes(Choice):
3372 """Predefined ``CHOICE`` for all generic primitive types
3374 It could be useful for general decoding of some unspecified values:
3376 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3377 OCTET STRING 3 bytes 666f6f
3378 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3382 schema = tuple((klass.__name__, klass()) for klass in (
3407 """``ANY`` special type
3409 >>> Any(Integer(-123))
3411 >>> a = Any(OctetString(b"hello world").encode())
3412 ANY 040b68656c6c6f20776f726c64
3413 >>> hexenc(bytes(a))
3414 b'0x040x0bhello world'
3416 __slots__ = ("defined",)
3417 tag_default = tag_encode(0)
3418 asn1_type_name = "ANY"
3428 :param value: set the value. Either any kind of pyderasn's
3429 **ready** object, or bytes. Pay attention that
3430 **no** validation is performed is raw binary value
3432 :param bytes expl: override default tag with ``EXPLICIT`` one
3433 :param bool optional: is object ``OPTIONAL`` in sequence
3435 super(Any, self).__init__(None, expl, None, optional, _decoded)
3436 self._value = None if value is None else self._value_sanitize(value)
3439 def _value_sanitize(self, value):
3440 if isinstance(value, self.__class__):
3442 if isinstance(value, Obj):
3443 return value.encode()
3444 if isinstance(value, binary_type):
3446 raise InvalidValueType((self.__class__, Obj, binary_type))
3450 return self._value is not None
3453 obj = self.__class__()
3454 obj._value = self._value
3456 obj._expl = self._expl
3457 obj.optional = self.optional
3458 obj.offset = self.offset
3459 obj.llen = self.llen
3460 obj.vlen = self.vlen
3463 def __eq__(self, their):
3464 if isinstance(their, binary_type):
3465 return self._value == their
3466 if issubclass(their.__class__, Any):
3467 return self._value == their._value
3476 return self.__class__(
3478 expl=self._expl if expl is None else expl,
3479 optional=self.optional if optional is None else optional,
3482 def __bytes__(self):
3483 self._assert_ready()
3491 self._assert_ready()
3494 def _decode(self, tlv, offset, decode_path, ctx):
3496 t, tlen, lv = tag_strip(tlv)
3497 l, llen, v = len_decode(lv)
3498 except DecodeError as err:
3499 raise err.__class__(
3501 klass=self.__class__,
3502 decode_path=decode_path,
3506 raise NotEnoughData(
3507 "encoded length is longer than data",
3508 klass=self.__class__,
3509 decode_path=decode_path,
3512 tlvlen = tlen + llen + l
3513 v, tail = tlv[:tlvlen], v[l:]
3514 obj = self.__class__(
3517 optional=self.optional,
3518 _decoded=(offset, 0, tlvlen),
3524 return pp_console_row(next(self.pps()))
3526 def pps(self, decode_path=()):
3528 asn1_type_name=self.asn1_type_name,
3529 obj_name=self.__class__.__name__,
3530 decode_path=decode_path,
3531 blob=self._value if self.ready else None,
3532 optional=self.optional,
3533 default=self == self.default,
3534 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3535 expl=None if self._expl is None else tag_decode(self._expl),
3540 expl_offset=self.expl_offset if self.expled else None,
3541 expl_tlen=self.expl_tlen if self.expled else None,
3542 expl_llen=self.expl_llen if self.expled else None,
3543 expl_vlen=self.expl_vlen if self.expled else None,
3545 defined_by, defined = self.defined or (None, None)
3546 if defined_by is not None:
3548 decode_path=decode_path + (decode_path_defby(defined_by),)
3552 ########################################################################
3553 # ASN.1 constructed types
3554 ########################################################################
3556 def get_def_by_path(defines_by_path, sub_decode_path):
3557 """Get define by decode path
3559 for path, define in defines_by_path:
3560 if len(path) != len(sub_decode_path):
3562 for p1, p2 in zip(path, sub_decode_path):
3563 if (p1 != any) and (p1 != p2):
3569 def abs_decode_path(decode_path, rel_path):
3570 """Create an absolute decode path from current and relative ones
3572 :param decode_path: current decode path, starting point.
3574 :param rel_path: relative path to ``decode_path``. Tuple of strings.
3575 If first tuple's element is "/", then treat it as
3576 an absolute path, ignoring ``decode_path`` as
3577 starting point. Also this tuple can contain ".."
3578 elements, stripping the leading element from
3581 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
3582 ("foo", "bar", "baz", "whatever")
3583 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
3585 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
3588 if rel_path[0] == "/":
3590 if rel_path[0] == "..":
3591 return abs_decode_path(decode_path[:-1], rel_path[1:])
3592 return decode_path + rel_path
3595 class Sequence(Obj):
3596 """``SEQUENCE`` structure type
3598 You have to make specification of sequence::
3600 class Extension(Sequence):
3602 ("extnID", ObjectIdentifier()),
3603 ("critical", Boolean(default=False)),
3604 ("extnValue", OctetString()),
3607 Then, you can work with it as with dictionary.
3609 >>> ext = Extension()
3610 >>> Extension().specs
3612 ('extnID', OBJECT IDENTIFIER),
3613 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3614 ('extnValue', OCTET STRING),
3616 >>> ext["extnID"] = "1.2.3"
3617 Traceback (most recent call last):
3618 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3619 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3621 You can know if sequence is ready to be encoded:
3626 Traceback (most recent call last):
3627 pyderasn.ObjNotReady: object is not ready: extnValue
3628 >>> ext["extnValue"] = OctetString(b"foobar")
3632 Value you want to assign, must have the same **type** as in
3633 corresponding specification, but it can have different tags,
3634 optional/default attributes -- they will be taken from specification
3637 class TBSCertificate(Sequence):
3639 ("version", Version(expl=tag_ctxc(0), default="v1")),
3642 >>> tbs = TBSCertificate()
3643 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3645 Assign ``None`` to remove value from sequence.
3647 You can know if value exists/set in the sequence and take its value:
3649 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3652 OBJECT IDENTIFIER 1.2.3
3654 But pay attention that if value has default, then it won't be (not
3655 in) in the sequence (because ``DEFAULT`` must not be encoded in
3656 DER), but you can read its value:
3658 >>> "critical" in ext, ext["critical"]
3659 (False, BOOLEAN False)
3660 >>> ext["critical"] = Boolean(True)
3661 >>> "critical" in ext, ext["critical"]
3662 (True, BOOLEAN True)
3664 All defaulted values are always optional.
3666 .. _strict_default_existence_ctx:
3670 When decoded DER contains defaulted value inside, then
3671 technically this is not valid DER encoding. But we allow and pass
3672 it **by default**. Of course reencoding of that kind of DER will
3673 result in different binary representation (validly without
3674 defaulted value inside). You can enable strict defaulted values
3675 existence validation by setting ``"strict_default_existence":
3676 True`` :ref:`context <ctx>` option -- decoding process will raise
3677 an exception if defaulted value is met.
3679 Two sequences are equal if they have equal specification (schema),
3680 implicit/explicit tagging and the same values.
3682 __slots__ = ("specs",)
3683 tag_default = tag_encode(form=TagFormConstructed, num=16)
3684 asn1_type_name = "SEQUENCE"
3696 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3698 schema = getattr(self, "schema", ())
3700 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3703 if value is not None:
3704 self._value = self._value_sanitize(value)
3705 if default is not None:
3706 default_value = self._value_sanitize(default)
3707 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3708 default_obj.specs = self.specs
3709 default_obj._value = default_value
3710 self.default = default_obj
3712 self._value = default_obj.copy()._value
3714 def _value_sanitize(self, value):
3715 if not issubclass(value.__class__, Sequence):
3716 raise InvalidValueType((Sequence,))
3721 for name, spec in self.specs.items():
3722 value = self._value.get(name)
3733 obj = self.__class__(schema=self.specs)
3735 obj._expl = self._expl
3736 obj.default = self.default
3737 obj.optional = self.optional
3738 obj.offset = self.offset
3739 obj.llen = self.llen
3740 obj.vlen = self.vlen
3741 obj._value = {k: v.copy() for k, v in self._value.items()}
3744 def __eq__(self, their):
3745 if not isinstance(their, self.__class__):
3748 self.specs == their.specs and
3749 self.tag == their.tag and
3750 self._expl == their._expl and
3751 self._value == their._value
3762 return self.__class__(
3765 impl=self.tag if impl is None else impl,
3766 expl=self._expl if expl is None else expl,
3767 default=self.default if default is None else default,
3768 optional=self.optional if optional is None else optional,
3771 def __contains__(self, key):
3772 return key in self._value
3774 def __setitem__(self, key, value):
3775 spec = self.specs.get(key)
3777 raise ObjUnknown(key)
3779 self._value.pop(key, None)
3781 if not isinstance(value, spec.__class__):
3782 raise InvalidValueType((spec.__class__,))
3783 value = spec(value=value)
3784 if spec.default is not None and value == spec.default:
3785 self._value.pop(key, None)
3787 self._value[key] = value
3789 def __getitem__(self, key):
3790 value = self._value.get(key)
3791 if value is not None:
3793 spec = self.specs.get(key)
3795 raise ObjUnknown(key)
3796 if spec.default is not None:
3800 def _encoded_values(self):
3802 for name, spec in self.specs.items():
3803 value = self._value.get(name)
3807 raise ObjNotReady(name)
3808 raws.append(value.encode())
3812 v = b"".join(self._encoded_values())
3813 return b"".join((self.tag, len_encode(len(v)), v))
3815 def _decode(self, tlv, offset, decode_path, ctx):
3817 t, tlen, lv = tag_strip(tlv)
3818 except DecodeError as err:
3819 raise err.__class__(
3821 klass=self.__class__,
3822 decode_path=decode_path,
3827 klass=self.__class__,
3828 decode_path=decode_path,
3832 l, llen, v = len_decode(lv)
3833 except DecodeError as err:
3834 raise err.__class__(
3836 klass=self.__class__,
3837 decode_path=decode_path,
3841 raise NotEnoughData(
3842 "encoded length is longer than data",
3843 klass=self.__class__,
3844 decode_path=decode_path,
3847 v, tail = v[:l], v[l:]
3848 sub_offset = offset + tlen + llen
3850 for name, spec in self.specs.items():
3851 if len(v) == 0 and spec.optional:
3853 sub_decode_path = decode_path + (name,)
3855 value, v_tail = spec.decode(
3859 decode_path=sub_decode_path,
3867 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
3868 if defined is not None:
3869 defined_by, defined_spec = defined
3870 if issubclass(value.__class__, SequenceOf):
3871 for i, _value in enumerate(value):
3872 sub_sub_decode_path = sub_decode_path + (
3874 decode_path_defby(defined_by),
3876 defined_value, defined_tail = defined_spec.decode(
3877 memoryview(bytes(_value)),
3879 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
3880 if value.expled else (value.tlen + value.llen)
3883 decode_path=sub_sub_decode_path,
3886 if len(defined_tail) > 0:
3889 klass=self.__class__,
3890 decode_path=sub_sub_decode_path,
3893 _value.defined = (defined_by, defined_value)
3895 defined_value, defined_tail = defined_spec.decode(
3896 memoryview(bytes(value)),
3898 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
3899 if value.expled else (value.tlen + value.llen)
3902 decode_path=sub_decode_path + (decode_path_defby(defined_by),),
3905 if len(defined_tail) > 0:
3908 klass=self.__class__,
3909 decode_path=sub_decode_path + (decode_path_defby(defined_by),),
3912 value.defined = (defined_by, defined_value)
3914 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3916 if spec.default is not None and value == spec.default:
3917 if ctx.get("strict_default_existence", False):
3919 "DEFAULT value met",
3920 klass=self.__class__,
3921 decode_path=sub_decode_path,
3926 values[name] = value
3928 spec_defines = getattr(spec, "defines", ())
3929 if len(spec_defines) == 0:
3930 defines_by_path = ctx.get("defines_by_path", ())
3931 if len(defines_by_path) > 0:
3932 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
3933 if spec_defines is not None and len(spec_defines) > 0:
3934 for rel_path, schema in spec_defines:
3935 defined = schema.get(value, None)
3936 if defined is not None:
3937 ctx.setdefault("defines", []).append((
3938 abs_decode_path(sub_decode_path[:-1], rel_path),
3944 klass=self.__class__,
3945 decode_path=decode_path,
3948 obj = self.__class__(
3952 default=self.default,
3953 optional=self.optional,
3954 _decoded=(offset, llen, l),
3960 value = pp_console_row(next(self.pps()))
3962 for name in self.specs:
3963 _value = self._value.get(name)
3966 cols.append(repr(_value))
3967 return "%s[%s]" % (value, ", ".join(cols))
3969 def pps(self, decode_path=()):
3971 asn1_type_name=self.asn1_type_name,
3972 obj_name=self.__class__.__name__,
3973 decode_path=decode_path,
3974 optional=self.optional,
3975 default=self == self.default,
3976 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3977 expl=None if self._expl is None else tag_decode(self._expl),
3982 expl_offset=self.expl_offset if self.expled else None,
3983 expl_tlen=self.expl_tlen if self.expled else None,
3984 expl_llen=self.expl_llen if self.expled else None,
3985 expl_vlen=self.expl_vlen if self.expled else None,
3987 for name in self.specs:
3988 value = self._value.get(name)
3991 yield value.pps(decode_path=decode_path + (name,))
3994 class Set(Sequence):
3995 """``SET`` structure type
3997 Its usage is identical to :py:class:`pyderasn.Sequence`.
4000 tag_default = tag_encode(form=TagFormConstructed, num=17)
4001 asn1_type_name = "SET"
4004 raws = self._encoded_values()
4007 return b"".join((self.tag, len_encode(len(v)), v))
4009 def _decode(self, tlv, offset, decode_path, ctx):
4011 t, tlen, lv = tag_strip(tlv)
4012 except DecodeError as err:
4013 raise err.__class__(
4015 klass=self.__class__,
4016 decode_path=decode_path,
4021 klass=self.__class__,
4022 decode_path=decode_path,
4026 l, llen, v = len_decode(lv)
4027 except DecodeError as err:
4028 raise err.__class__(
4030 klass=self.__class__,
4031 decode_path=decode_path,
4035 raise NotEnoughData(
4036 "encoded length is longer than data",
4037 klass=self.__class__,
4040 v, tail = v[:l], v[l:]
4041 sub_offset = offset + tlen + llen
4043 specs_items = self.specs.items
4045 for name, spec in specs_items():
4047 value, v_tail = spec.decode(
4051 decode_path=decode_path + (name,),
4057 value.expl_tlvlen if value.expled else value.tlvlen
4060 if spec.default is None or value != spec.default: # pragma: no cover
4061 # SeqMixing.test_encoded_default_accepted covers that place
4062 values[name] = value
4066 klass=self.__class__,
4067 decode_path=decode_path,
4070 obj = self.__class__(
4074 default=self.default,
4075 optional=self.optional,
4076 _decoded=(offset, llen, l),
4082 class SequenceOf(Obj):
4083 """``SEQUENCE OF`` sequence type
4085 For that kind of type you must specify the object it will carry on
4086 (bounds are for example here, not required)::
4088 class Ints(SequenceOf):
4093 >>> ints.append(Integer(123))
4094 >>> ints.append(Integer(234))
4096 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4097 >>> [int(i) for i in ints]
4099 >>> ints.append(Integer(345))
4100 Traceback (most recent call last):
4101 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4104 >>> ints[1] = Integer(345)
4106 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4108 Also you can initialize sequence with preinitialized values:
4110 >>> ints = Ints([Integer(123), Integer(234)])
4112 __slots__ = ("spec", "_bound_min", "_bound_max")
4113 tag_default = tag_encode(form=TagFormConstructed, num=16)
4114 asn1_type_name = "SEQUENCE OF"
4127 super(SequenceOf, self).__init__(
4135 schema = getattr(self, "schema", None)
4137 raise ValueError("schema must be specified")
4139 self._bound_min, self._bound_max = getattr(
4143 ) if bounds is None else bounds
4145 if value is not None:
4146 self._value = self._value_sanitize(value)
4147 if default is not None:
4148 default_value = self._value_sanitize(default)
4149 default_obj = self.__class__(
4154 default_obj._value = default_value
4155 self.default = default_obj
4157 self._value = default_obj.copy()._value
4159 def _value_sanitize(self, value):
4160 if issubclass(value.__class__, SequenceOf):
4161 value = value._value
4162 elif hasattr(value, "__iter__"):
4165 raise InvalidValueType((self.__class__, iter))
4166 if not self._bound_min <= len(value) <= self._bound_max:
4167 raise BoundsError(self._bound_min, len(value), self._bound_max)
4169 if not isinstance(v, self.spec.__class__):
4170 raise InvalidValueType((self.spec.__class__,))
4175 return all(v.ready for v in self._value)
4178 obj = self.__class__(schema=self.spec)
4179 obj._bound_min = self._bound_min
4180 obj._bound_max = self._bound_max
4182 obj._expl = self._expl
4183 obj.default = self.default
4184 obj.optional = self.optional
4185 obj.offset = self.offset
4186 obj.llen = self.llen
4187 obj.vlen = self.vlen
4188 obj._value = [v.copy() for v in self._value]
4191 def __eq__(self, their):
4192 if isinstance(their, self.__class__):
4194 self.spec == their.spec and
4195 self.tag == their.tag and
4196 self._expl == their._expl and
4197 self._value == their._value
4199 if hasattr(their, "__iter__"):
4200 return self._value == list(their)
4212 return self.__class__(
4216 (self._bound_min, self._bound_max)
4217 if bounds is None else bounds
4219 impl=self.tag if impl is None else impl,
4220 expl=self._expl if expl is None else expl,
4221 default=self.default if default is None else default,
4222 optional=self.optional if optional is None else optional,
4225 def __contains__(self, key):
4226 return key in self._value
4228 def append(self, value):
4229 if not isinstance(value, self.spec.__class__):
4230 raise InvalidValueType((self.spec.__class__,))
4231 if len(self._value) + 1 > self._bound_max:
4234 len(self._value) + 1,
4237 self._value.append(value)
4240 self._assert_ready()
4241 return iter(self._value)
4244 self._assert_ready()
4245 return len(self._value)
4247 def __setitem__(self, key, value):
4248 if not isinstance(value, self.spec.__class__):
4249 raise InvalidValueType((self.spec.__class__,))
4250 self._value[key] = self.spec(value=value)
4252 def __getitem__(self, key):
4253 return self._value[key]
4255 def _encoded_values(self):
4256 return [v.encode() for v in self._value]
4259 v = b"".join(self._encoded_values())
4260 return b"".join((self.tag, len_encode(len(v)), v))
4262 def _decode(self, tlv, offset, decode_path, ctx):
4264 t, tlen, lv = tag_strip(tlv)
4265 except DecodeError as err:
4266 raise err.__class__(
4268 klass=self.__class__,
4269 decode_path=decode_path,
4274 klass=self.__class__,
4275 decode_path=decode_path,
4279 l, llen, v = len_decode(lv)
4280 except DecodeError as err:
4281 raise err.__class__(
4283 klass=self.__class__,
4284 decode_path=decode_path,
4288 raise NotEnoughData(
4289 "encoded length is longer than data",
4290 klass=self.__class__,
4291 decode_path=decode_path,
4294 v, tail = v[:l], v[l:]
4295 sub_offset = offset + tlen + llen
4299 value, v_tail = spec.decode(
4303 decode_path=decode_path + (str(len(_value)),),
4306 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4308 _value.append(value)
4309 obj = self.__class__(
4312 bounds=(self._bound_min, self._bound_max),
4315 default=self.default,
4316 optional=self.optional,
4317 _decoded=(offset, llen, l),
4323 pp_console_row(next(self.pps())),
4324 ", ".join(repr(v) for v in self._value),
4327 def pps(self, decode_path=()):
4329 asn1_type_name=self.asn1_type_name,
4330 obj_name=self.__class__.__name__,
4331 decode_path=decode_path,
4332 optional=self.optional,
4333 default=self == self.default,
4334 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4335 expl=None if self._expl is None else tag_decode(self._expl),
4340 expl_offset=self.expl_offset if self.expled else None,
4341 expl_tlen=self.expl_tlen if self.expled else None,
4342 expl_llen=self.expl_llen if self.expled else None,
4343 expl_vlen=self.expl_vlen if self.expled else None,
4345 for i, value in enumerate(self._value):
4346 yield value.pps(decode_path=decode_path + (str(i),))
4349 class SetOf(SequenceOf):
4350 """``SET OF`` sequence type
4352 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4355 tag_default = tag_encode(form=TagFormConstructed, num=17)
4356 asn1_type_name = "SET OF"
4359 raws = self._encoded_values()
4362 return b"".join((self.tag, len_encode(len(v)), v))
4365 def obj_by_path(pypath): # pragma: no cover
4366 """Import object specified as string Python path
4368 Modules must be separated from classes/functions with ``:``.
4370 >>> obj_by_path("foo.bar:Baz")
4371 <class 'foo.bar.Baz'>
4372 >>> obj_by_path("foo.bar:Baz.boo")
4373 <classmethod 'foo.bar.Baz.boo'>
4375 mod, objs = pypath.rsplit(":", 1)
4376 from importlib import import_module
4377 obj = import_module(mod)
4378 for obj_name in objs.split("."):
4379 obj = getattr(obj, obj_name)
4383 def generic_decoder(): # pragma: no cover
4384 # All of this below is a big hack with self references
4385 choice = PrimitiveTypes()
4386 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4387 choice.specs["SetOf"] = SetOf(schema=choice)
4389 choice.specs["SequenceOf%d" % i] = SequenceOf(
4393 choice.specs["Any"] = Any()
4395 # Class name equals to type name, to omit it from output
4396 class SEQUENCEOF(SequenceOf):
4400 def pprint_any(obj, oids=None):
4401 def _pprint_pps(pps):
4403 if hasattr(pp, "_fields"):
4404 if pp.asn1_type_name == Choice.asn1_type_name:
4406 pp_kwargs = pp._asdict()
4407 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4408 pp = _pp(**pp_kwargs)
4409 yield pp_console_row(
4415 for row in pp_console_blob(pp):
4418 for row in _pprint_pps(pp):
4420 return "\n".join(_pprint_pps(obj.pps()))
4421 return SEQUENCEOF(), pprint_any
4424 def main(): # pragma: no cover
4426 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4427 parser.add_argument(
4431 help="Skip that number of bytes from the beginning",
4433 parser.add_argument(
4435 help="Python path to dictionary with OIDs",
4437 parser.add_argument(
4439 help="Python path to schema definition to use",
4441 parser.add_argument(
4442 "--defines-by-path",
4443 help="Python path to decoder's defines_by_path",
4445 parser.add_argument(
4447 type=argparse.FileType("rb"),
4448 help="Path to DER file you want to decode",
4450 args = parser.parse_args()
4451 args.DERFile.seek(args.skip)
4452 der = memoryview(args.DERFile.read())
4453 args.DERFile.close()
4454 oids = obj_by_path(args.oids) if args.oids else {}
4456 schema = obj_by_path(args.schema)
4457 from functools import partial
4458 pprinter = partial(pprint, big_blobs=True)
4460 schema, pprinter = generic_decoder()
4461 obj, tail = schema().decode(
4464 None if args.defines_by_path is None else
4465 {"defines_by_path": obj_by_path(args.defines_by_path)}
4468 print(pprinter(obj, oids=oids))
4470 print("\nTrailing data: %s" % hexenc(tail))
4473 if __name__ == "__main__":