3 # PyDERASN -- Python ASN.1 DER/BER 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/BER codec with abstract structures
21 This library allows you to marshal various structures in ASN.1 DER
22 format, unmarshal them in BER/CER/DER ones.
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:
193 * ``expled`` -- to know if explicit tag is set
194 * ``expl_offset`` (it is lesser than ``offset``)
197 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
198 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
200 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
203 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
210 You can specify so called context keyword argument during ``decode()``
211 invocation. It is dictionary containing various options governing
214 Currently available context options:
216 * :ref:`allow_default_values <allow_default_values_ctx>`
217 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
218 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
219 * :ref:`bered <bered_ctx>`
220 * :ref:`defines_by_path <defines_by_path_ctx>`
227 All objects have ``pps()`` method, that is a generator of
228 :py:class:`pyderasn.PP` namedtuple, holding various raw information
229 about the object. If ``pps`` is called on sequences, then all underlying
230 ``PP`` will be yielded.
232 You can use :py:func:`pyderasn.pp_console_row` function, converting
233 those ``PP`` to human readable string. Actually exactly it is used for
234 all object ``repr``. But it is easy to write custom formatters.
236 >>> from pyderasn import pprint
237 >>> encoded = Integer(-12345).encode()
238 >>> obj, tail = Integer().decode(encoded)
239 >>> print(pprint(obj))
240 0 [1,1, 2] INTEGER -12345
247 ASN.1 structures often have ANY and OCTET STRING fields, that are
248 DEFINED BY some previously met ObjectIdentifier. This library provides
249 ability to specify mapping between some OID and field that must be
250 decoded with specific specification.
255 :py:class:`pyderasn.ObjectIdentifier` field inside
256 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
257 necessary for decoding structures. For example, CMS (:rfc:`5652`)
260 class ContentInfo(Sequence):
262 ("contentType", ContentType(defines=((("content",), {
263 id_digestedData: DigestedData(),
264 id_signedData: SignedData(),
266 ("content", Any(expl=tag_ctxc(0))),
269 ``contentType`` field tells that it defines that ``content`` must be
270 decoded with ``SignedData`` specification, if ``contentType`` equals to
271 ``id-signedData``. The same applies to ``DigestedData``. If
272 ``contentType`` contains unknown OID, then no automatic decoding is
275 You can specify multiple fields, that will be autodecoded -- that is why
276 ``defines`` kwarg is a sequence. You can specify defined field
277 relatively or absolutely to current decode path. For example ``defines``
278 for AlgorithmIdentifier of X.509's
279 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
283 id_ecPublicKey: ECParameters(),
284 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
286 (("..", "subjectPublicKey"), {
287 id_rsaEncryption: RSAPublicKey(),
288 id_GostR3410_2001: OctetString(),
292 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
293 autodecode its parameters inside SPKI's algorithm and its public key
296 Following types can be automatically decoded (DEFINED BY):
298 * :py:class:`pyderasn.Any`
299 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
300 * :py:class:`pyderasn.OctetString`
301 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
302 ``Any``/``BitString``/``OctetString``-s
304 When any of those fields is automatically decoded, then ``.defined``
305 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
306 was defined, ``value`` contains corresponding decoded value. For example
307 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
309 .. _defines_by_path_ctx:
311 defines_by_path context option
312 ______________________________
314 Sometimes you either can not or do not want to explicitly set *defines*
315 in the scheme. You can dynamically apply those definitions when calling
316 ``.decode()`` method.
318 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
319 value must be sequence of following tuples::
321 (decode_path, defines)
323 where ``decode_path`` is a tuple holding so-called decode path to the
324 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
325 ``defines``, holding exactly the same value as accepted in its keyword
328 For example, again for CMS, you want to automatically decode
329 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
330 structures it may hold. Also, automatically decode ``controlSequence``
333 content_info, tail = ContentInfo().decode(data, defines_by_path=(
336 ((("content",), {id_signedData: SignedData()}),),
341 DecodePathDefBy(id_signedData),
346 id_cct_PKIData: PKIData(),
347 id_cct_PKIResponse: PKIResponse(),
353 DecodePathDefBy(id_signedData),
356 DecodePathDefBy(id_cct_PKIResponse),
362 id_cmc_recipientNonce: RecipientNonce(),
363 id_cmc_senderNonce: SenderNonce(),
364 id_cmc_statusInfoV2: CMCStatusInfoV2(),
365 id_cmc_transactionId: TransactionId(),
370 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
371 First function is useful for path construction when some automatic
372 decoding is already done. ``any`` means literally any value it meet --
373 useful for SEQUENCE/SET OF-s.
380 By default PyDERASN accepts only DER encoded data. It always encodes to
381 DER. But you can optionally enable BER decoding with setting ``bered``
382 :ref:`context <ctx>` argument to True. Indefinite lengths and
383 constructed primitive types should be parsed successfully.
385 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
386 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
387 STRING``, ``SEQUENCE``, ``SET``, ``SET OF`` can contain it.
388 * If object has an indefinite length encoding, then its ``lenindef``
389 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
390 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
392 * If object has an indefinite length encoded explicit tag, then
393 ``expl_lenindef`` is set to True.
394 * If object has either any of BER-related encoding (explicit tag
395 indefinite length, object's indefinite length, BER-encoding) or any
396 underlying component has that kind of encoding, then ``bered``
397 attribute is set to True. For example SignedData CMS can have
398 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
399 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
401 EOC (end-of-contents) token's length is taken in advance in object's
404 .. _allow_expl_oob_ctx:
406 Allow explicit tag out-of-bound
407 -------------------------------
409 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
410 one value, more than one object. If you set ``allow_expl_oob`` context
411 option to True, then no error will be raised and that invalid encoding
412 will be silently further processed. But pay attention that offsets and
413 lengths will be invalid in that case.
417 This option should be used only for skipping some decode errors, just
418 to see the decoded structure somehow.
425 .. autoclass:: pyderasn.Boolean
430 .. autoclass:: pyderasn.Integer
435 .. autoclass:: pyderasn.BitString
440 .. autoclass:: pyderasn.OctetString
445 .. autoclass:: pyderasn.Null
450 .. autoclass:: pyderasn.ObjectIdentifier
455 .. autoclass:: pyderasn.Enumerated
459 .. autoclass:: pyderasn.CommonString
463 .. autoclass:: pyderasn.NumericString
467 .. autoclass:: pyderasn.UTCTime
468 :members: __init__, todatetime
472 .. autoclass:: pyderasn.GeneralizedTime
479 .. autoclass:: pyderasn.Choice
484 .. autoclass:: PrimitiveTypes
488 .. autoclass:: pyderasn.Any
496 .. autoclass:: pyderasn.Sequence
501 .. autoclass:: pyderasn.Set
506 .. autoclass:: pyderasn.SequenceOf
511 .. autoclass:: pyderasn.SetOf
517 .. autofunction:: pyderasn.abs_decode_path
518 .. autofunction:: pyderasn.colonize_hex
519 .. autofunction:: pyderasn.hexenc
520 .. autofunction:: pyderasn.hexdec
521 .. autofunction:: pyderasn.tag_encode
522 .. autofunction:: pyderasn.tag_decode
523 .. autofunction:: pyderasn.tag_ctxp
524 .. autofunction:: pyderasn.tag_ctxc
525 .. autoclass:: pyderasn.Obj
526 .. autoclass:: pyderasn.DecodeError
528 .. autoclass:: pyderasn.NotEnoughData
529 .. autoclass:: pyderasn.LenIndefForm
530 .. autoclass:: pyderasn.TagMismatch
531 .. autoclass:: pyderasn.InvalidLength
532 .. autoclass:: pyderasn.InvalidOID
533 .. autoclass:: pyderasn.ObjUnknown
534 .. autoclass:: pyderasn.ObjNotReady
535 .. autoclass:: pyderasn.InvalidValueType
536 .. autoclass:: pyderasn.BoundsError
539 from codecs import getdecoder
540 from codecs import getencoder
541 from collections import namedtuple
542 from collections import OrderedDict
543 from copy import copy
544 from datetime import datetime
545 from math import ceil
546 from os import environ
547 from string import ascii_letters
548 from string import digits
550 from six import add_metaclass
551 from six import binary_type
552 from six import byte2int
553 from six import indexbytes
554 from six import int2byte
555 from six import integer_types
556 from six import iterbytes
558 from six import string_types
559 from six import text_type
560 from six import unichr as six_unichr
561 from six.moves import xrange as six_xrange
565 from termcolor import colored
566 except ImportError: # pragma: no cover
567 def colored(what, *args):
611 "TagClassApplication",
615 "TagFormConstructed",
626 TagClassUniversal = 0
627 TagClassApplication = 1 << 6
628 TagClassContext = 1 << 7
629 TagClassPrivate = 1 << 6 | 1 << 7
631 TagFormConstructed = 1 << 5
634 TagClassApplication: "APPLICATION ",
635 TagClassPrivate: "PRIVATE ",
636 TagClassUniversal: "UNIV ",
640 LENINDEF = b"\x80" # length indefinite mark
641 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
644 ########################################################################
646 ########################################################################
648 class ASN1Error(ValueError):
652 class DecodeError(ASN1Error):
653 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
655 :param str msg: reason of decode failing
656 :param klass: optional exact DecodeError inherited class (like
657 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
658 :py:exc:`InvalidLength`)
659 :param decode_path: tuple of strings. It contains human
660 readable names of the fields through which
661 decoding process has passed
662 :param int offset: binary offset where failure happened
664 super(DecodeError, self).__init__()
667 self.decode_path = decode_path
673 "" if self.klass is None else self.klass.__name__,
675 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
676 if len(self.decode_path) > 0 else ""
678 ("(at %d)" % self.offset) if self.offset > 0 else "",
684 return "%s(%s)" % (self.__class__.__name__, self)
687 class NotEnoughData(DecodeError):
691 class LenIndefForm(DecodeError):
695 class TagMismatch(DecodeError):
699 class InvalidLength(DecodeError):
703 class InvalidOID(DecodeError):
707 class ObjUnknown(ASN1Error):
708 def __init__(self, name):
709 super(ObjUnknown, self).__init__()
713 return "object is unknown: %s" % self.name
716 return "%s(%s)" % (self.__class__.__name__, self)
719 class ObjNotReady(ASN1Error):
720 def __init__(self, name):
721 super(ObjNotReady, self).__init__()
725 return "object is not ready: %s" % self.name
728 return "%s(%s)" % (self.__class__.__name__, self)
731 class InvalidValueType(ASN1Error):
732 def __init__(self, expected_types):
733 super(InvalidValueType, self).__init__()
734 self.expected_types = expected_types
737 return "invalid value type, expected: %s" % ", ".join(
738 [repr(t) for t in self.expected_types]
742 return "%s(%s)" % (self.__class__.__name__, self)
745 class BoundsError(ASN1Error):
746 def __init__(self, bound_min, value, bound_max):
747 super(BoundsError, self).__init__()
748 self.bound_min = bound_min
750 self.bound_max = bound_max
753 return "unsatisfied bounds: %s <= %s <= %s" % (
760 return "%s(%s)" % (self.__class__.__name__, self)
763 ########################################################################
765 ########################################################################
767 _hexdecoder = getdecoder("hex")
768 _hexencoder = getencoder("hex")
772 """Binary data to hexadecimal string convert
774 return _hexdecoder(data)[0]
778 """Hexadecimal string to binary data convert
780 return _hexencoder(data)[0].decode("ascii")
783 def int_bytes_len(num, byte_len=8):
786 return int(ceil(float(num.bit_length()) / byte_len))
789 def zero_ended_encode(num):
790 octets = bytearray(int_bytes_len(num, 7))
792 octets[i] = num & 0x7F
796 octets[i] = 0x80 | (num & 0x7F)
802 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
803 """Encode tag to binary form
805 :param int num: tag's number
806 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
807 :py:data:`pyderasn.TagClassContext`,
808 :py:data:`pyderasn.TagClassApplication`,
809 :py:data:`pyderasn.TagClassPrivate`)
810 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
811 :py:data:`pyderasn.TagFormConstructed`)
815 return int2byte(klass | form | num)
816 # [XX|X|11111][1.......][1.......] ... [0.......]
817 return int2byte(klass | form | 31) + zero_ended_encode(num)
821 """Decode tag from binary form
825 No validation is performed, assuming that it has already passed.
827 It returns tuple with three integers, as
828 :py:func:`pyderasn.tag_encode` accepts.
830 first_octet = byte2int(tag)
831 klass = first_octet & 0xC0
832 form = first_octet & 0x20
833 if first_octet & 0x1F < 0x1F:
834 return (klass, form, first_octet & 0x1F)
836 for octet in iterbytes(tag[1:]):
839 return (klass, form, num)
843 """Create CONTEXT PRIMITIVE tag
845 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
849 """Create CONTEXT CONSTRUCTED tag
851 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
855 """Take off tag from the data
857 :returns: (encoded tag, tag length, remaining data)
860 raise NotEnoughData("no data at all")
861 if byte2int(data) & 0x1F < 31:
862 return data[:1], 1, data[1:]
867 raise DecodeError("unfinished tag")
868 if indexbytes(data, i) & 0x80 == 0:
871 return data[:i], i, data[i:]
877 octets = bytearray(int_bytes_len(l) + 1)
878 octets[0] = 0x80 | (len(octets) - 1)
879 for i in six_xrange(len(octets) - 1, 0, -1):
885 def len_decode(data):
888 :returns: (decoded length, length's length, remaining data)
889 :raises LenIndefForm: if indefinite form encoding is met
892 raise NotEnoughData("no data at all")
893 first_octet = byte2int(data)
894 if first_octet & 0x80 == 0:
895 return first_octet, 1, data[1:]
896 octets_num = first_octet & 0x7F
897 if octets_num + 1 > len(data):
898 raise NotEnoughData("encoded length is longer than data")
901 if byte2int(data[1:]) == 0:
902 raise DecodeError("leading zeros")
904 for v in iterbytes(data[1:1 + octets_num]):
907 raise DecodeError("long form instead of short one")
908 return l, 1 + octets_num, data[1 + octets_num:]
911 ########################################################################
913 ########################################################################
915 class AutoAddSlots(type):
916 def __new__(mcs, name, bases, _dict):
917 _dict["__slots__"] = _dict.get("__slots__", ())
918 return type.__new__(mcs, name, bases, _dict)
921 @add_metaclass(AutoAddSlots)
923 """Common ASN.1 object class
925 All ASN.1 types are inherited from it. It has metaclass that
926 automatically adds ``__slots__`` to all inherited classes.
950 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
951 self._expl = getattr(self, "expl", None) if expl is None else expl
952 if self.tag != self.tag_default and self._expl is not None:
953 raise ValueError("implicit and explicit tags can not be set simultaneously")
954 if default is not None:
956 self.optional = optional
957 self.offset, self.llen, self.vlen = _decoded
959 self.expl_lenindef = False
960 self.lenindef = False
961 self.ber_encoded = False
964 def ready(self): # pragma: no cover
965 """Is object ready to be encoded?
967 raise NotImplementedError()
969 def _assert_ready(self):
971 raise ObjNotReady(self.__class__.__name__)
975 """Is either object or any elements inside is BER encoded?
977 return self.expl_lenindef or self.lenindef or self.ber_encoded
981 """Is object decoded?
983 return (self.llen + self.vlen) > 0
985 def copy(self): # pragma: no cover
986 """Make a copy of object, safe to be mutated
988 raise NotImplementedError()
996 return self.tlen + self.llen + self.vlen
998 def __str__(self): # pragma: no cover
999 return self.__bytes__() if PY2 else self.__unicode__()
1001 def __ne__(self, their):
1002 return not(self == their)
1004 def __gt__(self, their): # pragma: no cover
1005 return not(self < their)
1007 def __le__(self, their): # pragma: no cover
1008 return (self == their) or (self < their)
1010 def __ge__(self, their): # pragma: no cover
1011 return (self == their) or (self > their)
1013 def _encode(self): # pragma: no cover
1014 raise NotImplementedError()
1016 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1017 raise NotImplementedError()
1020 raw = self._encode()
1021 if self._expl is None:
1023 return b"".join((self._expl, len_encode(len(raw)), raw))
1033 _ctx_immutable=True,
1037 :param data: either binary or memoryview
1038 :param int offset: initial data's offset
1039 :param bool leavemm: do we need to leave memoryview of remaining
1040 data as is, or convert it to bytes otherwise
1041 :param ctx: optional :ref:`context <ctx>` governing decoding process
1042 :param tag_only: decode only the tag, without length and contents
1043 (used only in Choice and Set structures, trying to
1044 determine if tag satisfies the scheme)
1045 :param _ctx_immutable: do we need to copy ``ctx`` before using it
1046 :returns: (Obj, remaining data)
1050 elif _ctx_immutable:
1052 tlv = memoryview(data)
1053 if self._expl is None:
1054 result = self._decode(
1057 decode_path=decode_path,
1066 t, tlen, lv = tag_strip(tlv)
1067 except DecodeError as err:
1068 raise err.__class__(
1070 klass=self.__class__,
1071 decode_path=decode_path,
1076 klass=self.__class__,
1077 decode_path=decode_path,
1081 l, llen, v = len_decode(lv)
1082 except LenIndefForm as err:
1083 if not ctx.get("bered", False):
1084 raise err.__class__(
1086 klass=self.__class__,
1087 decode_path=decode_path,
1091 offset += tlen + llen
1092 result = self._decode(
1095 decode_path=decode_path,
1099 if tag_only: # pragma: no cover
1102 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1103 if eoc_expected.tobytes() != EOC:
1106 klass=self.__class__,
1107 decode_path=decode_path,
1111 obj.expl_lenindef = True
1112 except DecodeError as err:
1113 raise err.__class__(
1115 klass=self.__class__,
1116 decode_path=decode_path,
1121 raise NotEnoughData(
1122 "encoded length is longer than data",
1123 klass=self.__class__,
1124 decode_path=decode_path,
1127 result = self._decode(
1129 offset=offset + tlen + llen,
1130 decode_path=decode_path,
1134 if tag_only: # pragma: no cover
1137 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1139 "explicit tag out-of-bound, longer than data",
1140 klass=self.__class__,
1141 decode_path=decode_path,
1144 return obj, (tail if leavemm else tail.tobytes())
1148 return self._expl is not None
1155 def expl_tlen(self):
1156 return len(self._expl)
1159 def expl_llen(self):
1160 if self.expl_lenindef:
1162 return len(len_encode(self.tlvlen))
1165 def expl_offset(self):
1166 return self.offset - self.expl_tlen - self.expl_llen
1169 def expl_vlen(self):
1173 def expl_tlvlen(self):
1174 return self.expl_tlen + self.expl_llen + self.expl_vlen
1177 def fulloffset(self):
1178 return self.expl_offset if self.expled else self.offset
1182 return self.expl_tlvlen if self.expled else self.tlvlen
1184 def pps_lenindef(self, decode_path):
1185 if self.lenindef and not (
1186 getattr(self, "defined", None) is not None and
1187 self.defined[1].lenindef
1190 asn1_type_name="EOC",
1192 decode_path=decode_path,
1194 self.offset + self.tlvlen -
1195 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1203 if self.expl_lenindef:
1205 asn1_type_name="EOC",
1206 obj_name="EXPLICIT",
1207 decode_path=decode_path,
1208 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1217 class DecodePathDefBy(object):
1218 """DEFINED BY representation inside decode path
1220 __slots__ = ("defined_by",)
1222 def __init__(self, defined_by):
1223 self.defined_by = defined_by
1225 def __ne__(self, their):
1226 return not(self == their)
1228 def __eq__(self, their):
1229 if not isinstance(their, self.__class__):
1231 return self.defined_by == their.defined_by
1234 return "DEFINED BY " + str(self.defined_by)
1237 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1240 ########################################################################
1242 ########################################################################
1244 PP = namedtuple("PP", (
1272 asn1_type_name="unknown",
1289 expl_lenindef=False,
1320 def _colourize(what, colour, with_colours, attrs=("bold",)):
1321 return colored(what, colour, attrs=attrs) if with_colours else what
1324 def colonize_hex(hexed):
1325 """Separate hexadecimal string with colons
1327 return ":".join(hexed[i:i + 2] for i in range(0, len(hexed), 2))
1336 with_decode_path=False,
1337 decode_path_len_decrease=0,
1344 " " if pp.expl_offset is None else
1345 ("-%d" % (pp.offset - pp.expl_offset))
1347 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1349 col = _colourize(col, "red", with_colours, ())
1350 col += _colourize("B", "red", with_colours) if pp.bered else " "
1352 col = "[%d,%d,%4d]%s" % (
1356 LENINDEF_PP_CHAR if pp.lenindef else " "
1358 col = _colourize(col, "green", with_colours, ())
1360 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1361 if decode_path_len > 0:
1362 cols.append(" ." * decode_path_len)
1363 ent = pp.decode_path[-1]
1364 if isinstance(ent, DecodePathDefBy):
1365 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1366 value = str(ent.defined_by)
1368 oids is not None and
1369 ent.defined_by.asn1_type_name ==
1370 ObjectIdentifier.asn1_type_name and
1373 cols.append(_colourize("%s:" % oids[value], "green", with_colours))
1375 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1377 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1378 if pp.expl is not None:
1379 klass, _, num = pp.expl
1380 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1381 cols.append(_colourize(col, "blue", with_colours))
1382 if pp.impl is not None:
1383 klass, _, num = pp.impl
1384 col = "[%s%d]" % (TagClassReprs[klass], num)
1385 cols.append(_colourize(col, "blue", with_colours))
1386 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1387 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1389 cols.append(_colourize("BER", "red", with_colours))
1390 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1391 if pp.value is not None:
1393 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1395 oids is not None and
1396 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1399 cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
1400 if pp.asn1_type_name == Integer.asn1_type_name:
1401 hex_repr = hex(int(pp.obj._value))[2:].upper()
1402 if len(hex_repr) % 2 != 0:
1403 hex_repr = "0" + hex_repr
1404 cols.append(_colourize(
1405 "(%s)" % colonize_hex(hex_repr),
1410 if isinstance(pp.blob, binary_type):
1411 cols.append(hexenc(pp.blob))
1412 elif isinstance(pp.blob, tuple):
1413 cols.append(", ".join(pp.blob))
1415 cols.append(_colourize("OPTIONAL", "red", with_colours))
1417 cols.append(_colourize("DEFAULT", "red", with_colours))
1418 if with_decode_path:
1419 cols.append(_colourize(
1420 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1424 return " ".join(cols)
1427 def pp_console_blob(pp, decode_path_len_decrease=0):
1428 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1429 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1430 if decode_path_len > 0:
1431 cols.append(" ." * (decode_path_len + 1))
1432 if isinstance(pp.blob, binary_type):
1433 blob = hexenc(pp.blob).upper()
1434 for i in range(0, len(blob), 32):
1435 chunk = blob[i:i + 32]
1436 yield " ".join(cols + [colonize_hex(chunk)])
1437 elif isinstance(pp.blob, tuple):
1438 yield " ".join(cols + [", ".join(pp.blob)])
1446 with_decode_path=False,
1447 decode_path_only=(),
1449 """Pretty print object
1451 :param Obj obj: object you want to pretty print
1452 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1453 from it is met, then its humand readable form is printed
1454 :param big_blobs: if large binary objects are met (like OctetString
1455 values), do we need to print them too, on separate
1457 :param with_colours: colourize output, if ``termcolor`` library
1459 :param with_decode_path: print decode path
1460 :param decode_path_only: print only that specified decode path
1462 def _pprint_pps(pps):
1464 if hasattr(pp, "_fields"):
1466 decode_path_only != () and
1468 str(p) for p in pp.decode_path[:len(decode_path_only)]
1469 ) != decode_path_only
1473 yield pp_console_row(
1478 with_colours=with_colours,
1479 with_decode_path=with_decode_path,
1480 decode_path_len_decrease=len(decode_path_only),
1482 for row in pp_console_blob(
1484 decode_path_len_decrease=len(decode_path_only),
1488 yield pp_console_row(
1493 with_colours=with_colours,
1494 with_decode_path=with_decode_path,
1495 decode_path_len_decrease=len(decode_path_only),
1498 for row in _pprint_pps(pp):
1500 return "\n".join(_pprint_pps(obj.pps()))
1503 ########################################################################
1504 # ASN.1 primitive types
1505 ########################################################################
1508 """``BOOLEAN`` boolean type
1510 >>> b = Boolean(True)
1512 >>> b == Boolean(True)
1518 tag_default = tag_encode(1)
1519 asn1_type_name = "BOOLEAN"
1531 :param value: set the value. Either boolean type, or
1532 :py:class:`pyderasn.Boolean` object
1533 :param bytes impl: override default tag with ``IMPLICIT`` one
1534 :param bytes expl: override default tag with ``EXPLICIT`` one
1535 :param default: set default value. Type same as in ``value``
1536 :param bool optional: is object ``OPTIONAL`` in sequence
1538 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1539 self._value = None if value is None else self._value_sanitize(value)
1540 if default is not None:
1541 default = self._value_sanitize(default)
1542 self.default = self.__class__(
1548 self._value = default
1550 def _value_sanitize(self, value):
1551 if issubclass(value.__class__, Boolean):
1553 if isinstance(value, bool):
1555 raise InvalidValueType((self.__class__, bool))
1559 return self._value is not None
1562 obj = self.__class__()
1563 obj._value = self._value
1565 obj._expl = self._expl
1566 obj.default = self.default
1567 obj.optional = self.optional
1568 obj.offset = self.offset
1569 obj.llen = self.llen
1570 obj.vlen = self.vlen
1573 def __nonzero__(self):
1574 self._assert_ready()
1578 self._assert_ready()
1581 def __eq__(self, their):
1582 if isinstance(their, bool):
1583 return self._value == their
1584 if not issubclass(their.__class__, Boolean):
1587 self._value == their._value and
1588 self.tag == their.tag and
1589 self._expl == their._expl
1600 return self.__class__(
1602 impl=self.tag if impl is None else impl,
1603 expl=self._expl if expl is None else expl,
1604 default=self.default if default is None else default,
1605 optional=self.optional if optional is None else optional,
1609 self._assert_ready()
1613 (b"\xFF" if self._value else b"\x00"),
1616 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1618 t, _, lv = tag_strip(tlv)
1619 except DecodeError as err:
1620 raise err.__class__(
1622 klass=self.__class__,
1623 decode_path=decode_path,
1628 klass=self.__class__,
1629 decode_path=decode_path,
1635 l, _, v = len_decode(lv)
1636 except DecodeError as err:
1637 raise err.__class__(
1639 klass=self.__class__,
1640 decode_path=decode_path,
1644 raise InvalidLength(
1645 "Boolean's length must be equal to 1",
1646 klass=self.__class__,
1647 decode_path=decode_path,
1651 raise NotEnoughData(
1652 "encoded length is longer than data",
1653 klass=self.__class__,
1654 decode_path=decode_path,
1657 first_octet = byte2int(v)
1659 if first_octet == 0:
1661 elif first_octet == 0xFF:
1663 elif ctx.get("bered", False):
1668 "unacceptable Boolean value",
1669 klass=self.__class__,
1670 decode_path=decode_path,
1673 obj = self.__class__(
1677 default=self.default,
1678 optional=self.optional,
1679 _decoded=(offset, 1, 1),
1681 obj.ber_encoded = ber_encoded
1685 return pp_console_row(next(self.pps()))
1687 def pps(self, decode_path=()):
1690 asn1_type_name=self.asn1_type_name,
1691 obj_name=self.__class__.__name__,
1692 decode_path=decode_path,
1693 value=str(self._value) if self.ready else None,
1694 optional=self.optional,
1695 default=self == self.default,
1696 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1697 expl=None if self._expl is None else tag_decode(self._expl),
1702 expl_offset=self.expl_offset if self.expled else None,
1703 expl_tlen=self.expl_tlen if self.expled else None,
1704 expl_llen=self.expl_llen if self.expled else None,
1705 expl_vlen=self.expl_vlen if self.expled else None,
1706 expl_lenindef=self.expl_lenindef,
1707 ber_encoded=self.ber_encoded,
1710 for pp in self.pps_lenindef(decode_path):
1715 """``INTEGER`` integer type
1717 >>> b = Integer(-123)
1719 >>> b == Integer(-123)
1724 >>> Integer(2, bounds=(1, 3))
1726 >>> Integer(5, bounds=(1, 3))
1727 Traceback (most recent call last):
1728 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1732 class Version(Integer):
1739 >>> v = Version("v1")
1746 {'v3': 2, 'v1': 0, 'v2': 1}
1748 __slots__ = ("specs", "_bound_min", "_bound_max")
1749 tag_default = tag_encode(2)
1750 asn1_type_name = "INTEGER"
1764 :param value: set the value. Either integer type, named value
1765 (if ``schema`` is specified in the class), or
1766 :py:class:`pyderasn.Integer` object
1767 :param bounds: set ``(MIN, MAX)`` value constraint.
1768 (-inf, +inf) by default
1769 :param bytes impl: override default tag with ``IMPLICIT`` one
1770 :param bytes expl: override default tag with ``EXPLICIT`` one
1771 :param default: set default value. Type same as in ``value``
1772 :param bool optional: is object ``OPTIONAL`` in sequence
1774 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1776 specs = getattr(self, "schema", {}) if _specs is None else _specs
1777 self.specs = specs if isinstance(specs, dict) else dict(specs)
1778 self._bound_min, self._bound_max = getattr(
1781 (float("-inf"), float("+inf")),
1782 ) if bounds is None else bounds
1783 if value is not None:
1784 self._value = self._value_sanitize(value)
1785 if default is not None:
1786 default = self._value_sanitize(default)
1787 self.default = self.__class__(
1793 if self._value is None:
1794 self._value = default
1796 def _value_sanitize(self, value):
1797 if issubclass(value.__class__, Integer):
1798 value = value._value
1799 elif isinstance(value, integer_types):
1801 elif isinstance(value, str):
1802 value = self.specs.get(value)
1804 raise ObjUnknown("integer value: %s" % value)
1806 raise InvalidValueType((self.__class__, int, str))
1807 if not self._bound_min <= value <= self._bound_max:
1808 raise BoundsError(self._bound_min, value, self._bound_max)
1813 return self._value is not None
1816 obj = self.__class__(_specs=self.specs)
1817 obj._value = self._value
1818 obj._bound_min = self._bound_min
1819 obj._bound_max = self._bound_max
1821 obj._expl = self._expl
1822 obj.default = self.default
1823 obj.optional = self.optional
1824 obj.offset = self.offset
1825 obj.llen = self.llen
1826 obj.vlen = self.vlen
1830 self._assert_ready()
1831 return int(self._value)
1834 self._assert_ready()
1837 bytes(self._expl or b"") +
1838 str(self._value).encode("ascii"),
1841 def __eq__(self, their):
1842 if isinstance(their, integer_types):
1843 return self._value == their
1844 if not issubclass(their.__class__, Integer):
1847 self._value == their._value and
1848 self.tag == their.tag and
1849 self._expl == their._expl
1852 def __lt__(self, their):
1853 return self._value < their._value
1857 for name, value in self.specs.items():
1858 if value == self._value:
1870 return self.__class__(
1873 (self._bound_min, self._bound_max)
1874 if bounds is None else bounds
1876 impl=self.tag if impl is None else impl,
1877 expl=self._expl if expl is None else expl,
1878 default=self.default if default is None else default,
1879 optional=self.optional if optional is None else optional,
1884 self._assert_ready()
1888 octets = bytearray([0])
1892 octets = bytearray()
1894 octets.append((value & 0xFF) ^ 0xFF)
1896 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1899 octets = bytearray()
1901 octets.append(value & 0xFF)
1903 if octets[-1] & 0x80 > 0:
1906 octets = bytes(octets)
1908 bytes_len = ceil(value.bit_length() / 8) or 1
1911 octets = value.to_bytes(
1916 except OverflowError:
1920 return b"".join((self.tag, len_encode(len(octets)), octets))
1922 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1924 t, _, lv = tag_strip(tlv)
1925 except DecodeError as err:
1926 raise err.__class__(
1928 klass=self.__class__,
1929 decode_path=decode_path,
1934 klass=self.__class__,
1935 decode_path=decode_path,
1941 l, llen, v = len_decode(lv)
1942 except DecodeError as err:
1943 raise err.__class__(
1945 klass=self.__class__,
1946 decode_path=decode_path,
1950 raise NotEnoughData(
1951 "encoded length is longer than data",
1952 klass=self.__class__,
1953 decode_path=decode_path,
1957 raise NotEnoughData(
1959 klass=self.__class__,
1960 decode_path=decode_path,
1963 v, tail = v[:l], v[l:]
1964 first_octet = byte2int(v)
1966 second_octet = byte2int(v[1:])
1968 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1969 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1972 "non normalized integer",
1973 klass=self.__class__,
1974 decode_path=decode_path,
1979 if first_octet & 0x80 > 0:
1980 octets = bytearray()
1981 for octet in bytearray(v):
1982 octets.append(octet ^ 0xFF)
1983 for octet in octets:
1984 value = (value << 8) | octet
1988 for octet in bytearray(v):
1989 value = (value << 8) | octet
1991 value = int.from_bytes(v, byteorder="big", signed=True)
1993 obj = self.__class__(
1995 bounds=(self._bound_min, self._bound_max),
1998 default=self.default,
1999 optional=self.optional,
2001 _decoded=(offset, llen, l),
2003 except BoundsError as err:
2006 klass=self.__class__,
2007 decode_path=decode_path,
2013 return pp_console_row(next(self.pps()))
2015 def pps(self, decode_path=()):
2018 asn1_type_name=self.asn1_type_name,
2019 obj_name=self.__class__.__name__,
2020 decode_path=decode_path,
2021 value=(self.named or str(self._value)) if self.ready else None,
2022 optional=self.optional,
2023 default=self == self.default,
2024 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2025 expl=None if self._expl is None else tag_decode(self._expl),
2030 expl_offset=self.expl_offset if self.expled else None,
2031 expl_tlen=self.expl_tlen if self.expled else None,
2032 expl_llen=self.expl_llen if self.expled else None,
2033 expl_vlen=self.expl_vlen if self.expled else None,
2034 expl_lenindef=self.expl_lenindef,
2037 for pp in self.pps_lenindef(decode_path):
2041 class BitString(Obj):
2042 """``BIT STRING`` bit string type
2044 >>> BitString(b"hello world")
2045 BIT STRING 88 bits 68656c6c6f20776f726c64
2048 >>> b == b"hello world"
2053 >>> BitString("'0A3B5F291CD'H")
2054 BIT STRING 44 bits 0a3b5f291cd0
2055 >>> b = BitString("'010110000000'B")
2056 BIT STRING 12 bits 5800
2059 >>> b[0], b[1], b[2], b[3]
2060 (False, True, False, True)
2064 [False, True, False, True, True, False, False, False, False, False, False, False]
2068 class KeyUsage(BitString):
2070 ("digitalSignature", 0),
2071 ("nonRepudiation", 1),
2072 ("keyEncipherment", 2),
2075 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2076 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2078 ['nonRepudiation', 'keyEncipherment']
2080 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2084 Pay attention that BIT STRING can be encoded both in primitive
2085 and constructed forms. Decoder always checks constructed form tag
2086 additionally to specified primitive one. If BER decoding is
2087 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2088 of DER restrictions.
2090 __slots__ = ("tag_constructed", "specs", "defined")
2091 tag_default = tag_encode(3)
2092 asn1_type_name = "BIT STRING"
2105 :param value: set the value. Either binary type, tuple of named
2106 values (if ``schema`` is specified in the class),
2107 string in ``'XXX...'B`` form, or
2108 :py:class:`pyderasn.BitString` object
2109 :param bytes impl: override default tag with ``IMPLICIT`` one
2110 :param bytes expl: override default tag with ``EXPLICIT`` one
2111 :param default: set default value. Type same as in ``value``
2112 :param bool optional: is object ``OPTIONAL`` in sequence
2114 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2115 specs = getattr(self, "schema", {}) if _specs is None else _specs
2116 self.specs = specs if isinstance(specs, dict) else dict(specs)
2117 self._value = None if value is None else self._value_sanitize(value)
2118 if default is not None:
2119 default = self._value_sanitize(default)
2120 self.default = self.__class__(
2126 self._value = default
2128 tag_klass, _, tag_num = tag_decode(self.tag)
2129 self.tag_constructed = tag_encode(
2131 form=TagFormConstructed,
2135 def _bits2octets(self, bits):
2136 if len(self.specs) > 0:
2137 bits = bits.rstrip("0")
2139 bits += "0" * ((8 - (bit_len % 8)) % 8)
2140 octets = bytearray(len(bits) // 8)
2141 for i in six_xrange(len(octets)):
2142 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2143 return bit_len, bytes(octets)
2145 def _value_sanitize(self, value):
2146 if issubclass(value.__class__, BitString):
2148 if isinstance(value, (string_types, binary_type)):
2150 isinstance(value, string_types) and
2151 value.startswith("'")
2153 if value.endswith("'B"):
2155 if not set(value) <= set(("0", "1")):
2156 raise ValueError("B's coding contains unacceptable chars")
2157 return self._bits2octets(value)
2158 elif value.endswith("'H"):
2162 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2164 if isinstance(value, binary_type):
2165 return (len(value) * 8, value)
2167 raise InvalidValueType((self.__class__, string_types, binary_type))
2168 if isinstance(value, tuple):
2171 isinstance(value[0], integer_types) and
2172 isinstance(value[1], binary_type)
2177 bit = self.specs.get(name)
2179 raise ObjUnknown("BitString value: %s" % name)
2182 return self._bits2octets("")
2184 return self._bits2octets("".join(
2185 ("1" if bit in bits else "0")
2186 for bit in six_xrange(max(bits) + 1)
2188 raise InvalidValueType((self.__class__, binary_type, string_types))
2192 return self._value is not None
2195 obj = self.__class__(_specs=self.specs)
2197 if value is not None:
2198 value = (value[0], value[1])
2201 obj._expl = self._expl
2202 obj.default = self.default
2203 obj.optional = self.optional
2204 obj.offset = self.offset
2205 obj.llen = self.llen
2206 obj.vlen = self.vlen
2210 self._assert_ready()
2211 for i in six_xrange(self._value[0]):
2216 self._assert_ready()
2217 return self._value[0]
2219 def __bytes__(self):
2220 self._assert_ready()
2221 return self._value[1]
2223 def __eq__(self, their):
2224 if isinstance(their, bytes):
2225 return self._value[1] == their
2226 if not issubclass(their.__class__, BitString):
2229 self._value == their._value and
2230 self.tag == their.tag and
2231 self._expl == their._expl
2236 return [name for name, bit in self.specs.items() if self[bit]]
2246 return self.__class__(
2248 impl=self.tag if impl is None else impl,
2249 expl=self._expl if expl is None else expl,
2250 default=self.default if default is None else default,
2251 optional=self.optional if optional is None else optional,
2255 def __getitem__(self, key):
2256 if isinstance(key, int):
2257 bit_len, octets = self._value
2261 byte2int(memoryview(octets)[key // 8:]) >>
2264 if isinstance(key, string_types):
2265 value = self.specs.get(key)
2267 raise ObjUnknown("BitString value: %s" % key)
2269 raise InvalidValueType((int, str))
2272 self._assert_ready()
2273 bit_len, octets = self._value
2276 len_encode(len(octets) + 1),
2277 int2byte((8 - bit_len % 8) % 8),
2281 def _decode_chunk(self, lv, offset, decode_path, ctx):
2283 l, llen, v = len_decode(lv)
2284 except DecodeError as err:
2285 raise err.__class__(
2287 klass=self.__class__,
2288 decode_path=decode_path,
2292 raise NotEnoughData(
2293 "encoded length is longer than data",
2294 klass=self.__class__,
2295 decode_path=decode_path,
2299 raise NotEnoughData(
2301 klass=self.__class__,
2302 decode_path=decode_path,
2305 pad_size = byte2int(v)
2306 if l == 1 and pad_size != 0:
2308 "invalid empty value",
2309 klass=self.__class__,
2310 decode_path=decode_path,
2316 klass=self.__class__,
2317 decode_path=decode_path,
2320 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2323 klass=self.__class__,
2324 decode_path=decode_path,
2327 v, tail = v[:l], v[l:]
2328 obj = self.__class__(
2329 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2332 default=self.default,
2333 optional=self.optional,
2335 _decoded=(offset, llen, l),
2339 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2341 t, tlen, lv = tag_strip(tlv)
2342 except DecodeError as err:
2343 raise err.__class__(
2345 klass=self.__class__,
2346 decode_path=decode_path,
2350 if tag_only: # pragma: no cover
2352 return self._decode_chunk(lv, offset, decode_path, ctx)
2353 if t == self.tag_constructed:
2354 if not ctx.get("bered", False):
2356 "unallowed BER constructed encoding",
2357 klass=self.__class__,
2358 decode_path=decode_path,
2361 if tag_only: # pragma: no cover
2365 l, llen, v = len_decode(lv)
2366 except LenIndefForm:
2367 llen, l, v = 1, 0, lv[1:]
2369 except DecodeError as err:
2370 raise err.__class__(
2372 klass=self.__class__,
2373 decode_path=decode_path,
2377 raise NotEnoughData(
2378 "encoded length is longer than data",
2379 klass=self.__class__,
2380 decode_path=decode_path,
2383 if not lenindef and l == 0:
2384 raise NotEnoughData(
2386 klass=self.__class__,
2387 decode_path=decode_path,
2391 sub_offset = offset + tlen + llen
2395 if v[:EOC_LEN].tobytes() == EOC:
2402 "chunk out of bounds",
2403 klass=self.__class__,
2404 decode_path=decode_path + (str(len(chunks) - 1),),
2405 offset=chunks[-1].offset,
2407 sub_decode_path = decode_path + (str(len(chunks)),)
2409 chunk, v_tail = BitString().decode(
2412 decode_path=sub_decode_path,
2415 _ctx_immutable=False,
2419 "expected BitString encoded chunk",
2420 klass=self.__class__,
2421 decode_path=sub_decode_path,
2424 chunks.append(chunk)
2425 sub_offset += chunk.tlvlen
2426 vlen += chunk.tlvlen
2428 if len(chunks) == 0:
2431 klass=self.__class__,
2432 decode_path=decode_path,
2437 for chunk_i, chunk in enumerate(chunks[:-1]):
2438 if chunk.bit_len % 8 != 0:
2440 "BitString chunk is not multiple of 8 bits",
2441 klass=self.__class__,
2442 decode_path=decode_path + (str(chunk_i),),
2443 offset=chunk.offset,
2445 values.append(bytes(chunk))
2446 bit_len += chunk.bit_len
2447 chunk_last = chunks[-1]
2448 values.append(bytes(chunk_last))
2449 bit_len += chunk_last.bit_len
2450 obj = self.__class__(
2451 value=(bit_len, b"".join(values)),
2454 default=self.default,
2455 optional=self.optional,
2457 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2459 obj.lenindef = lenindef
2460 obj.ber_encoded = True
2461 return obj, (v[EOC_LEN:] if lenindef else v)
2463 klass=self.__class__,
2464 decode_path=decode_path,
2469 return pp_console_row(next(self.pps()))
2471 def pps(self, decode_path=()):
2475 bit_len, blob = self._value
2476 value = "%d bits" % bit_len
2477 if len(self.specs) > 0:
2478 blob = tuple(self.named)
2481 asn1_type_name=self.asn1_type_name,
2482 obj_name=self.__class__.__name__,
2483 decode_path=decode_path,
2486 optional=self.optional,
2487 default=self == self.default,
2488 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2489 expl=None if self._expl is None else tag_decode(self._expl),
2494 expl_offset=self.expl_offset if self.expled else None,
2495 expl_tlen=self.expl_tlen if self.expled else None,
2496 expl_llen=self.expl_llen if self.expled else None,
2497 expl_vlen=self.expl_vlen if self.expled else None,
2498 expl_lenindef=self.expl_lenindef,
2499 lenindef=self.lenindef,
2500 ber_encoded=self.ber_encoded,
2503 defined_by, defined = self.defined or (None, None)
2504 if defined_by is not None:
2506 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2508 for pp in self.pps_lenindef(decode_path):
2512 class OctetString(Obj):
2513 """``OCTET STRING`` binary string type
2515 >>> s = OctetString(b"hello world")
2516 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2517 >>> s == OctetString(b"hello world")
2522 >>> OctetString(b"hello", bounds=(4, 4))
2523 Traceback (most recent call last):
2524 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2525 >>> OctetString(b"hell", bounds=(4, 4))
2526 OCTET STRING 4 bytes 68656c6c
2530 Pay attention that OCTET STRING can be encoded both in primitive
2531 and constructed forms. Decoder always checks constructed form tag
2532 additionally to specified primitive one. If BER decoding is
2533 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2534 of DER restrictions.
2536 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2537 tag_default = tag_encode(4)
2538 asn1_type_name = "OCTET STRING"
2551 :param value: set the value. Either binary type, or
2552 :py:class:`pyderasn.OctetString` object
2553 :param bounds: set ``(MIN, MAX)`` value size constraint.
2554 (-inf, +inf) by default
2555 :param bytes impl: override default tag with ``IMPLICIT`` one
2556 :param bytes expl: override default tag with ``EXPLICIT`` one
2557 :param default: set default value. Type same as in ``value``
2558 :param bool optional: is object ``OPTIONAL`` in sequence
2560 super(OctetString, self).__init__(
2568 self._bound_min, self._bound_max = getattr(
2572 ) if bounds is None else bounds
2573 if value is not None:
2574 self._value = self._value_sanitize(value)
2575 if default is not None:
2576 default = self._value_sanitize(default)
2577 self.default = self.__class__(
2582 if self._value is None:
2583 self._value = default
2585 tag_klass, _, tag_num = tag_decode(self.tag)
2586 self.tag_constructed = tag_encode(
2588 form=TagFormConstructed,
2592 def _value_sanitize(self, value):
2593 if issubclass(value.__class__, OctetString):
2594 value = value._value
2595 elif isinstance(value, binary_type):
2598 raise InvalidValueType((self.__class__, bytes))
2599 if not self._bound_min <= len(value) <= self._bound_max:
2600 raise BoundsError(self._bound_min, len(value), self._bound_max)
2605 return self._value is not None
2608 obj = self.__class__()
2609 obj._value = self._value
2610 obj._bound_min = self._bound_min
2611 obj._bound_max = self._bound_max
2613 obj._expl = self._expl
2614 obj.default = self.default
2615 obj.optional = self.optional
2616 obj.offset = self.offset
2617 obj.llen = self.llen
2618 obj.vlen = self.vlen
2621 def __bytes__(self):
2622 self._assert_ready()
2625 def __eq__(self, their):
2626 if isinstance(their, binary_type):
2627 return self._value == their
2628 if not issubclass(their.__class__, OctetString):
2631 self._value == their._value and
2632 self.tag == their.tag and
2633 self._expl == their._expl
2636 def __lt__(self, their):
2637 return self._value < their._value
2648 return self.__class__(
2651 (self._bound_min, self._bound_max)
2652 if bounds is None else bounds
2654 impl=self.tag if impl is None else impl,
2655 expl=self._expl if expl is None else expl,
2656 default=self.default if default is None else default,
2657 optional=self.optional if optional is None else optional,
2661 self._assert_ready()
2664 len_encode(len(self._value)),
2668 def _decode_chunk(self, lv, offset, decode_path, ctx):
2670 l, llen, v = len_decode(lv)
2671 except DecodeError as err:
2672 raise err.__class__(
2674 klass=self.__class__,
2675 decode_path=decode_path,
2679 raise NotEnoughData(
2680 "encoded length is longer than data",
2681 klass=self.__class__,
2682 decode_path=decode_path,
2685 v, tail = v[:l], v[l:]
2687 obj = self.__class__(
2689 bounds=(self._bound_min, self._bound_max),
2692 default=self.default,
2693 optional=self.optional,
2694 _decoded=(offset, llen, l),
2696 except DecodeError as err:
2699 klass=self.__class__,
2700 decode_path=decode_path,
2703 except BoundsError as err:
2706 klass=self.__class__,
2707 decode_path=decode_path,
2712 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2714 t, tlen, lv = tag_strip(tlv)
2715 except DecodeError as err:
2716 raise err.__class__(
2718 klass=self.__class__,
2719 decode_path=decode_path,
2725 return self._decode_chunk(lv, offset, decode_path, ctx)
2726 if t == self.tag_constructed:
2727 if not ctx.get("bered", False):
2729 "unallowed BER constructed encoding",
2730 klass=self.__class__,
2731 decode_path=decode_path,
2738 l, llen, v = len_decode(lv)
2739 except LenIndefForm:
2740 llen, l, v = 1, 0, lv[1:]
2742 except DecodeError as err:
2743 raise err.__class__(
2745 klass=self.__class__,
2746 decode_path=decode_path,
2750 raise NotEnoughData(
2751 "encoded length is longer than data",
2752 klass=self.__class__,
2753 decode_path=decode_path,
2757 sub_offset = offset + tlen + llen
2761 if v[:EOC_LEN].tobytes() == EOC:
2768 "chunk out of bounds",
2769 klass=self.__class__,
2770 decode_path=decode_path + (str(len(chunks) - 1),),
2771 offset=chunks[-1].offset,
2773 sub_decode_path = decode_path + (str(len(chunks)),)
2775 chunk, v_tail = OctetString().decode(
2778 decode_path=sub_decode_path,
2781 _ctx_immutable=False,
2785 "expected OctetString encoded chunk",
2786 klass=self.__class__,
2787 decode_path=sub_decode_path,
2790 chunks.append(chunk)
2791 sub_offset += chunk.tlvlen
2792 vlen += chunk.tlvlen
2795 obj = self.__class__(
2796 value=b"".join(bytes(chunk) for chunk in chunks),
2797 bounds=(self._bound_min, self._bound_max),
2800 default=self.default,
2801 optional=self.optional,
2802 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2804 except DecodeError as err:
2807 klass=self.__class__,
2808 decode_path=decode_path,
2811 except BoundsError as err:
2814 klass=self.__class__,
2815 decode_path=decode_path,
2818 obj.lenindef = lenindef
2819 obj.ber_encoded = True
2820 return obj, (v[EOC_LEN:] if lenindef else v)
2822 klass=self.__class__,
2823 decode_path=decode_path,
2828 return pp_console_row(next(self.pps()))
2830 def pps(self, decode_path=()):
2833 asn1_type_name=self.asn1_type_name,
2834 obj_name=self.__class__.__name__,
2835 decode_path=decode_path,
2836 value=("%d bytes" % len(self._value)) if self.ready else None,
2837 blob=self._value if self.ready else None,
2838 optional=self.optional,
2839 default=self == self.default,
2840 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2841 expl=None if self._expl is None else tag_decode(self._expl),
2846 expl_offset=self.expl_offset if self.expled else None,
2847 expl_tlen=self.expl_tlen if self.expled else None,
2848 expl_llen=self.expl_llen if self.expled else None,
2849 expl_vlen=self.expl_vlen if self.expled else None,
2850 expl_lenindef=self.expl_lenindef,
2851 lenindef=self.lenindef,
2852 ber_encoded=self.ber_encoded,
2855 defined_by, defined = self.defined or (None, None)
2856 if defined_by is not None:
2858 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2860 for pp in self.pps_lenindef(decode_path):
2865 """``NULL`` null object
2873 tag_default = tag_encode(5)
2874 asn1_type_name = "NULL"
2878 value=None, # unused, but Sequence passes it
2885 :param bytes impl: override default tag with ``IMPLICIT`` one
2886 :param bytes expl: override default tag with ``EXPLICIT`` one
2887 :param bool optional: is object ``OPTIONAL`` in sequence
2889 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2897 obj = self.__class__()
2899 obj._expl = self._expl
2900 obj.default = self.default
2901 obj.optional = self.optional
2902 obj.offset = self.offset
2903 obj.llen = self.llen
2904 obj.vlen = self.vlen
2907 def __eq__(self, their):
2908 if not issubclass(their.__class__, Null):
2911 self.tag == their.tag and
2912 self._expl == their._expl
2922 return self.__class__(
2923 impl=self.tag if impl is None else impl,
2924 expl=self._expl if expl is None else expl,
2925 optional=self.optional if optional is None else optional,
2929 return self.tag + len_encode(0)
2931 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2933 t, _, lv = tag_strip(tlv)
2934 except DecodeError as err:
2935 raise err.__class__(
2937 klass=self.__class__,
2938 decode_path=decode_path,
2943 klass=self.__class__,
2944 decode_path=decode_path,
2947 if tag_only: # pragma: no cover
2950 l, _, v = len_decode(lv)
2951 except DecodeError as err:
2952 raise err.__class__(
2954 klass=self.__class__,
2955 decode_path=decode_path,
2959 raise InvalidLength(
2960 "Null must have zero length",
2961 klass=self.__class__,
2962 decode_path=decode_path,
2965 obj = self.__class__(
2968 optional=self.optional,
2969 _decoded=(offset, 1, 0),
2974 return pp_console_row(next(self.pps()))
2976 def pps(self, decode_path=()):
2979 asn1_type_name=self.asn1_type_name,
2980 obj_name=self.__class__.__name__,
2981 decode_path=decode_path,
2982 optional=self.optional,
2983 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2984 expl=None if self._expl is None else tag_decode(self._expl),
2989 expl_offset=self.expl_offset if self.expled else None,
2990 expl_tlen=self.expl_tlen if self.expled else None,
2991 expl_llen=self.expl_llen if self.expled else None,
2992 expl_vlen=self.expl_vlen if self.expled else None,
2993 expl_lenindef=self.expl_lenindef,
2996 for pp in self.pps_lenindef(decode_path):
3000 class ObjectIdentifier(Obj):
3001 """``OBJECT IDENTIFIER`` OID type
3003 >>> oid = ObjectIdentifier((1, 2, 3))
3004 OBJECT IDENTIFIER 1.2.3
3005 >>> oid == ObjectIdentifier("1.2.3")
3011 >>> oid + (4, 5) + ObjectIdentifier("1.7")
3012 OBJECT IDENTIFIER 1.2.3.4.5.1.7
3014 >>> str(ObjectIdentifier((3, 1)))
3015 Traceback (most recent call last):
3016 pyderasn.InvalidOID: unacceptable first arc value
3018 __slots__ = ("defines",)
3019 tag_default = tag_encode(6)
3020 asn1_type_name = "OBJECT IDENTIFIER"
3033 :param value: set the value. Either tuples of integers,
3034 string of "."-concatenated integers, or
3035 :py:class:`pyderasn.ObjectIdentifier` object
3036 :param defines: sequence of tuples. Each tuple has two elements.
3037 First one is relative to current one decode
3038 path, aiming to the field defined by that OID.
3039 Read about relative path in
3040 :py:func:`pyderasn.abs_decode_path`. Second
3041 tuple element is ``{OID: pyderasn.Obj()}``
3042 dictionary, mapping between current OID value
3043 and structure applied to defined field.
3044 :ref:`Read about DEFINED BY <definedby>`
3045 :param bytes impl: override default tag with ``IMPLICIT`` one
3046 :param bytes expl: override default tag with ``EXPLICIT`` one
3047 :param default: set default value. Type same as in ``value``
3048 :param bool optional: is object ``OPTIONAL`` in sequence
3050 super(ObjectIdentifier, self).__init__(
3058 if value is not None:
3059 self._value = self._value_sanitize(value)
3060 if default is not None:
3061 default = self._value_sanitize(default)
3062 self.default = self.__class__(
3067 if self._value is None:
3068 self._value = default
3069 self.defines = defines
3071 def __add__(self, their):
3072 if isinstance(their, self.__class__):
3073 return self.__class__(self._value + their._value)
3074 if isinstance(their, tuple):
3075 return self.__class__(self._value + their)
3076 raise InvalidValueType((self.__class__, tuple))
3078 def _value_sanitize(self, value):
3079 if issubclass(value.__class__, ObjectIdentifier):
3081 if isinstance(value, string_types):
3083 value = tuple(int(arc) for arc in value.split("."))
3085 raise InvalidOID("unacceptable arcs values")
3086 if isinstance(value, tuple):
3088 raise InvalidOID("less than 2 arcs")
3089 first_arc = value[0]
3090 if first_arc in (0, 1):
3091 if not (0 <= value[1] <= 39):
3092 raise InvalidOID("second arc is too wide")
3093 elif first_arc == 2:
3096 raise InvalidOID("unacceptable first arc value")
3098 raise InvalidValueType((self.__class__, str, tuple))
3102 return self._value is not None
3105 obj = self.__class__()
3106 obj._value = self._value
3107 obj.defines = self.defines
3109 obj._expl = self._expl
3110 obj.default = self.default
3111 obj.optional = self.optional
3112 obj.offset = self.offset
3113 obj.llen = self.llen
3114 obj.vlen = self.vlen
3118 self._assert_ready()
3119 return iter(self._value)
3122 return ".".join(str(arc) for arc in self._value or ())
3125 self._assert_ready()
3128 bytes(self._expl or b"") +
3129 str(self._value).encode("ascii"),
3132 def __eq__(self, their):
3133 if isinstance(their, tuple):
3134 return self._value == their
3135 if not issubclass(their.__class__, ObjectIdentifier):
3138 self.tag == their.tag and
3139 self._expl == their._expl and
3140 self._value == their._value
3143 def __lt__(self, their):
3144 return self._value < their._value
3155 return self.__class__(
3157 defines=self.defines if defines is None else defines,
3158 impl=self.tag if impl is None else impl,
3159 expl=self._expl if expl is None else expl,
3160 default=self.default if default is None else default,
3161 optional=self.optional if optional is None else optional,
3165 self._assert_ready()
3167 first_value = value[1]
3168 first_arc = value[0]
3171 elif first_arc == 1:
3173 elif first_arc == 2:
3175 else: # pragma: no cover
3176 raise RuntimeError("invalid arc is stored")
3177 octets = [zero_ended_encode(first_value)]
3178 for arc in value[2:]:
3179 octets.append(zero_ended_encode(arc))
3180 v = b"".join(octets)
3181 return b"".join((self.tag, len_encode(len(v)), v))
3183 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3185 t, _, lv = tag_strip(tlv)
3186 except DecodeError as err:
3187 raise err.__class__(
3189 klass=self.__class__,
3190 decode_path=decode_path,
3195 klass=self.__class__,
3196 decode_path=decode_path,
3199 if tag_only: # pragma: no cover
3202 l, llen, v = len_decode(lv)
3203 except DecodeError as err:
3204 raise err.__class__(
3206 klass=self.__class__,
3207 decode_path=decode_path,
3211 raise NotEnoughData(
3212 "encoded length is longer than data",
3213 klass=self.__class__,
3214 decode_path=decode_path,
3218 raise NotEnoughData(
3220 klass=self.__class__,
3221 decode_path=decode_path,
3224 v, tail = v[:l], v[l:]
3230 octet = indexbytes(v, i)
3231 arc = (arc << 7) | (octet & 0x7F)
3232 if octet & 0x80 == 0:
3240 klass=self.__class__,
3241 decode_path=decode_path,
3245 second_arc = arcs[0]
3246 if 0 <= second_arc <= 39:
3248 elif 40 <= second_arc <= 79:
3254 obj = self.__class__(
3255 value=tuple([first_arc, second_arc] + arcs[1:]),
3258 default=self.default,
3259 optional=self.optional,
3260 _decoded=(offset, llen, l),
3265 return pp_console_row(next(self.pps()))
3267 def pps(self, decode_path=()):
3270 asn1_type_name=self.asn1_type_name,
3271 obj_name=self.__class__.__name__,
3272 decode_path=decode_path,
3273 value=str(self) if self.ready else None,
3274 optional=self.optional,
3275 default=self == self.default,
3276 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3277 expl=None if self._expl is None else tag_decode(self._expl),
3282 expl_offset=self.expl_offset if self.expled else None,
3283 expl_tlen=self.expl_tlen if self.expled else None,
3284 expl_llen=self.expl_llen if self.expled else None,
3285 expl_vlen=self.expl_vlen if self.expled else None,
3286 expl_lenindef=self.expl_lenindef,
3289 for pp in self.pps_lenindef(decode_path):
3293 class Enumerated(Integer):
3294 """``ENUMERATED`` integer type
3296 This type is identical to :py:class:`pyderasn.Integer`, but requires
3297 schema to be specified and does not accept values missing from it.
3300 tag_default = tag_encode(10)
3301 asn1_type_name = "ENUMERATED"
3312 bounds=None, # dummy argument, workability for Integer.decode
3314 super(Enumerated, self).__init__(
3323 if len(self.specs) == 0:
3324 raise ValueError("schema must be specified")
3326 def _value_sanitize(self, value):
3327 if isinstance(value, self.__class__):
3328 value = value._value
3329 elif isinstance(value, integer_types):
3330 if value not in list(self.specs.values()):
3332 "unknown integer value: %s" % value,
3333 klass=self.__class__,
3335 elif isinstance(value, string_types):
3336 value = self.specs.get(value)
3338 raise ObjUnknown("integer value: %s" % value)
3340 raise InvalidValueType((self.__class__, int, str))
3344 obj = self.__class__(_specs=self.specs)
3345 obj._value = self._value
3346 obj._bound_min = self._bound_min
3347 obj._bound_max = self._bound_max
3349 obj._expl = self._expl
3350 obj.default = self.default
3351 obj.optional = self.optional
3352 obj.offset = self.offset
3353 obj.llen = self.llen
3354 obj.vlen = self.vlen
3366 return self.__class__(
3368 impl=self.tag if impl is None else impl,
3369 expl=self._expl if expl is None else expl,
3370 default=self.default if default is None else default,
3371 optional=self.optional if optional is None else optional,
3376 class CommonString(OctetString):
3377 """Common class for all strings
3379 Everything resembles :py:class:`pyderasn.OctetString`, except
3380 ability to deal with unicode text strings.
3382 >>> hexenc("привет мир".encode("utf-8"))
3383 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3384 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3386 >>> s = UTF8String("привет мир")
3387 UTF8String UTF8String привет мир
3389 'привет мир'
3390 >>> hexenc(bytes(s))
3391 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3393 >>> PrintableString("привет мир")
3394 Traceback (most recent call last):
3395 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3397 >>> BMPString("ада", bounds=(2, 2))
3398 Traceback (most recent call last):
3399 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3400 >>> s = BMPString("ад", bounds=(2, 2))
3403 >>> hexenc(bytes(s))
3411 * - :py:class:`pyderasn.UTF8String`
3413 * - :py:class:`pyderasn.NumericString`
3415 * - :py:class:`pyderasn.PrintableString`
3417 * - :py:class:`pyderasn.TeletexString`
3419 * - :py:class:`pyderasn.T61String`
3421 * - :py:class:`pyderasn.VideotexString`
3423 * - :py:class:`pyderasn.IA5String`
3425 * - :py:class:`pyderasn.GraphicString`
3427 * - :py:class:`pyderasn.VisibleString`
3429 * - :py:class:`pyderasn.ISO646String`
3431 * - :py:class:`pyderasn.GeneralString`
3433 * - :py:class:`pyderasn.UniversalString`
3435 * - :py:class:`pyderasn.BMPString`
3438 __slots__ = ("encoding",)
3440 def _value_sanitize(self, value):
3442 value_decoded = None
3443 if isinstance(value, self.__class__):
3444 value_raw = value._value
3445 elif isinstance(value, text_type):
3446 value_decoded = value
3447 elif isinstance(value, binary_type):
3450 raise InvalidValueType((self.__class__, text_type, binary_type))
3453 value_decoded.encode(self.encoding)
3454 if value_raw is None else value_raw
3457 value_raw.decode(self.encoding)
3458 if value_decoded is None else value_decoded
3460 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3461 raise DecodeError(str(err))
3462 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3470 def __eq__(self, their):
3471 if isinstance(their, binary_type):
3472 return self._value == their
3473 if isinstance(their, text_type):
3474 return self._value == their.encode(self.encoding)
3475 if not isinstance(their, self.__class__):
3478 self._value == their._value and
3479 self.tag == their.tag and
3480 self._expl == their._expl
3483 def __unicode__(self):
3485 return self._value.decode(self.encoding)
3486 return text_type(self._value)
3489 return pp_console_row(next(self.pps(no_unicode=PY2)))
3491 def pps(self, decode_path=(), no_unicode=False):
3494 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3497 asn1_type_name=self.asn1_type_name,
3498 obj_name=self.__class__.__name__,
3499 decode_path=decode_path,
3501 optional=self.optional,
3502 default=self == self.default,
3503 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3504 expl=None if self._expl is None else tag_decode(self._expl),
3509 expl_offset=self.expl_offset if self.expled else None,
3510 expl_tlen=self.expl_tlen if self.expled else None,
3511 expl_llen=self.expl_llen if self.expled else None,
3512 expl_vlen=self.expl_vlen if self.expled else None,
3513 expl_lenindef=self.expl_lenindef,
3514 ber_encoded=self.ber_encoded,
3517 for pp in self.pps_lenindef(decode_path):
3521 class UTF8String(CommonString):
3523 tag_default = tag_encode(12)
3525 asn1_type_name = "UTF8String"
3528 class AllowableCharsMixin(object):
3530 def allowable_chars(self):
3532 return self._allowable_chars
3533 return set(six_unichr(c) for c in self._allowable_chars)
3536 class NumericString(AllowableCharsMixin, CommonString):
3539 Its value is properly sanitized: only ASCII digits with spaces can
3542 >>> NumericString().allowable_chars
3543 set(['3', '4', '7', '5', '1', '0', '8', '9', ' ', '6', '2'])
3546 tag_default = tag_encode(18)
3548 asn1_type_name = "NumericString"
3549 _allowable_chars = set(digits.encode("ascii") + b" ")
3551 def _value_sanitize(self, value):
3552 value = super(NumericString, self)._value_sanitize(value)
3553 if not set(value) <= self._allowable_chars:
3554 raise DecodeError("non-numeric value")
3558 class PrintableString(AllowableCharsMixin, CommonString):
3561 Its value is properly sanitized: see X.680 41.4 table 10.
3563 >>> PrintableString().allowable_chars
3564 >>> set([' ', "'", ..., 'z'])
3567 tag_default = tag_encode(19)
3569 asn1_type_name = "PrintableString"
3570 _allowable_chars = set(
3571 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3574 def _value_sanitize(self, value):
3575 value = super(PrintableString, self)._value_sanitize(value)
3576 if not set(value) <= self._allowable_chars:
3577 raise DecodeError("non-printable value")
3581 class TeletexString(CommonString):
3583 tag_default = tag_encode(20)
3585 asn1_type_name = "TeletexString"
3588 class T61String(TeletexString):
3590 asn1_type_name = "T61String"
3593 class VideotexString(CommonString):
3595 tag_default = tag_encode(21)
3596 encoding = "iso-8859-1"
3597 asn1_type_name = "VideotexString"
3600 class IA5String(CommonString):
3602 tag_default = tag_encode(22)
3604 asn1_type_name = "IA5"
3607 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3608 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3609 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3612 class UTCTime(CommonString):
3613 """``UTCTime`` datetime type
3615 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3616 UTCTime UTCTime 2017-09-30T22:07:50
3622 datetime.datetime(2017, 9, 30, 22, 7, 50)
3623 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3624 datetime.datetime(1957, 9, 30, 22, 7, 50)
3627 tag_default = tag_encode(23)
3629 asn1_type_name = "UTCTime"
3631 fmt = "%y%m%d%H%M%SZ"
3641 bounds=None, # dummy argument, workability for OctetString.decode
3644 :param value: set the value. Either datetime type, or
3645 :py:class:`pyderasn.UTCTime` object
3646 :param bytes impl: override default tag with ``IMPLICIT`` one
3647 :param bytes expl: override default tag with ``EXPLICIT`` one
3648 :param default: set default value. Type same as in ``value``
3649 :param bool optional: is object ``OPTIONAL`` in sequence
3651 super(UTCTime, self).__init__(
3659 if value is not None:
3660 self._value = self._value_sanitize(value)
3661 if default is not None:
3662 default = self._value_sanitize(default)
3663 self.default = self.__class__(
3668 if self._value is None:
3669 self._value = default
3671 def _value_sanitize(self, value):
3672 if isinstance(value, self.__class__):
3674 if isinstance(value, datetime):
3675 return value.strftime(self.fmt).encode("ascii")
3676 if isinstance(value, binary_type):
3678 value_decoded = value.decode("ascii")
3679 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3680 raise DecodeError("invalid UTCTime encoding")
3681 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3683 datetime.strptime(value_decoded, self.fmt)
3684 except (TypeError, ValueError):
3685 raise DecodeError("invalid UTCTime format")
3688 raise DecodeError("invalid UTCTime length")
3689 raise InvalidValueType((self.__class__, datetime))
3691 def __eq__(self, their):
3692 if isinstance(their, binary_type):
3693 return self._value == their
3694 if isinstance(their, datetime):
3695 return self.todatetime() == their
3696 if not isinstance(their, self.__class__):
3699 self._value == their._value and
3700 self.tag == their.tag and
3701 self._expl == their._expl
3704 def todatetime(self):
3705 """Convert to datetime
3709 Pay attention that UTCTime can not hold full year, so all years
3710 having < 50 years are treated as 20xx, 19xx otherwise, according
3711 to X.509 recomendation.
3713 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3714 year = value.year % 100
3716 year=(2000 + year) if year < 50 else (1900 + year),
3720 minute=value.minute,
3721 second=value.second,
3725 return pp_console_row(next(self.pps()))
3727 def pps(self, decode_path=()):
3730 asn1_type_name=self.asn1_type_name,
3731 obj_name=self.__class__.__name__,
3732 decode_path=decode_path,
3733 value=self.todatetime().isoformat() if self.ready else None,
3734 optional=self.optional,
3735 default=self == self.default,
3736 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3737 expl=None if self._expl is None else tag_decode(self._expl),
3742 expl_offset=self.expl_offset if self.expled else None,
3743 expl_tlen=self.expl_tlen if self.expled else None,
3744 expl_llen=self.expl_llen if self.expled else None,
3745 expl_vlen=self.expl_vlen if self.expled else None,
3746 expl_lenindef=self.expl_lenindef,
3747 ber_encoded=self.ber_encoded,
3750 for pp in self.pps_lenindef(decode_path):
3754 class GeneralizedTime(UTCTime):
3755 """``GeneralizedTime`` datetime type
3757 This type is similar to :py:class:`pyderasn.UTCTime`.
3759 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3760 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3762 '20170930220750.000123Z'
3763 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3764 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3767 tag_default = tag_encode(24)
3768 asn1_type_name = "GeneralizedTime"
3770 fmt = "%Y%m%d%H%M%SZ"
3771 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3773 def _value_sanitize(self, value):
3774 if isinstance(value, self.__class__):
3776 if isinstance(value, datetime):
3777 return value.strftime(
3778 self.fmt_ms if value.microsecond > 0 else self.fmt
3780 if isinstance(value, binary_type):
3782 value_decoded = value.decode("ascii")
3783 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3784 raise DecodeError("invalid GeneralizedTime encoding")
3785 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3787 datetime.strptime(value_decoded, self.fmt)
3788 except (TypeError, ValueError):
3790 "invalid GeneralizedTime (without ms) format",
3793 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3795 datetime.strptime(value_decoded, self.fmt_ms)
3796 except (TypeError, ValueError):
3798 "invalid GeneralizedTime (with ms) format",
3803 "invalid GeneralizedTime length",
3804 klass=self.__class__,
3806 raise InvalidValueType((self.__class__, datetime))
3808 def todatetime(self):
3809 value = self._value.decode("ascii")
3810 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3811 return datetime.strptime(value, self.fmt)
3812 return datetime.strptime(value, self.fmt_ms)
3815 class GraphicString(CommonString):
3817 tag_default = tag_encode(25)
3818 encoding = "iso-8859-1"
3819 asn1_type_name = "GraphicString"
3822 class VisibleString(CommonString):
3824 tag_default = tag_encode(26)
3826 asn1_type_name = "VisibleString"
3829 class ISO646String(VisibleString):
3831 asn1_type_name = "ISO646String"
3834 class GeneralString(CommonString):
3836 tag_default = tag_encode(27)
3837 encoding = "iso-8859-1"
3838 asn1_type_name = "GeneralString"
3841 class UniversalString(CommonString):
3843 tag_default = tag_encode(28)
3844 encoding = "utf-32-be"
3845 asn1_type_name = "UniversalString"
3848 class BMPString(CommonString):
3850 tag_default = tag_encode(30)
3851 encoding = "utf-16-be"
3852 asn1_type_name = "BMPString"
3856 """``CHOICE`` special type
3860 class GeneralName(Choice):
3862 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3863 ("dNSName", IA5String(impl=tag_ctxp(2))),
3866 >>> gn = GeneralName()
3868 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3869 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3870 >>> gn["dNSName"] = IA5String("bar.baz")
3871 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3872 >>> gn["rfc822Name"]
3875 [2] IA5String IA5 bar.baz
3878 >>> gn.value == gn["dNSName"]
3881 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3883 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3884 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3886 __slots__ = ("specs",)
3888 asn1_type_name = "CHOICE"
3901 :param value: set the value. Either ``(choice, value)`` tuple, or
3902 :py:class:`pyderasn.Choice` object
3903 :param bytes impl: can not be set, do **not** use it
3904 :param bytes expl: override default tag with ``EXPLICIT`` one
3905 :param default: set default value. Type same as in ``value``
3906 :param bool optional: is object ``OPTIONAL`` in sequence
3908 if impl is not None:
3909 raise ValueError("no implicit tag allowed for CHOICE")
3910 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3912 schema = getattr(self, "schema", ())
3913 if len(schema) == 0:
3914 raise ValueError("schema must be specified")
3916 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3919 if value is not None:
3920 self._value = self._value_sanitize(value)
3921 if default is not None:
3922 default_value = self._value_sanitize(default)
3923 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3924 default_obj.specs = self.specs
3925 default_obj._value = default_value
3926 self.default = default_obj
3928 self._value = default_obj.copy()._value
3930 def _value_sanitize(self, value):
3931 if isinstance(value, self.__class__):
3933 if isinstance(value, tuple) and len(value) == 2:
3935 spec = self.specs.get(choice)
3937 raise ObjUnknown(choice)
3938 if not isinstance(obj, spec.__class__):
3939 raise InvalidValueType((spec,))
3940 return (choice, spec(obj))
3941 raise InvalidValueType((self.__class__, tuple))
3945 return self._value is not None and self._value[1].ready
3949 return self.expl_lenindef or (
3950 (self._value is not None) and
3951 self._value[1].bered
3955 obj = self.__class__(schema=self.specs)
3956 obj._expl = self._expl
3957 obj.default = self.default
3958 obj.optional = self.optional
3959 obj.offset = self.offset
3960 obj.llen = self.llen
3961 obj.vlen = self.vlen
3963 if value is not None:
3964 obj._value = (value[0], value[1].copy())
3967 def __eq__(self, their):
3968 if isinstance(their, tuple) and len(their) == 2:
3969 return self._value == their
3970 if not isinstance(their, self.__class__):
3973 self.specs == their.specs and
3974 self._value == their._value
3984 return self.__class__(
3987 expl=self._expl if expl is None else expl,
3988 default=self.default if default is None else default,
3989 optional=self.optional if optional is None else optional,
3994 self._assert_ready()
3995 return self._value[0]
3999 self._assert_ready()
4000 return self._value[1]
4002 def __getitem__(self, key):
4003 if key not in self.specs:
4004 raise ObjUnknown(key)
4005 if self._value is None:
4007 choice, value = self._value
4012 def __setitem__(self, key, value):
4013 spec = self.specs.get(key)
4015 raise ObjUnknown(key)
4016 if not isinstance(value, spec.__class__):
4017 raise InvalidValueType((spec.__class__,))
4018 self._value = (key, spec(value))
4026 return self._value[1].decoded if self.ready else False
4029 self._assert_ready()
4030 return self._value[1].encode()
4032 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4033 for choice, spec in self.specs.items():
4034 sub_decode_path = decode_path + (choice,)
4040 decode_path=sub_decode_path,
4043 _ctx_immutable=False,
4050 klass=self.__class__,
4051 decode_path=decode_path,
4054 if tag_only: # pragma: no cover
4056 value, tail = spec.decode(
4060 decode_path=sub_decode_path,
4062 _ctx_immutable=False,
4064 obj = self.__class__(
4067 default=self.default,
4068 optional=self.optional,
4069 _decoded=(offset, 0, value.fulllen),
4071 obj._value = (choice, value)
4075 value = pp_console_row(next(self.pps()))
4077 value = "%s[%r]" % (value, self.value)
4080 def pps(self, decode_path=()):
4083 asn1_type_name=self.asn1_type_name,
4084 obj_name=self.__class__.__name__,
4085 decode_path=decode_path,
4086 value=self.choice if self.ready else None,
4087 optional=self.optional,
4088 default=self == self.default,
4089 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4090 expl=None if self._expl is None else tag_decode(self._expl),
4095 expl_lenindef=self.expl_lenindef,
4099 yield self.value.pps(decode_path=decode_path + (self.choice,))
4100 for pp in self.pps_lenindef(decode_path):
4104 class PrimitiveTypes(Choice):
4105 """Predefined ``CHOICE`` for all generic primitive types
4107 It could be useful for general decoding of some unspecified values:
4109 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4110 OCTET STRING 3 bytes 666f6f
4111 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4115 schema = tuple((klass.__name__, klass()) for klass in (
4140 """``ANY`` special type
4142 >>> Any(Integer(-123))
4144 >>> a = Any(OctetString(b"hello world").encode())
4145 ANY 040b68656c6c6f20776f726c64
4146 >>> hexenc(bytes(a))
4147 b'0x040x0bhello world'
4149 __slots__ = ("defined",)
4150 tag_default = tag_encode(0)
4151 asn1_type_name = "ANY"
4161 :param value: set the value. Either any kind of pyderasn's
4162 **ready** object, or bytes. Pay attention that
4163 **no** validation is performed is raw binary value
4165 :param bytes expl: override default tag with ``EXPLICIT`` one
4166 :param bool optional: is object ``OPTIONAL`` in sequence
4168 super(Any, self).__init__(None, expl, None, optional, _decoded)
4169 self._value = None if value is None else self._value_sanitize(value)
4172 def _value_sanitize(self, value):
4173 if isinstance(value, self.__class__):
4175 if isinstance(value, Obj):
4176 return value.encode()
4177 if isinstance(value, binary_type):
4179 raise InvalidValueType((self.__class__, Obj, binary_type))
4183 return self._value is not None
4187 if self.expl_lenindef or self.lenindef:
4189 if self.defined is None:
4191 return self.defined[1].bered
4194 obj = self.__class__()
4195 obj._value = self._value
4197 obj._expl = self._expl
4198 obj.optional = self.optional
4199 obj.offset = self.offset
4200 obj.llen = self.llen
4201 obj.vlen = self.vlen
4204 def __eq__(self, their):
4205 if isinstance(their, binary_type):
4206 return self._value == their
4207 if issubclass(their.__class__, Any):
4208 return self._value == their._value
4217 return self.__class__(
4219 expl=self._expl if expl is None else expl,
4220 optional=self.optional if optional is None else optional,
4223 def __bytes__(self):
4224 self._assert_ready()
4232 self._assert_ready()
4235 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4237 t, tlen, lv = tag_strip(tlv)
4238 except DecodeError as err:
4239 raise err.__class__(
4241 klass=self.__class__,
4242 decode_path=decode_path,
4246 l, llen, v = len_decode(lv)
4247 except LenIndefForm as err:
4248 if not ctx.get("bered", False):
4249 raise err.__class__(
4251 klass=self.__class__,
4252 decode_path=decode_path,
4255 llen, vlen, v = 1, 0, lv[1:]
4256 sub_offset = offset + tlen + llen
4258 while v[:EOC_LEN].tobytes() != EOC:
4259 chunk, v = Any().decode(
4262 decode_path=decode_path + (str(chunk_i),),
4265 _ctx_immutable=False,
4267 vlen += chunk.tlvlen
4268 sub_offset += chunk.tlvlen
4270 tlvlen = tlen + llen + vlen + EOC_LEN
4271 obj = self.__class__(
4272 value=tlv[:tlvlen].tobytes(),
4274 optional=self.optional,
4275 _decoded=(offset, 0, tlvlen),
4279 return obj, v[EOC_LEN:]
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 tlvlen = tlen + llen + l
4295 v, tail = tlv[:tlvlen], v[l:]
4296 obj = self.__class__(
4299 optional=self.optional,
4300 _decoded=(offset, 0, tlvlen),
4306 return pp_console_row(next(self.pps()))
4308 def pps(self, decode_path=()):
4311 asn1_type_name=self.asn1_type_name,
4312 obj_name=self.__class__.__name__,
4313 decode_path=decode_path,
4314 blob=self._value if self.ready else None,
4315 optional=self.optional,
4316 default=self == self.default,
4317 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4318 expl=None if self._expl is None else tag_decode(self._expl),
4323 expl_offset=self.expl_offset if self.expled else None,
4324 expl_tlen=self.expl_tlen if self.expled else None,
4325 expl_llen=self.expl_llen if self.expled else None,
4326 expl_vlen=self.expl_vlen if self.expled else None,
4327 expl_lenindef=self.expl_lenindef,
4328 lenindef=self.lenindef,
4331 defined_by, defined = self.defined or (None, None)
4332 if defined_by is not None:
4334 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4336 for pp in self.pps_lenindef(decode_path):
4340 ########################################################################
4341 # ASN.1 constructed types
4342 ########################################################################
4344 def get_def_by_path(defines_by_path, sub_decode_path):
4345 """Get define by decode path
4347 for path, define in defines_by_path:
4348 if len(path) != len(sub_decode_path):
4350 for p1, p2 in zip(path, sub_decode_path):
4351 if (p1 != any) and (p1 != p2):
4357 def abs_decode_path(decode_path, rel_path):
4358 """Create an absolute decode path from current and relative ones
4360 :param decode_path: current decode path, starting point. Tuple of strings
4361 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4362 If first tuple's element is "/", then treat it as
4363 an absolute path, ignoring ``decode_path`` as
4364 starting point. Also this tuple can contain ".."
4365 elements, stripping the leading element from
4368 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4369 ("foo", "bar", "baz", "whatever")
4370 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4372 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4375 if rel_path[0] == "/":
4377 if rel_path[0] == "..":
4378 return abs_decode_path(decode_path[:-1], rel_path[1:])
4379 return decode_path + rel_path
4382 class Sequence(Obj):
4383 """``SEQUENCE`` structure type
4385 You have to make specification of sequence::
4387 class Extension(Sequence):
4389 ("extnID", ObjectIdentifier()),
4390 ("critical", Boolean(default=False)),
4391 ("extnValue", OctetString()),
4394 Then, you can work with it as with dictionary.
4396 >>> ext = Extension()
4397 >>> Extension().specs
4399 ('extnID', OBJECT IDENTIFIER),
4400 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4401 ('extnValue', OCTET STRING),
4403 >>> ext["extnID"] = "1.2.3"
4404 Traceback (most recent call last):
4405 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4406 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4408 You can determine if sequence is ready to be encoded:
4413 Traceback (most recent call last):
4414 pyderasn.ObjNotReady: object is not ready: extnValue
4415 >>> ext["extnValue"] = OctetString(b"foobar")
4419 Value you want to assign, must have the same **type** as in
4420 corresponding specification, but it can have different tags,
4421 optional/default attributes -- they will be taken from specification
4424 class TBSCertificate(Sequence):
4426 ("version", Version(expl=tag_ctxc(0), default="v1")),
4429 >>> tbs = TBSCertificate()
4430 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4432 Assign ``None`` to remove value from sequence.
4434 You can set values in Sequence during its initialization:
4436 >>> AlgorithmIdentifier((
4437 ("algorithm", ObjectIdentifier("1.2.3")),
4438 ("parameters", Any(Null()))
4440 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4442 You can determine if value exists/set in the sequence and take its value:
4444 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4447 OBJECT IDENTIFIER 1.2.3
4449 But pay attention that if value has default, then it won't be (not
4450 in) in the sequence (because ``DEFAULT`` must not be encoded in
4451 DER), but you can read its value:
4453 >>> "critical" in ext, ext["critical"]
4454 (False, BOOLEAN False)
4455 >>> ext["critical"] = Boolean(True)
4456 >>> "critical" in ext, ext["critical"]
4457 (True, BOOLEAN True)
4459 All defaulted values are always optional.
4461 .. _allow_default_values_ctx:
4463 DER prohibits default value encoding and will raise an error if
4464 default value is unexpectedly met during decode.
4465 If :ref:`bered <bered_ctx>` context option is set, then no error
4466 will be raised, but ``bered`` attribute set. You can disable strict
4467 defaulted values existence validation by setting
4468 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4470 Two sequences are equal if they have equal specification (schema),
4471 implicit/explicit tagging and the same values.
4473 __slots__ = ("specs",)
4474 tag_default = tag_encode(form=TagFormConstructed, num=16)
4475 asn1_type_name = "SEQUENCE"
4487 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4489 schema = getattr(self, "schema", ())
4491 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4494 if value is not None:
4495 if issubclass(value.__class__, Sequence):
4496 self._value = value._value
4497 elif hasattr(value, "__iter__"):
4498 for seq_key, seq_value in value:
4499 self[seq_key] = seq_value
4501 raise InvalidValueType((Sequence,))
4502 if default is not None:
4503 if not issubclass(default.__class__, Sequence):
4504 raise InvalidValueType((Sequence,))
4505 default_value = default._value
4506 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4507 default_obj.specs = self.specs
4508 default_obj._value = default_value
4509 self.default = default_obj
4511 self._value = default_obj.copy()._value
4515 for name, spec in self.specs.items():
4516 value = self._value.get(name)
4528 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4530 return any(value.bered for value in self._value.values())
4533 obj = self.__class__(schema=self.specs)
4535 obj._expl = self._expl
4536 obj.default = self.default
4537 obj.optional = self.optional
4538 obj.offset = self.offset
4539 obj.llen = self.llen
4540 obj.vlen = self.vlen
4541 obj._value = {k: v.copy() for k, v in self._value.items()}
4544 def __eq__(self, their):
4545 if not isinstance(their, self.__class__):
4548 self.specs == their.specs and
4549 self.tag == their.tag and
4550 self._expl == their._expl and
4551 self._value == their._value
4562 return self.__class__(
4565 impl=self.tag if impl is None else impl,
4566 expl=self._expl if expl is None else expl,
4567 default=self.default if default is None else default,
4568 optional=self.optional if optional is None else optional,
4571 def __contains__(self, key):
4572 return key in self._value
4574 def __setitem__(self, key, value):
4575 spec = self.specs.get(key)
4577 raise ObjUnknown(key)
4579 self._value.pop(key, None)
4581 if not isinstance(value, spec.__class__):
4582 raise InvalidValueType((spec.__class__,))
4583 value = spec(value=value)
4584 if spec.default is not None and value == spec.default:
4585 self._value.pop(key, None)
4587 self._value[key] = value
4589 def __getitem__(self, key):
4590 value = self._value.get(key)
4591 if value is not None:
4593 spec = self.specs.get(key)
4595 raise ObjUnknown(key)
4596 if spec.default is not None:
4600 def _encoded_values(self):
4602 for name, spec in self.specs.items():
4603 value = self._value.get(name)
4607 raise ObjNotReady(name)
4608 raws.append(value.encode())
4612 v = b"".join(self._encoded_values())
4613 return b"".join((self.tag, len_encode(len(v)), v))
4615 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4617 t, tlen, lv = tag_strip(tlv)
4618 except DecodeError as err:
4619 raise err.__class__(
4621 klass=self.__class__,
4622 decode_path=decode_path,
4627 klass=self.__class__,
4628 decode_path=decode_path,
4631 if tag_only: # pragma: no cover
4634 ctx_bered = ctx.get("bered", False)
4636 l, llen, v = len_decode(lv)
4637 except LenIndefForm as err:
4639 raise err.__class__(
4641 klass=self.__class__,
4642 decode_path=decode_path,
4645 l, llen, v = 0, 1, lv[1:]
4647 except DecodeError as err:
4648 raise err.__class__(
4650 klass=self.__class__,
4651 decode_path=decode_path,
4655 raise NotEnoughData(
4656 "encoded length is longer than data",
4657 klass=self.__class__,
4658 decode_path=decode_path,
4662 v, tail = v[:l], v[l:]
4664 sub_offset = offset + tlen + llen
4667 ctx_allow_default_values = ctx.get("allow_default_values", False)
4668 for name, spec in self.specs.items():
4669 if spec.optional and (
4670 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4674 sub_decode_path = decode_path + (name,)
4676 value, v_tail = spec.decode(
4680 decode_path=sub_decode_path,
4682 _ctx_immutable=False,
4689 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4690 if defined is not None:
4691 defined_by, defined_spec = defined
4692 if issubclass(value.__class__, SequenceOf):
4693 for i, _value in enumerate(value):
4694 sub_sub_decode_path = sub_decode_path + (
4696 DecodePathDefBy(defined_by),
4698 defined_value, defined_tail = defined_spec.decode(
4699 memoryview(bytes(_value)),
4701 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4702 if value.expled else (value.tlen + value.llen)
4705 decode_path=sub_sub_decode_path,
4707 _ctx_immutable=False,
4709 if len(defined_tail) > 0:
4712 klass=self.__class__,
4713 decode_path=sub_sub_decode_path,
4716 _value.defined = (defined_by, defined_value)
4718 defined_value, defined_tail = defined_spec.decode(
4719 memoryview(bytes(value)),
4721 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4722 if value.expled else (value.tlen + value.llen)
4725 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4727 _ctx_immutable=False,
4729 if len(defined_tail) > 0:
4732 klass=self.__class__,
4733 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4736 value.defined = (defined_by, defined_value)
4738 value_len = value.fulllen
4740 sub_offset += value_len
4742 if spec.default is not None and value == spec.default:
4743 if ctx_bered or ctx_allow_default_values:
4747 "DEFAULT value met",
4748 klass=self.__class__,
4749 decode_path=sub_decode_path,
4752 values[name] = value
4754 spec_defines = getattr(spec, "defines", ())
4755 if len(spec_defines) == 0:
4756 defines_by_path = ctx.get("defines_by_path", ())
4757 if len(defines_by_path) > 0:
4758 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4759 if spec_defines is not None and len(spec_defines) > 0:
4760 for rel_path, schema in spec_defines:
4761 defined = schema.get(value, None)
4762 if defined is not None:
4763 ctx.setdefault("_defines", []).append((
4764 abs_decode_path(sub_decode_path[:-1], rel_path),
4768 if v[:EOC_LEN].tobytes() != EOC:
4771 klass=self.__class__,
4772 decode_path=decode_path,
4780 klass=self.__class__,
4781 decode_path=decode_path,
4784 obj = self.__class__(
4788 default=self.default,
4789 optional=self.optional,
4790 _decoded=(offset, llen, vlen),
4793 obj.lenindef = lenindef
4794 obj.ber_encoded = ber_encoded
4798 value = pp_console_row(next(self.pps()))
4800 for name in self.specs:
4801 _value = self._value.get(name)
4804 cols.append("%s: %s" % (name, repr(_value)))
4805 return "%s[%s]" % (value, "; ".join(cols))
4807 def pps(self, decode_path=()):
4810 asn1_type_name=self.asn1_type_name,
4811 obj_name=self.__class__.__name__,
4812 decode_path=decode_path,
4813 optional=self.optional,
4814 default=self == self.default,
4815 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4816 expl=None if self._expl is None else tag_decode(self._expl),
4821 expl_offset=self.expl_offset if self.expled else None,
4822 expl_tlen=self.expl_tlen if self.expled else None,
4823 expl_llen=self.expl_llen if self.expled else None,
4824 expl_vlen=self.expl_vlen if self.expled else None,
4825 expl_lenindef=self.expl_lenindef,
4826 lenindef=self.lenindef,
4827 ber_encoded=self.ber_encoded,
4830 for name in self.specs:
4831 value = self._value.get(name)
4834 yield value.pps(decode_path=decode_path + (name,))
4835 for pp in self.pps_lenindef(decode_path):
4839 class Set(Sequence):
4840 """``SET`` structure type
4842 Its usage is identical to :py:class:`pyderasn.Sequence`.
4844 .. _allow_unordered_set_ctx:
4846 DER prohibits unordered values encoding and will raise an error
4847 during decode. If If :ref:`bered <bered_ctx>` context option is set,
4848 then no error will occure. Also you can disable strict values
4849 ordering check by setting ``"allow_unordered_set": True``
4850 :ref:`context <ctx>` option.
4853 tag_default = tag_encode(form=TagFormConstructed, num=17)
4854 asn1_type_name = "SET"
4857 raws = self._encoded_values()
4860 return b"".join((self.tag, len_encode(len(v)), v))
4862 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4864 t, tlen, lv = tag_strip(tlv)
4865 except DecodeError as err:
4866 raise err.__class__(
4868 klass=self.__class__,
4869 decode_path=decode_path,
4874 klass=self.__class__,
4875 decode_path=decode_path,
4881 ctx_bered = ctx.get("bered", False)
4883 l, llen, v = len_decode(lv)
4884 except LenIndefForm as err:
4886 raise err.__class__(
4888 klass=self.__class__,
4889 decode_path=decode_path,
4892 l, llen, v = 0, 1, lv[1:]
4894 except DecodeError as err:
4895 raise err.__class__(
4897 klass=self.__class__,
4898 decode_path=decode_path,
4902 raise NotEnoughData(
4903 "encoded length is longer than data",
4904 klass=self.__class__,
4908 v, tail = v[:l], v[l:]
4910 sub_offset = offset + tlen + llen
4913 ctx_allow_default_values = ctx.get("allow_default_values", False)
4914 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4915 value_prev = memoryview(v[:0])
4916 specs_items = self.specs.items
4918 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4920 for name, spec in specs_items():
4921 sub_decode_path = decode_path + (name,)
4927 decode_path=sub_decode_path,
4930 _ctx_immutable=False,
4937 klass=self.__class__,
4938 decode_path=decode_path,
4941 value, v_tail = spec.decode(
4945 decode_path=sub_decode_path,
4947 _ctx_immutable=False,
4949 value_len = value.fulllen
4950 if value_prev.tobytes() > v[:value_len].tobytes():
4951 if ctx_bered or ctx_allow_unordered_set:
4955 "unordered " + self.asn1_type_name,
4956 klass=self.__class__,
4957 decode_path=sub_decode_path,
4960 if spec.default is None or value != spec.default:
4962 elif ctx_bered or ctx_allow_default_values:
4966 "DEFAULT value met",
4967 klass=self.__class__,
4968 decode_path=sub_decode_path,
4971 values[name] = value
4972 value_prev = v[:value_len]
4973 sub_offset += value_len
4976 obj = self.__class__(
4980 default=self.default,
4981 optional=self.optional,
4982 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4985 if v[:EOC_LEN].tobytes() != EOC:
4988 klass=self.__class__,
4989 decode_path=decode_path,
4997 "not all values are ready",
4998 klass=self.__class__,
4999 decode_path=decode_path,
5002 obj.ber_encoded = ber_encoded
5006 class SequenceOf(Obj):
5007 """``SEQUENCE OF`` sequence type
5009 For that kind of type you must specify the object it will carry on
5010 (bounds are for example here, not required)::
5012 class Ints(SequenceOf):
5017 >>> ints.append(Integer(123))
5018 >>> ints.append(Integer(234))
5020 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5021 >>> [int(i) for i in ints]
5023 >>> ints.append(Integer(345))
5024 Traceback (most recent call last):
5025 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5028 >>> ints[1] = Integer(345)
5030 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5032 Also you can initialize sequence with preinitialized values:
5034 >>> ints = Ints([Integer(123), Integer(234)])
5036 __slots__ = ("spec", "_bound_min", "_bound_max")
5037 tag_default = tag_encode(form=TagFormConstructed, num=16)
5038 asn1_type_name = "SEQUENCE OF"
5051 super(SequenceOf, self).__init__(
5059 schema = getattr(self, "schema", None)
5061 raise ValueError("schema must be specified")
5063 self._bound_min, self._bound_max = getattr(
5067 ) if bounds is None else bounds
5069 if value is not None:
5070 self._value = self._value_sanitize(value)
5071 if default is not None:
5072 default_value = self._value_sanitize(default)
5073 default_obj = self.__class__(
5078 default_obj._value = default_value
5079 self.default = default_obj
5081 self._value = default_obj.copy()._value
5083 def _value_sanitize(self, value):
5084 if issubclass(value.__class__, SequenceOf):
5085 value = value._value
5086 elif hasattr(value, "__iter__"):
5089 raise InvalidValueType((self.__class__, iter))
5090 if not self._bound_min <= len(value) <= self._bound_max:
5091 raise BoundsError(self._bound_min, len(value), self._bound_max)
5093 if not isinstance(v, self.spec.__class__):
5094 raise InvalidValueType((self.spec.__class__,))
5099 return all(v.ready for v in self._value)
5103 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5105 return any(v.bered for v in self._value)
5108 obj = self.__class__(schema=self.spec)
5109 obj._bound_min = self._bound_min
5110 obj._bound_max = self._bound_max
5112 obj._expl = self._expl
5113 obj.default = self.default
5114 obj.optional = self.optional
5115 obj.offset = self.offset
5116 obj.llen = self.llen
5117 obj.vlen = self.vlen
5118 obj._value = [v.copy() for v in self._value]
5121 def __eq__(self, their):
5122 if isinstance(their, self.__class__):
5124 self.spec == their.spec and
5125 self.tag == their.tag and
5126 self._expl == their._expl and
5127 self._value == their._value
5129 if hasattr(their, "__iter__"):
5130 return self._value == list(their)
5142 return self.__class__(
5146 (self._bound_min, self._bound_max)
5147 if bounds is None else bounds
5149 impl=self.tag if impl is None else impl,
5150 expl=self._expl if expl is None else expl,
5151 default=self.default if default is None else default,
5152 optional=self.optional if optional is None else optional,
5155 def __contains__(self, key):
5156 return key in self._value
5158 def append(self, value):
5159 if not isinstance(value, self.spec.__class__):
5160 raise InvalidValueType((self.spec.__class__,))
5161 if len(self._value) + 1 > self._bound_max:
5164 len(self._value) + 1,
5167 self._value.append(value)
5170 self._assert_ready()
5171 return iter(self._value)
5174 self._assert_ready()
5175 return len(self._value)
5177 def __setitem__(self, key, value):
5178 if not isinstance(value, self.spec.__class__):
5179 raise InvalidValueType((self.spec.__class__,))
5180 self._value[key] = self.spec(value=value)
5182 def __getitem__(self, key):
5183 return self._value[key]
5185 def _encoded_values(self):
5186 return [v.encode() for v in self._value]
5189 v = b"".join(self._encoded_values())
5190 return b"".join((self.tag, len_encode(len(v)), v))
5192 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5194 t, tlen, lv = tag_strip(tlv)
5195 except DecodeError as err:
5196 raise err.__class__(
5198 klass=self.__class__,
5199 decode_path=decode_path,
5204 klass=self.__class__,
5205 decode_path=decode_path,
5211 ctx_bered = ctx.get("bered", False)
5213 l, llen, v = len_decode(lv)
5214 except LenIndefForm as err:
5216 raise err.__class__(
5218 klass=self.__class__,
5219 decode_path=decode_path,
5222 l, llen, v = 0, 1, lv[1:]
5224 except DecodeError as err:
5225 raise err.__class__(
5227 klass=self.__class__,
5228 decode_path=decode_path,
5232 raise NotEnoughData(
5233 "encoded length is longer than data",
5234 klass=self.__class__,
5235 decode_path=decode_path,
5239 v, tail = v[:l], v[l:]
5241 sub_offset = offset + tlen + llen
5243 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5244 value_prev = memoryview(v[:0])
5248 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5250 sub_decode_path = decode_path + (str(len(_value)),)
5251 value, v_tail = spec.decode(
5255 decode_path=sub_decode_path,
5257 _ctx_immutable=False,
5259 value_len = value.fulllen
5261 if value_prev.tobytes() > v[:value_len].tobytes():
5262 if ctx_bered or ctx_allow_unordered_set:
5266 "unordered " + self.asn1_type_name,
5267 klass=self.__class__,
5268 decode_path=sub_decode_path,
5271 value_prev = v[:value_len]
5272 _value.append(value)
5273 sub_offset += value_len
5277 obj = self.__class__(
5280 bounds=(self._bound_min, self._bound_max),
5283 default=self.default,
5284 optional=self.optional,
5285 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5287 except BoundsError as err:
5290 klass=self.__class__,
5291 decode_path=decode_path,
5295 if v[:EOC_LEN].tobytes() != EOC:
5298 klass=self.__class__,
5299 decode_path=decode_path,
5304 obj.ber_encoded = ber_encoded
5309 pp_console_row(next(self.pps())),
5310 ", ".join(repr(v) for v in self._value),
5313 def pps(self, decode_path=()):
5316 asn1_type_name=self.asn1_type_name,
5317 obj_name=self.__class__.__name__,
5318 decode_path=decode_path,
5319 optional=self.optional,
5320 default=self == self.default,
5321 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5322 expl=None if self._expl is None else tag_decode(self._expl),
5327 expl_offset=self.expl_offset if self.expled else None,
5328 expl_tlen=self.expl_tlen if self.expled else None,
5329 expl_llen=self.expl_llen if self.expled else None,
5330 expl_vlen=self.expl_vlen if self.expled else None,
5331 expl_lenindef=self.expl_lenindef,
5332 lenindef=self.lenindef,
5333 ber_encoded=self.ber_encoded,
5336 for i, value in enumerate(self._value):
5337 yield value.pps(decode_path=decode_path + (str(i),))
5338 for pp in self.pps_lenindef(decode_path):
5342 class SetOf(SequenceOf):
5343 """``SET OF`` sequence type
5345 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5348 tag_default = tag_encode(form=TagFormConstructed, num=17)
5349 asn1_type_name = "SET OF"
5352 raws = self._encoded_values()
5355 return b"".join((self.tag, len_encode(len(v)), v))
5357 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5358 return super(SetOf, self)._decode(
5364 ordering_check=True,
5368 def obj_by_path(pypath): # pragma: no cover
5369 """Import object specified as string Python path
5371 Modules must be separated from classes/functions with ``:``.
5373 >>> obj_by_path("foo.bar:Baz")
5374 <class 'foo.bar.Baz'>
5375 >>> obj_by_path("foo.bar:Baz.boo")
5376 <classmethod 'foo.bar.Baz.boo'>
5378 mod, objs = pypath.rsplit(":", 1)
5379 from importlib import import_module
5380 obj = import_module(mod)
5381 for obj_name in objs.split("."):
5382 obj = getattr(obj, obj_name)
5386 def generic_decoder(): # pragma: no cover
5387 # All of this below is a big hack with self references
5388 choice = PrimitiveTypes()
5389 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5390 choice.specs["SetOf"] = SetOf(schema=choice)
5392 choice.specs["SequenceOf%d" % i] = SequenceOf(
5396 choice.specs["Any"] = Any()
5398 # Class name equals to type name, to omit it from output
5399 class SEQUENCEOF(SequenceOf):
5407 with_decode_path=False,
5408 decode_path_only=(),
5410 def _pprint_pps(pps):
5412 if hasattr(pp, "_fields"):
5414 decode_path_only != () and
5415 pp.decode_path[:len(decode_path_only)] != decode_path_only
5418 if pp.asn1_type_name == Choice.asn1_type_name:
5420 pp_kwargs = pp._asdict()
5421 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5422 pp = _pp(**pp_kwargs)
5423 yield pp_console_row(
5428 with_colours=with_colours,
5429 with_decode_path=with_decode_path,
5430 decode_path_len_decrease=len(decode_path_only),
5432 for row in pp_console_blob(
5434 decode_path_len_decrease=len(decode_path_only),
5438 for row in _pprint_pps(pp):
5440 return "\n".join(_pprint_pps(obj.pps()))
5441 return SEQUENCEOF(), pprint_any
5444 def main(): # pragma: no cover
5446 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5447 parser.add_argument(
5451 help="Skip that number of bytes from the beginning",
5453 parser.add_argument(
5455 help="Python path to dictionary with OIDs",
5457 parser.add_argument(
5459 help="Python path to schema definition to use",
5461 parser.add_argument(
5462 "--defines-by-path",
5463 help="Python path to decoder's defines_by_path",
5465 parser.add_argument(
5467 action="store_true",
5468 help="Disallow BER encoding",
5470 parser.add_argument(
5471 "--print-decode-path",
5472 action="store_true",
5473 help="Print decode paths",
5475 parser.add_argument(
5476 "--decode-path-only",
5477 help="Print only specified decode path",
5479 parser.add_argument(
5481 action="store_true",
5482 help="Allow explicit tag out-of-bound",
5484 parser.add_argument(
5486 type=argparse.FileType("rb"),
5487 help="Path to DER file you want to decode",
5489 args = parser.parse_args()
5490 args.DERFile.seek(args.skip)
5491 der = memoryview(args.DERFile.read())
5492 args.DERFile.close()
5493 oids = obj_by_path(args.oids) if args.oids else {}
5495 schema = obj_by_path(args.schema)
5496 from functools import partial
5497 pprinter = partial(pprint, big_blobs=True)
5499 schema, pprinter = generic_decoder()
5501 "bered": not args.nobered,
5502 "allow_expl_oob": args.allow_expl_oob,
5504 if args.defines_by_path is not None:
5505 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5506 obj, tail = schema().decode(der, ctx=ctx)
5510 with_colours=True if environ.get("NO_COLOR") is None else False,
5511 with_decode_path=args.print_decode_path,
5513 () if args.decode_path_only is None else
5514 tuple(args.decode_path_only.split(":"))
5518 print("\nTrailing data: %s" % hexenc(tail))
5521 if __name__ == "__main__":