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.hexenc
519 .. autofunction:: pyderasn.hexdec
520 .. autofunction:: pyderasn.tag_encode
521 .. autofunction:: pyderasn.tag_decode
522 .. autofunction:: pyderasn.tag_ctxp
523 .. autofunction:: pyderasn.tag_ctxc
524 .. autoclass:: pyderasn.Obj
525 .. autoclass:: pyderasn.DecodeError
527 .. autoclass:: pyderasn.NotEnoughData
528 .. autoclass:: pyderasn.LenIndefForm
529 .. autoclass:: pyderasn.TagMismatch
530 .. autoclass:: pyderasn.InvalidLength
531 .. autoclass:: pyderasn.InvalidOID
532 .. autoclass:: pyderasn.ObjUnknown
533 .. autoclass:: pyderasn.ObjNotReady
534 .. autoclass:: pyderasn.InvalidValueType
535 .. autoclass:: pyderasn.BoundsError
538 from codecs import getdecoder
539 from codecs import getencoder
540 from collections import namedtuple
541 from collections import OrderedDict
542 from copy import copy
543 from datetime import datetime
544 from math import ceil
545 from os import environ
546 from string import ascii_letters
547 from string import digits
549 from six import add_metaclass
550 from six import binary_type
551 from six import byte2int
552 from six import indexbytes
553 from six import int2byte
554 from six import integer_types
555 from six import iterbytes
557 from six import string_types
558 from six import text_type
559 from six import unichr as six_unichr
560 from six.moves import xrange as six_xrange
564 from termcolor import colored
565 except ImportError: # pragma: no cover
566 def colored(what, *args):
610 "TagClassApplication",
614 "TagFormConstructed",
625 TagClassUniversal = 0
626 TagClassApplication = 1 << 6
627 TagClassContext = 1 << 7
628 TagClassPrivate = 1 << 6 | 1 << 7
630 TagFormConstructed = 1 << 5
633 TagClassApplication: "APPLICATION ",
634 TagClassPrivate: "PRIVATE ",
635 TagClassUniversal: "UNIV ",
639 LENINDEF = b"\x80" # length indefinite mark
640 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
643 ########################################################################
645 ########################################################################
647 class ASN1Error(ValueError):
651 class DecodeError(ASN1Error):
652 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
654 :param str msg: reason of decode failing
655 :param klass: optional exact DecodeError inherited class (like
656 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
657 :py:exc:`InvalidLength`)
658 :param decode_path: tuple of strings. It contains human
659 readable names of the fields through which
660 decoding process has passed
661 :param int offset: binary offset where failure happened
663 super(DecodeError, self).__init__()
666 self.decode_path = decode_path
672 "" if self.klass is None else self.klass.__name__,
674 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
675 if len(self.decode_path) > 0 else ""
677 ("(at %d)" % self.offset) if self.offset > 0 else "",
683 return "%s(%s)" % (self.__class__.__name__, self)
686 class NotEnoughData(DecodeError):
690 class LenIndefForm(DecodeError):
694 class TagMismatch(DecodeError):
698 class InvalidLength(DecodeError):
702 class InvalidOID(DecodeError):
706 class ObjUnknown(ASN1Error):
707 def __init__(self, name):
708 super(ObjUnknown, self).__init__()
712 return "object is unknown: %s" % self.name
715 return "%s(%s)" % (self.__class__.__name__, self)
718 class ObjNotReady(ASN1Error):
719 def __init__(self, name):
720 super(ObjNotReady, self).__init__()
724 return "object is not ready: %s" % self.name
727 return "%s(%s)" % (self.__class__.__name__, self)
730 class InvalidValueType(ASN1Error):
731 def __init__(self, expected_types):
732 super(InvalidValueType, self).__init__()
733 self.expected_types = expected_types
736 return "invalid value type, expected: %s" % ", ".join(
737 [repr(t) for t in self.expected_types]
741 return "%s(%s)" % (self.__class__.__name__, self)
744 class BoundsError(ASN1Error):
745 def __init__(self, bound_min, value, bound_max):
746 super(BoundsError, self).__init__()
747 self.bound_min = bound_min
749 self.bound_max = bound_max
752 return "unsatisfied bounds: %s <= %s <= %s" % (
759 return "%s(%s)" % (self.__class__.__name__, self)
762 ########################################################################
764 ########################################################################
766 _hexdecoder = getdecoder("hex")
767 _hexencoder = getencoder("hex")
771 """Binary data to hexadecimal string convert
773 return _hexdecoder(data)[0]
777 """Hexadecimal string to binary data convert
779 return _hexencoder(data)[0].decode("ascii")
782 def int_bytes_len(num, byte_len=8):
785 return int(ceil(float(num.bit_length()) / byte_len))
788 def zero_ended_encode(num):
789 octets = bytearray(int_bytes_len(num, 7))
791 octets[i] = num & 0x7F
795 octets[i] = 0x80 | (num & 0x7F)
801 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
802 """Encode tag to binary form
804 :param int num: tag's number
805 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
806 :py:data:`pyderasn.TagClassContext`,
807 :py:data:`pyderasn.TagClassApplication`,
808 :py:data:`pyderasn.TagClassPrivate`)
809 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
810 :py:data:`pyderasn.TagFormConstructed`)
814 return int2byte(klass | form | num)
815 # [XX|X|11111][1.......][1.......] ... [0.......]
816 return int2byte(klass | form | 31) + zero_ended_encode(num)
820 """Decode tag from binary form
824 No validation is performed, assuming that it has already passed.
826 It returns tuple with three integers, as
827 :py:func:`pyderasn.tag_encode` accepts.
829 first_octet = byte2int(tag)
830 klass = first_octet & 0xC0
831 form = first_octet & 0x20
832 if first_octet & 0x1F < 0x1F:
833 return (klass, form, first_octet & 0x1F)
835 for octet in iterbytes(tag[1:]):
838 return (klass, form, num)
842 """Create CONTEXT PRIMITIVE tag
844 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
848 """Create CONTEXT CONSTRUCTED tag
850 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
854 """Take off tag from the data
856 :returns: (encoded tag, tag length, remaining data)
859 raise NotEnoughData("no data at all")
860 if byte2int(data) & 0x1F < 31:
861 return data[:1], 1, data[1:]
866 raise DecodeError("unfinished tag")
867 if indexbytes(data, i) & 0x80 == 0:
870 return data[:i], i, data[i:]
876 octets = bytearray(int_bytes_len(l) + 1)
877 octets[0] = 0x80 | (len(octets) - 1)
878 for i in six_xrange(len(octets) - 1, 0, -1):
884 def len_decode(data):
887 :returns: (decoded length, length's length, remaining data)
888 :raises LenIndefForm: if indefinite form encoding is met
891 raise NotEnoughData("no data at all")
892 first_octet = byte2int(data)
893 if first_octet & 0x80 == 0:
894 return first_octet, 1, data[1:]
895 octets_num = first_octet & 0x7F
896 if octets_num + 1 > len(data):
897 raise NotEnoughData("encoded length is longer than data")
900 if byte2int(data[1:]) == 0:
901 raise DecodeError("leading zeros")
903 for v in iterbytes(data[1:1 + octets_num]):
906 raise DecodeError("long form instead of short one")
907 return l, 1 + octets_num, data[1 + octets_num:]
910 ########################################################################
912 ########################################################################
914 class AutoAddSlots(type):
915 def __new__(mcs, name, bases, _dict):
916 _dict["__slots__"] = _dict.get("__slots__", ())
917 return type.__new__(mcs, name, bases, _dict)
920 @add_metaclass(AutoAddSlots)
922 """Common ASN.1 object class
924 All ASN.1 types are inherited from it. It has metaclass that
925 automatically adds ``__slots__`` to all inherited classes.
949 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
950 self._expl = getattr(self, "expl", None) if expl is None else expl
951 if self.tag != self.tag_default and self._expl is not None:
952 raise ValueError("implicit and explicit tags can not be set simultaneously")
953 if default is not None:
955 self.optional = optional
956 self.offset, self.llen, self.vlen = _decoded
958 self.expl_lenindef = False
959 self.lenindef = False
960 self.ber_encoded = False
963 def ready(self): # pragma: no cover
964 """Is object ready to be encoded?
966 raise NotImplementedError()
968 def _assert_ready(self):
970 raise ObjNotReady(self.__class__.__name__)
974 """Is either object or any elements inside is BER encoded?
976 return self.expl_lenindef or self.lenindef or self.ber_encoded
980 """Is object decoded?
982 return (self.llen + self.vlen) > 0
984 def copy(self): # pragma: no cover
985 """Make a copy of object, safe to be mutated
987 raise NotImplementedError()
995 return self.tlen + self.llen + self.vlen
997 def __str__(self): # pragma: no cover
998 return self.__bytes__() if PY2 else self.__unicode__()
1000 def __ne__(self, their):
1001 return not(self == their)
1003 def __gt__(self, their): # pragma: no cover
1004 return not(self < their)
1006 def __le__(self, their): # pragma: no cover
1007 return (self == their) or (self < their)
1009 def __ge__(self, their): # pragma: no cover
1010 return (self == their) or (self > their)
1012 def _encode(self): # pragma: no cover
1013 raise NotImplementedError()
1015 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1016 raise NotImplementedError()
1019 raw = self._encode()
1020 if self._expl is None:
1022 return b"".join((self._expl, len_encode(len(raw)), raw))
1032 _ctx_immutable=True,
1036 :param data: either binary or memoryview
1037 :param int offset: initial data's offset
1038 :param bool leavemm: do we need to leave memoryview of remaining
1039 data as is, or convert it to bytes otherwise
1040 :param ctx: optional :ref:`context <ctx>` governing decoding process
1041 :param tag_only: decode only the tag, without length and contents
1042 (used only in Choice and Set structures, trying to
1043 determine if tag satisfies the scheme)
1044 :param _ctx_immutable: do we need to copy ``ctx`` before using it
1045 :returns: (Obj, remaining data)
1049 elif _ctx_immutable:
1051 tlv = memoryview(data)
1052 if self._expl is None:
1053 result = self._decode(
1056 decode_path=decode_path,
1065 t, tlen, lv = tag_strip(tlv)
1066 except DecodeError as err:
1067 raise err.__class__(
1069 klass=self.__class__,
1070 decode_path=decode_path,
1075 klass=self.__class__,
1076 decode_path=decode_path,
1080 l, llen, v = len_decode(lv)
1081 except LenIndefForm as err:
1082 if not ctx.get("bered", False):
1083 raise err.__class__(
1085 klass=self.__class__,
1086 decode_path=decode_path,
1090 offset += tlen + llen
1091 result = self._decode(
1094 decode_path=decode_path,
1098 if tag_only: # pragma: no cover
1101 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1102 if eoc_expected.tobytes() != EOC:
1105 klass=self.__class__,
1106 decode_path=decode_path,
1110 obj.expl_lenindef = True
1111 except DecodeError as err:
1112 raise err.__class__(
1114 klass=self.__class__,
1115 decode_path=decode_path,
1120 raise NotEnoughData(
1121 "encoded length is longer than data",
1122 klass=self.__class__,
1123 decode_path=decode_path,
1126 result = self._decode(
1128 offset=offset + tlen + llen,
1129 decode_path=decode_path,
1133 if tag_only: # pragma: no cover
1136 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1138 "explicit tag out-of-bound, longer than data",
1139 klass=self.__class__,
1140 decode_path=decode_path,
1143 return obj, (tail if leavemm else tail.tobytes())
1147 return self._expl is not None
1154 def expl_tlen(self):
1155 return len(self._expl)
1158 def expl_llen(self):
1159 if self.expl_lenindef:
1161 return len(len_encode(self.tlvlen))
1164 def expl_offset(self):
1165 return self.offset - self.expl_tlen - self.expl_llen
1168 def expl_vlen(self):
1172 def expl_tlvlen(self):
1173 return self.expl_tlen + self.expl_llen + self.expl_vlen
1176 def fulloffset(self):
1177 return self.expl_offset if self.expled else self.offset
1181 return self.expl_tlvlen if self.expled else self.tlvlen
1183 def pps_lenindef(self, decode_path):
1184 if self.lenindef and not (
1185 getattr(self, "defined", None) is not None and
1186 self.defined[1].lenindef
1189 asn1_type_name="EOC",
1191 decode_path=decode_path,
1193 self.offset + self.tlvlen -
1194 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1202 if self.expl_lenindef:
1204 asn1_type_name="EOC",
1205 obj_name="EXPLICIT",
1206 decode_path=decode_path,
1207 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1216 class DecodePathDefBy(object):
1217 """DEFINED BY representation inside decode path
1219 __slots__ = ("defined_by",)
1221 def __init__(self, defined_by):
1222 self.defined_by = defined_by
1224 def __ne__(self, their):
1225 return not(self == their)
1227 def __eq__(self, their):
1228 if not isinstance(their, self.__class__):
1230 return self.defined_by == their.defined_by
1233 return "DEFINED BY " + str(self.defined_by)
1236 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1239 ########################################################################
1241 ########################################################################
1243 PP = namedtuple("PP", (
1269 asn1_type_name="unknown",
1286 expl_lenindef=False,
1316 def _colourize(what, colour, with_colours, attrs=("bold",)):
1317 return colored(what, colour, attrs=attrs) if with_colours else what
1326 with_decode_path=False,
1327 decode_path_len_decrease=0,
1334 " " if pp.expl_offset is None else
1335 ("-%d" % (pp.offset - pp.expl_offset))
1337 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1339 col = _colourize(col, "red", with_colours, ())
1340 col += _colourize("B", "red", with_colours) if pp.bered else " "
1342 col = "[%d,%d,%4d]%s" % (
1346 LENINDEF_PP_CHAR if pp.lenindef else " "
1348 col = _colourize(col, "green", with_colours, ())
1350 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1351 if decode_path_len > 0:
1352 cols.append(" ." * decode_path_len)
1353 ent = pp.decode_path[-1]
1354 if isinstance(ent, DecodePathDefBy):
1355 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1356 value = str(ent.defined_by)
1358 oids is not None and
1359 ent.defined_by.asn1_type_name ==
1360 ObjectIdentifier.asn1_type_name and
1363 cols.append(_colourize("%s:" % oids[value], "green", with_colours))
1365 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1367 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1368 if pp.expl is not None:
1369 klass, _, num = pp.expl
1370 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1371 cols.append(_colourize(col, "blue", with_colours))
1372 if pp.impl is not None:
1373 klass, _, num = pp.impl
1374 col = "[%s%d]" % (TagClassReprs[klass], num)
1375 cols.append(_colourize(col, "blue", with_colours))
1376 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1377 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1379 cols.append(_colourize("BER", "red", with_colours))
1380 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1381 if pp.value is not None:
1383 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1385 oids is not None and
1386 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1389 cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
1391 if isinstance(pp.blob, binary_type):
1392 cols.append(hexenc(pp.blob))
1393 elif isinstance(pp.blob, tuple):
1394 cols.append(", ".join(pp.blob))
1396 cols.append(_colourize("OPTIONAL", "red", with_colours))
1398 cols.append(_colourize("DEFAULT", "red", with_colours))
1399 if with_decode_path:
1400 cols.append(_colourize(
1401 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1405 return " ".join(cols)
1408 def pp_console_blob(pp, decode_path_len_decrease=0):
1409 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1410 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1411 if decode_path_len > 0:
1412 cols.append(" ." * (decode_path_len + 1))
1413 if isinstance(pp.blob, binary_type):
1414 blob = hexenc(pp.blob).upper()
1415 for i in range(0, len(blob), 32):
1416 chunk = blob[i:i + 32]
1417 yield " ".join(cols + [":".join(
1418 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1420 elif isinstance(pp.blob, tuple):
1421 yield " ".join(cols + [", ".join(pp.blob)])
1429 with_decode_path=False,
1430 decode_path_only=(),
1432 """Pretty print object
1434 :param Obj obj: object you want to pretty print
1435 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1436 from it is met, then its humand readable form is printed
1437 :param big_blobs: if large binary objects are met (like OctetString
1438 values), do we need to print them too, on separate
1440 :param with_colours: colourize output, if ``termcolor`` library
1442 :param with_decode_path: print decode path
1443 :param decode_path_only: print only that specified decode path
1445 def _pprint_pps(pps):
1447 if hasattr(pp, "_fields"):
1449 decode_path_only != () and
1451 str(p) for p in pp.decode_path[:len(decode_path_only)]
1452 ) != decode_path_only
1456 yield pp_console_row(
1461 with_colours=with_colours,
1462 with_decode_path=with_decode_path,
1463 decode_path_len_decrease=len(decode_path_only),
1465 for row in pp_console_blob(
1467 decode_path_len_decrease=len(decode_path_only),
1471 yield pp_console_row(
1476 with_colours=with_colours,
1477 with_decode_path=with_decode_path,
1478 decode_path_len_decrease=len(decode_path_only),
1481 for row in _pprint_pps(pp):
1483 return "\n".join(_pprint_pps(obj.pps()))
1486 ########################################################################
1487 # ASN.1 primitive types
1488 ########################################################################
1491 """``BOOLEAN`` boolean type
1493 >>> b = Boolean(True)
1495 >>> b == Boolean(True)
1501 tag_default = tag_encode(1)
1502 asn1_type_name = "BOOLEAN"
1514 :param value: set the value. Either boolean type, or
1515 :py:class:`pyderasn.Boolean` object
1516 :param bytes impl: override default tag with ``IMPLICIT`` one
1517 :param bytes expl: override default tag with ``EXPLICIT`` one
1518 :param default: set default value. Type same as in ``value``
1519 :param bool optional: is object ``OPTIONAL`` in sequence
1521 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1522 self._value = None if value is None else self._value_sanitize(value)
1523 if default is not None:
1524 default = self._value_sanitize(default)
1525 self.default = self.__class__(
1531 self._value = default
1533 def _value_sanitize(self, value):
1534 if issubclass(value.__class__, Boolean):
1536 if isinstance(value, bool):
1538 raise InvalidValueType((self.__class__, bool))
1542 return self._value is not None
1545 obj = self.__class__()
1546 obj._value = self._value
1548 obj._expl = self._expl
1549 obj.default = self.default
1550 obj.optional = self.optional
1551 obj.offset = self.offset
1552 obj.llen = self.llen
1553 obj.vlen = self.vlen
1556 def __nonzero__(self):
1557 self._assert_ready()
1561 self._assert_ready()
1564 def __eq__(self, their):
1565 if isinstance(their, bool):
1566 return self._value == their
1567 if not issubclass(their.__class__, Boolean):
1570 self._value == their._value and
1571 self.tag == their.tag and
1572 self._expl == their._expl
1583 return self.__class__(
1585 impl=self.tag if impl is None else impl,
1586 expl=self._expl if expl is None else expl,
1587 default=self.default if default is None else default,
1588 optional=self.optional if optional is None else optional,
1592 self._assert_ready()
1596 (b"\xFF" if self._value else b"\x00"),
1599 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1601 t, _, lv = tag_strip(tlv)
1602 except DecodeError as err:
1603 raise err.__class__(
1605 klass=self.__class__,
1606 decode_path=decode_path,
1611 klass=self.__class__,
1612 decode_path=decode_path,
1618 l, _, v = len_decode(lv)
1619 except DecodeError as err:
1620 raise err.__class__(
1622 klass=self.__class__,
1623 decode_path=decode_path,
1627 raise InvalidLength(
1628 "Boolean's length must be equal to 1",
1629 klass=self.__class__,
1630 decode_path=decode_path,
1634 raise NotEnoughData(
1635 "encoded length is longer than data",
1636 klass=self.__class__,
1637 decode_path=decode_path,
1640 first_octet = byte2int(v)
1642 if first_octet == 0:
1644 elif first_octet == 0xFF:
1646 elif ctx.get("bered", False):
1651 "unacceptable Boolean value",
1652 klass=self.__class__,
1653 decode_path=decode_path,
1656 obj = self.__class__(
1660 default=self.default,
1661 optional=self.optional,
1662 _decoded=(offset, 1, 1),
1664 obj.ber_encoded = ber_encoded
1668 return pp_console_row(next(self.pps()))
1670 def pps(self, decode_path=()):
1672 asn1_type_name=self.asn1_type_name,
1673 obj_name=self.__class__.__name__,
1674 decode_path=decode_path,
1675 value=str(self._value) if self.ready else None,
1676 optional=self.optional,
1677 default=self == self.default,
1678 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1679 expl=None if self._expl is None else tag_decode(self._expl),
1684 expl_offset=self.expl_offset if self.expled else None,
1685 expl_tlen=self.expl_tlen if self.expled else None,
1686 expl_llen=self.expl_llen if self.expled else None,
1687 expl_vlen=self.expl_vlen if self.expled else None,
1688 expl_lenindef=self.expl_lenindef,
1689 ber_encoded=self.ber_encoded,
1692 for pp in self.pps_lenindef(decode_path):
1697 """``INTEGER`` integer type
1699 >>> b = Integer(-123)
1701 >>> b == Integer(-123)
1706 >>> Integer(2, bounds=(1, 3))
1708 >>> Integer(5, bounds=(1, 3))
1709 Traceback (most recent call last):
1710 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1714 class Version(Integer):
1721 >>> v = Version("v1")
1728 {'v3': 2, 'v1': 0, 'v2': 1}
1730 __slots__ = ("specs", "_bound_min", "_bound_max")
1731 tag_default = tag_encode(2)
1732 asn1_type_name = "INTEGER"
1746 :param value: set the value. Either integer type, named value
1747 (if ``schema`` is specified in the class), or
1748 :py:class:`pyderasn.Integer` object
1749 :param bounds: set ``(MIN, MAX)`` value constraint.
1750 (-inf, +inf) by default
1751 :param bytes impl: override default tag with ``IMPLICIT`` one
1752 :param bytes expl: override default tag with ``EXPLICIT`` one
1753 :param default: set default value. Type same as in ``value``
1754 :param bool optional: is object ``OPTIONAL`` in sequence
1756 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1758 specs = getattr(self, "schema", {}) if _specs is None else _specs
1759 self.specs = specs if isinstance(specs, dict) else dict(specs)
1760 self._bound_min, self._bound_max = getattr(
1763 (float("-inf"), float("+inf")),
1764 ) if bounds is None else bounds
1765 if value is not None:
1766 self._value = self._value_sanitize(value)
1767 if default is not None:
1768 default = self._value_sanitize(default)
1769 self.default = self.__class__(
1775 if self._value is None:
1776 self._value = default
1778 def _value_sanitize(self, value):
1779 if issubclass(value.__class__, Integer):
1780 value = value._value
1781 elif isinstance(value, integer_types):
1783 elif isinstance(value, str):
1784 value = self.specs.get(value)
1786 raise ObjUnknown("integer value: %s" % value)
1788 raise InvalidValueType((self.__class__, int, str))
1789 if not self._bound_min <= value <= self._bound_max:
1790 raise BoundsError(self._bound_min, value, self._bound_max)
1795 return self._value is not None
1798 obj = self.__class__(_specs=self.specs)
1799 obj._value = self._value
1800 obj._bound_min = self._bound_min
1801 obj._bound_max = self._bound_max
1803 obj._expl = self._expl
1804 obj.default = self.default
1805 obj.optional = self.optional
1806 obj.offset = self.offset
1807 obj.llen = self.llen
1808 obj.vlen = self.vlen
1812 self._assert_ready()
1813 return int(self._value)
1816 self._assert_ready()
1819 bytes(self._expl or b"") +
1820 str(self._value).encode("ascii"),
1823 def __eq__(self, their):
1824 if isinstance(their, integer_types):
1825 return self._value == their
1826 if not issubclass(their.__class__, Integer):
1829 self._value == their._value and
1830 self.tag == their.tag and
1831 self._expl == their._expl
1834 def __lt__(self, their):
1835 return self._value < their._value
1839 for name, value in self.specs.items():
1840 if value == self._value:
1852 return self.__class__(
1855 (self._bound_min, self._bound_max)
1856 if bounds is None else bounds
1858 impl=self.tag if impl is None else impl,
1859 expl=self._expl if expl is None else expl,
1860 default=self.default if default is None else default,
1861 optional=self.optional if optional is None else optional,
1866 self._assert_ready()
1870 octets = bytearray([0])
1874 octets = bytearray()
1876 octets.append((value & 0xFF) ^ 0xFF)
1878 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1881 octets = bytearray()
1883 octets.append(value & 0xFF)
1885 if octets[-1] & 0x80 > 0:
1888 octets = bytes(octets)
1890 bytes_len = ceil(value.bit_length() / 8) or 1
1893 octets = value.to_bytes(
1898 except OverflowError:
1902 return b"".join((self.tag, len_encode(len(octets)), octets))
1904 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1906 t, _, lv = tag_strip(tlv)
1907 except DecodeError as err:
1908 raise err.__class__(
1910 klass=self.__class__,
1911 decode_path=decode_path,
1916 klass=self.__class__,
1917 decode_path=decode_path,
1923 l, llen, v = len_decode(lv)
1924 except DecodeError as err:
1925 raise err.__class__(
1927 klass=self.__class__,
1928 decode_path=decode_path,
1932 raise NotEnoughData(
1933 "encoded length is longer than data",
1934 klass=self.__class__,
1935 decode_path=decode_path,
1939 raise NotEnoughData(
1941 klass=self.__class__,
1942 decode_path=decode_path,
1945 v, tail = v[:l], v[l:]
1946 first_octet = byte2int(v)
1948 second_octet = byte2int(v[1:])
1950 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1951 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1954 "non normalized integer",
1955 klass=self.__class__,
1956 decode_path=decode_path,
1961 if first_octet & 0x80 > 0:
1962 octets = bytearray()
1963 for octet in bytearray(v):
1964 octets.append(octet ^ 0xFF)
1965 for octet in octets:
1966 value = (value << 8) | octet
1970 for octet in bytearray(v):
1971 value = (value << 8) | octet
1973 value = int.from_bytes(v, byteorder="big", signed=True)
1975 obj = self.__class__(
1977 bounds=(self._bound_min, self._bound_max),
1980 default=self.default,
1981 optional=self.optional,
1983 _decoded=(offset, llen, l),
1985 except BoundsError as err:
1988 klass=self.__class__,
1989 decode_path=decode_path,
1995 return pp_console_row(next(self.pps()))
1997 def pps(self, decode_path=()):
1999 asn1_type_name=self.asn1_type_name,
2000 obj_name=self.__class__.__name__,
2001 decode_path=decode_path,
2002 value=(self.named or str(self._value)) if self.ready else None,
2003 optional=self.optional,
2004 default=self == self.default,
2005 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2006 expl=None if self._expl is None else tag_decode(self._expl),
2011 expl_offset=self.expl_offset if self.expled else None,
2012 expl_tlen=self.expl_tlen if self.expled else None,
2013 expl_llen=self.expl_llen if self.expled else None,
2014 expl_vlen=self.expl_vlen if self.expled else None,
2015 expl_lenindef=self.expl_lenindef,
2018 for pp in self.pps_lenindef(decode_path):
2022 class BitString(Obj):
2023 """``BIT STRING`` bit string type
2025 >>> BitString(b"hello world")
2026 BIT STRING 88 bits 68656c6c6f20776f726c64
2029 >>> b == b"hello world"
2034 >>> BitString("'0A3B5F291CD'H")
2035 BIT STRING 44 bits 0a3b5f291cd0
2036 >>> b = BitString("'010110000000'B")
2037 BIT STRING 12 bits 5800
2040 >>> b[0], b[1], b[2], b[3]
2041 (False, True, False, True)
2045 [False, True, False, True, True, False, False, False, False, False, False, False]
2049 class KeyUsage(BitString):
2051 ("digitalSignature", 0),
2052 ("nonRepudiation", 1),
2053 ("keyEncipherment", 2),
2056 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2057 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2059 ['nonRepudiation', 'keyEncipherment']
2061 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2065 Pay attention that BIT STRING can be encoded both in primitive
2066 and constructed forms. Decoder always checks constructed form tag
2067 additionally to specified primitive one. If BER decoding is
2068 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2069 of DER restrictions.
2071 __slots__ = ("tag_constructed", "specs", "defined")
2072 tag_default = tag_encode(3)
2073 asn1_type_name = "BIT STRING"
2086 :param value: set the value. Either binary type, tuple of named
2087 values (if ``schema`` is specified in the class),
2088 string in ``'XXX...'B`` form, or
2089 :py:class:`pyderasn.BitString` object
2090 :param bytes impl: override default tag with ``IMPLICIT`` one
2091 :param bytes expl: override default tag with ``EXPLICIT`` one
2092 :param default: set default value. Type same as in ``value``
2093 :param bool optional: is object ``OPTIONAL`` in sequence
2095 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2096 specs = getattr(self, "schema", {}) if _specs is None else _specs
2097 self.specs = specs if isinstance(specs, dict) else dict(specs)
2098 self._value = None if value is None else self._value_sanitize(value)
2099 if default is not None:
2100 default = self._value_sanitize(default)
2101 self.default = self.__class__(
2107 self._value = default
2109 tag_klass, _, tag_num = tag_decode(self.tag)
2110 self.tag_constructed = tag_encode(
2112 form=TagFormConstructed,
2116 def _bits2octets(self, bits):
2117 if len(self.specs) > 0:
2118 bits = bits.rstrip("0")
2120 bits += "0" * ((8 - (bit_len % 8)) % 8)
2121 octets = bytearray(len(bits) // 8)
2122 for i in six_xrange(len(octets)):
2123 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2124 return bit_len, bytes(octets)
2126 def _value_sanitize(self, value):
2127 if issubclass(value.__class__, BitString):
2129 if isinstance(value, (string_types, binary_type)):
2131 isinstance(value, string_types) and
2132 value.startswith("'")
2134 if value.endswith("'B"):
2136 if not set(value) <= set(("0", "1")):
2137 raise ValueError("B's coding contains unacceptable chars")
2138 return self._bits2octets(value)
2139 elif value.endswith("'H"):
2143 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2145 if isinstance(value, binary_type):
2146 return (len(value) * 8, value)
2148 raise InvalidValueType((self.__class__, string_types, binary_type))
2149 if isinstance(value, tuple):
2152 isinstance(value[0], integer_types) and
2153 isinstance(value[1], binary_type)
2158 bit = self.specs.get(name)
2160 raise ObjUnknown("BitString value: %s" % name)
2163 return self._bits2octets("")
2165 return self._bits2octets("".join(
2166 ("1" if bit in bits else "0")
2167 for bit in six_xrange(max(bits) + 1)
2169 raise InvalidValueType((self.__class__, binary_type, string_types))
2173 return self._value is not None
2176 obj = self.__class__(_specs=self.specs)
2178 if value is not None:
2179 value = (value[0], value[1])
2182 obj._expl = self._expl
2183 obj.default = self.default
2184 obj.optional = self.optional
2185 obj.offset = self.offset
2186 obj.llen = self.llen
2187 obj.vlen = self.vlen
2191 self._assert_ready()
2192 for i in six_xrange(self._value[0]):
2197 self._assert_ready()
2198 return self._value[0]
2200 def __bytes__(self):
2201 self._assert_ready()
2202 return self._value[1]
2204 def __eq__(self, their):
2205 if isinstance(their, bytes):
2206 return self._value[1] == their
2207 if not issubclass(their.__class__, BitString):
2210 self._value == their._value and
2211 self.tag == their.tag and
2212 self._expl == their._expl
2217 return [name for name, bit in self.specs.items() if self[bit]]
2227 return self.__class__(
2229 impl=self.tag if impl is None else impl,
2230 expl=self._expl if expl is None else expl,
2231 default=self.default if default is None else default,
2232 optional=self.optional if optional is None else optional,
2236 def __getitem__(self, key):
2237 if isinstance(key, int):
2238 bit_len, octets = self._value
2242 byte2int(memoryview(octets)[key // 8:]) >>
2245 if isinstance(key, string_types):
2246 value = self.specs.get(key)
2248 raise ObjUnknown("BitString value: %s" % key)
2250 raise InvalidValueType((int, str))
2253 self._assert_ready()
2254 bit_len, octets = self._value
2257 len_encode(len(octets) + 1),
2258 int2byte((8 - bit_len % 8) % 8),
2262 def _decode_chunk(self, lv, offset, decode_path, ctx):
2264 l, llen, v = len_decode(lv)
2265 except DecodeError as err:
2266 raise err.__class__(
2268 klass=self.__class__,
2269 decode_path=decode_path,
2273 raise NotEnoughData(
2274 "encoded length is longer than data",
2275 klass=self.__class__,
2276 decode_path=decode_path,
2280 raise NotEnoughData(
2282 klass=self.__class__,
2283 decode_path=decode_path,
2286 pad_size = byte2int(v)
2287 if l == 1 and pad_size != 0:
2289 "invalid empty value",
2290 klass=self.__class__,
2291 decode_path=decode_path,
2297 klass=self.__class__,
2298 decode_path=decode_path,
2301 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2304 klass=self.__class__,
2305 decode_path=decode_path,
2308 v, tail = v[:l], v[l:]
2309 obj = self.__class__(
2310 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2313 default=self.default,
2314 optional=self.optional,
2316 _decoded=(offset, llen, l),
2320 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2322 t, tlen, lv = tag_strip(tlv)
2323 except DecodeError as err:
2324 raise err.__class__(
2326 klass=self.__class__,
2327 decode_path=decode_path,
2331 if tag_only: # pragma: no cover
2333 return self._decode_chunk(lv, offset, decode_path, ctx)
2334 if t == self.tag_constructed:
2335 if not ctx.get("bered", False):
2337 "unallowed BER constructed encoding",
2338 klass=self.__class__,
2339 decode_path=decode_path,
2342 if tag_only: # pragma: no cover
2346 l, llen, v = len_decode(lv)
2347 except LenIndefForm:
2348 llen, l, v = 1, 0, lv[1:]
2350 except DecodeError as err:
2351 raise err.__class__(
2353 klass=self.__class__,
2354 decode_path=decode_path,
2358 raise NotEnoughData(
2359 "encoded length is longer than data",
2360 klass=self.__class__,
2361 decode_path=decode_path,
2364 if not lenindef and l == 0:
2365 raise NotEnoughData(
2367 klass=self.__class__,
2368 decode_path=decode_path,
2372 sub_offset = offset + tlen + llen
2376 if v[:EOC_LEN].tobytes() == EOC:
2383 "chunk out of bounds",
2384 klass=self.__class__,
2385 decode_path=decode_path + (str(len(chunks) - 1),),
2386 offset=chunks[-1].offset,
2388 sub_decode_path = decode_path + (str(len(chunks)),)
2390 chunk, v_tail = BitString().decode(
2393 decode_path=sub_decode_path,
2396 _ctx_immutable=False,
2400 "expected BitString encoded chunk",
2401 klass=self.__class__,
2402 decode_path=sub_decode_path,
2405 chunks.append(chunk)
2406 sub_offset += chunk.tlvlen
2407 vlen += chunk.tlvlen
2409 if len(chunks) == 0:
2412 klass=self.__class__,
2413 decode_path=decode_path,
2418 for chunk_i, chunk in enumerate(chunks[:-1]):
2419 if chunk.bit_len % 8 != 0:
2421 "BitString chunk is not multiple of 8 bits",
2422 klass=self.__class__,
2423 decode_path=decode_path + (str(chunk_i),),
2424 offset=chunk.offset,
2426 values.append(bytes(chunk))
2427 bit_len += chunk.bit_len
2428 chunk_last = chunks[-1]
2429 values.append(bytes(chunk_last))
2430 bit_len += chunk_last.bit_len
2431 obj = self.__class__(
2432 value=(bit_len, b"".join(values)),
2435 default=self.default,
2436 optional=self.optional,
2438 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2440 obj.lenindef = lenindef
2441 obj.ber_encoded = True
2442 return obj, (v[EOC_LEN:] if lenindef else v)
2444 klass=self.__class__,
2445 decode_path=decode_path,
2450 return pp_console_row(next(self.pps()))
2452 def pps(self, decode_path=()):
2456 bit_len, blob = self._value
2457 value = "%d bits" % bit_len
2458 if len(self.specs) > 0:
2459 blob = tuple(self.named)
2461 asn1_type_name=self.asn1_type_name,
2462 obj_name=self.__class__.__name__,
2463 decode_path=decode_path,
2466 optional=self.optional,
2467 default=self == self.default,
2468 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2469 expl=None if self._expl is None else tag_decode(self._expl),
2474 expl_offset=self.expl_offset if self.expled else None,
2475 expl_tlen=self.expl_tlen if self.expled else None,
2476 expl_llen=self.expl_llen if self.expled else None,
2477 expl_vlen=self.expl_vlen if self.expled else None,
2478 expl_lenindef=self.expl_lenindef,
2479 lenindef=self.lenindef,
2480 ber_encoded=self.ber_encoded,
2483 defined_by, defined = self.defined or (None, None)
2484 if defined_by is not None:
2486 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2488 for pp in self.pps_lenindef(decode_path):
2492 class OctetString(Obj):
2493 """``OCTET STRING`` binary string type
2495 >>> s = OctetString(b"hello world")
2496 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2497 >>> s == OctetString(b"hello world")
2502 >>> OctetString(b"hello", bounds=(4, 4))
2503 Traceback (most recent call last):
2504 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2505 >>> OctetString(b"hell", bounds=(4, 4))
2506 OCTET STRING 4 bytes 68656c6c
2510 Pay attention that OCTET STRING can be encoded both in primitive
2511 and constructed forms. Decoder always checks constructed form tag
2512 additionally to specified primitive one. If BER decoding is
2513 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2514 of DER restrictions.
2516 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2517 tag_default = tag_encode(4)
2518 asn1_type_name = "OCTET STRING"
2531 :param value: set the value. Either binary type, or
2532 :py:class:`pyderasn.OctetString` object
2533 :param bounds: set ``(MIN, MAX)`` value size constraint.
2534 (-inf, +inf) by default
2535 :param bytes impl: override default tag with ``IMPLICIT`` one
2536 :param bytes expl: override default tag with ``EXPLICIT`` one
2537 :param default: set default value. Type same as in ``value``
2538 :param bool optional: is object ``OPTIONAL`` in sequence
2540 super(OctetString, self).__init__(
2548 self._bound_min, self._bound_max = getattr(
2552 ) if bounds is None else bounds
2553 if value is not None:
2554 self._value = self._value_sanitize(value)
2555 if default is not None:
2556 default = self._value_sanitize(default)
2557 self.default = self.__class__(
2562 if self._value is None:
2563 self._value = default
2565 tag_klass, _, tag_num = tag_decode(self.tag)
2566 self.tag_constructed = tag_encode(
2568 form=TagFormConstructed,
2572 def _value_sanitize(self, value):
2573 if issubclass(value.__class__, OctetString):
2574 value = value._value
2575 elif isinstance(value, binary_type):
2578 raise InvalidValueType((self.__class__, bytes))
2579 if not self._bound_min <= len(value) <= self._bound_max:
2580 raise BoundsError(self._bound_min, len(value), self._bound_max)
2585 return self._value is not None
2588 obj = self.__class__()
2589 obj._value = self._value
2590 obj._bound_min = self._bound_min
2591 obj._bound_max = self._bound_max
2593 obj._expl = self._expl
2594 obj.default = self.default
2595 obj.optional = self.optional
2596 obj.offset = self.offset
2597 obj.llen = self.llen
2598 obj.vlen = self.vlen
2601 def __bytes__(self):
2602 self._assert_ready()
2605 def __eq__(self, their):
2606 if isinstance(their, binary_type):
2607 return self._value == their
2608 if not issubclass(their.__class__, OctetString):
2611 self._value == their._value and
2612 self.tag == their.tag and
2613 self._expl == their._expl
2616 def __lt__(self, their):
2617 return self._value < their._value
2628 return self.__class__(
2631 (self._bound_min, self._bound_max)
2632 if bounds is None else bounds
2634 impl=self.tag if impl is None else impl,
2635 expl=self._expl if expl is None else expl,
2636 default=self.default if default is None else default,
2637 optional=self.optional if optional is None else optional,
2641 self._assert_ready()
2644 len_encode(len(self._value)),
2648 def _decode_chunk(self, lv, offset, decode_path, ctx):
2650 l, llen, v = len_decode(lv)
2651 except DecodeError as err:
2652 raise err.__class__(
2654 klass=self.__class__,
2655 decode_path=decode_path,
2659 raise NotEnoughData(
2660 "encoded length is longer than data",
2661 klass=self.__class__,
2662 decode_path=decode_path,
2665 v, tail = v[:l], v[l:]
2667 obj = self.__class__(
2669 bounds=(self._bound_min, self._bound_max),
2672 default=self.default,
2673 optional=self.optional,
2674 _decoded=(offset, llen, l),
2676 except DecodeError as err:
2679 klass=self.__class__,
2680 decode_path=decode_path,
2683 except BoundsError as err:
2686 klass=self.__class__,
2687 decode_path=decode_path,
2692 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2694 t, tlen, lv = tag_strip(tlv)
2695 except DecodeError as err:
2696 raise err.__class__(
2698 klass=self.__class__,
2699 decode_path=decode_path,
2705 return self._decode_chunk(lv, offset, decode_path, ctx)
2706 if t == self.tag_constructed:
2707 if not ctx.get("bered", False):
2709 "unallowed BER constructed encoding",
2710 klass=self.__class__,
2711 decode_path=decode_path,
2718 l, llen, v = len_decode(lv)
2719 except LenIndefForm:
2720 llen, l, v = 1, 0, lv[1:]
2722 except DecodeError as err:
2723 raise err.__class__(
2725 klass=self.__class__,
2726 decode_path=decode_path,
2730 raise NotEnoughData(
2731 "encoded length is longer than data",
2732 klass=self.__class__,
2733 decode_path=decode_path,
2737 sub_offset = offset + tlen + llen
2741 if v[:EOC_LEN].tobytes() == EOC:
2748 "chunk out of bounds",
2749 klass=self.__class__,
2750 decode_path=decode_path + (str(len(chunks) - 1),),
2751 offset=chunks[-1].offset,
2753 sub_decode_path = decode_path + (str(len(chunks)),)
2755 chunk, v_tail = OctetString().decode(
2758 decode_path=sub_decode_path,
2761 _ctx_immutable=False,
2765 "expected OctetString encoded chunk",
2766 klass=self.__class__,
2767 decode_path=sub_decode_path,
2770 chunks.append(chunk)
2771 sub_offset += chunk.tlvlen
2772 vlen += chunk.tlvlen
2775 obj = self.__class__(
2776 value=b"".join(bytes(chunk) for chunk in chunks),
2777 bounds=(self._bound_min, self._bound_max),
2780 default=self.default,
2781 optional=self.optional,
2782 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2784 except DecodeError as err:
2787 klass=self.__class__,
2788 decode_path=decode_path,
2791 except BoundsError as err:
2794 klass=self.__class__,
2795 decode_path=decode_path,
2798 obj.lenindef = lenindef
2799 obj.ber_encoded = True
2800 return obj, (v[EOC_LEN:] if lenindef else v)
2802 klass=self.__class__,
2803 decode_path=decode_path,
2808 return pp_console_row(next(self.pps()))
2810 def pps(self, decode_path=()):
2812 asn1_type_name=self.asn1_type_name,
2813 obj_name=self.__class__.__name__,
2814 decode_path=decode_path,
2815 value=("%d bytes" % len(self._value)) if self.ready else None,
2816 blob=self._value if self.ready else None,
2817 optional=self.optional,
2818 default=self == self.default,
2819 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2820 expl=None if self._expl is None else tag_decode(self._expl),
2825 expl_offset=self.expl_offset if self.expled else None,
2826 expl_tlen=self.expl_tlen if self.expled else None,
2827 expl_llen=self.expl_llen if self.expled else None,
2828 expl_vlen=self.expl_vlen if self.expled else None,
2829 expl_lenindef=self.expl_lenindef,
2830 lenindef=self.lenindef,
2831 ber_encoded=self.ber_encoded,
2834 defined_by, defined = self.defined or (None, None)
2835 if defined_by is not None:
2837 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2839 for pp in self.pps_lenindef(decode_path):
2844 """``NULL`` null object
2852 tag_default = tag_encode(5)
2853 asn1_type_name = "NULL"
2857 value=None, # unused, but Sequence passes it
2864 :param bytes impl: override default tag with ``IMPLICIT`` one
2865 :param bytes expl: override default tag with ``EXPLICIT`` one
2866 :param bool optional: is object ``OPTIONAL`` in sequence
2868 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2876 obj = self.__class__()
2878 obj._expl = self._expl
2879 obj.default = self.default
2880 obj.optional = self.optional
2881 obj.offset = self.offset
2882 obj.llen = self.llen
2883 obj.vlen = self.vlen
2886 def __eq__(self, their):
2887 if not issubclass(their.__class__, Null):
2890 self.tag == their.tag and
2891 self._expl == their._expl
2901 return self.__class__(
2902 impl=self.tag if impl is None else impl,
2903 expl=self._expl if expl is None else expl,
2904 optional=self.optional if optional is None else optional,
2908 return self.tag + len_encode(0)
2910 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2912 t, _, lv = tag_strip(tlv)
2913 except DecodeError as err:
2914 raise err.__class__(
2916 klass=self.__class__,
2917 decode_path=decode_path,
2922 klass=self.__class__,
2923 decode_path=decode_path,
2926 if tag_only: # pragma: no cover
2929 l, _, v = len_decode(lv)
2930 except DecodeError as err:
2931 raise err.__class__(
2933 klass=self.__class__,
2934 decode_path=decode_path,
2938 raise InvalidLength(
2939 "Null must have zero length",
2940 klass=self.__class__,
2941 decode_path=decode_path,
2944 obj = self.__class__(
2947 optional=self.optional,
2948 _decoded=(offset, 1, 0),
2953 return pp_console_row(next(self.pps()))
2955 def pps(self, decode_path=()):
2957 asn1_type_name=self.asn1_type_name,
2958 obj_name=self.__class__.__name__,
2959 decode_path=decode_path,
2960 optional=self.optional,
2961 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2962 expl=None if self._expl is None else tag_decode(self._expl),
2967 expl_offset=self.expl_offset if self.expled else None,
2968 expl_tlen=self.expl_tlen if self.expled else None,
2969 expl_llen=self.expl_llen if self.expled else None,
2970 expl_vlen=self.expl_vlen if self.expled else None,
2971 expl_lenindef=self.expl_lenindef,
2974 for pp in self.pps_lenindef(decode_path):
2978 class ObjectIdentifier(Obj):
2979 """``OBJECT IDENTIFIER`` OID type
2981 >>> oid = ObjectIdentifier((1, 2, 3))
2982 OBJECT IDENTIFIER 1.2.3
2983 >>> oid == ObjectIdentifier("1.2.3")
2989 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2990 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2992 >>> str(ObjectIdentifier((3, 1)))
2993 Traceback (most recent call last):
2994 pyderasn.InvalidOID: unacceptable first arc value
2996 __slots__ = ("defines",)
2997 tag_default = tag_encode(6)
2998 asn1_type_name = "OBJECT IDENTIFIER"
3011 :param value: set the value. Either tuples of integers,
3012 string of "."-concatenated integers, or
3013 :py:class:`pyderasn.ObjectIdentifier` object
3014 :param defines: sequence of tuples. Each tuple has two elements.
3015 First one is relative to current one decode
3016 path, aiming to the field defined by that OID.
3017 Read about relative path in
3018 :py:func:`pyderasn.abs_decode_path`. Second
3019 tuple element is ``{OID: pyderasn.Obj()}``
3020 dictionary, mapping between current OID value
3021 and structure applied to defined field.
3022 :ref:`Read about DEFINED BY <definedby>`
3023 :param bytes impl: override default tag with ``IMPLICIT`` one
3024 :param bytes expl: override default tag with ``EXPLICIT`` one
3025 :param default: set default value. Type same as in ``value``
3026 :param bool optional: is object ``OPTIONAL`` in sequence
3028 super(ObjectIdentifier, self).__init__(
3036 if value is not None:
3037 self._value = self._value_sanitize(value)
3038 if default is not None:
3039 default = self._value_sanitize(default)
3040 self.default = self.__class__(
3045 if self._value is None:
3046 self._value = default
3047 self.defines = defines
3049 def __add__(self, their):
3050 if isinstance(their, self.__class__):
3051 return self.__class__(self._value + their._value)
3052 if isinstance(their, tuple):
3053 return self.__class__(self._value + their)
3054 raise InvalidValueType((self.__class__, tuple))
3056 def _value_sanitize(self, value):
3057 if issubclass(value.__class__, ObjectIdentifier):
3059 if isinstance(value, string_types):
3061 value = tuple(int(arc) for arc in value.split("."))
3063 raise InvalidOID("unacceptable arcs values")
3064 if isinstance(value, tuple):
3066 raise InvalidOID("less than 2 arcs")
3067 first_arc = value[0]
3068 if first_arc in (0, 1):
3069 if not (0 <= value[1] <= 39):
3070 raise InvalidOID("second arc is too wide")
3071 elif first_arc == 2:
3074 raise InvalidOID("unacceptable first arc value")
3076 raise InvalidValueType((self.__class__, str, tuple))
3080 return self._value is not None
3083 obj = self.__class__()
3084 obj._value = self._value
3085 obj.defines = self.defines
3087 obj._expl = self._expl
3088 obj.default = self.default
3089 obj.optional = self.optional
3090 obj.offset = self.offset
3091 obj.llen = self.llen
3092 obj.vlen = self.vlen
3096 self._assert_ready()
3097 return iter(self._value)
3100 return ".".join(str(arc) for arc in self._value or ())
3103 self._assert_ready()
3106 bytes(self._expl or b"") +
3107 str(self._value).encode("ascii"),
3110 def __eq__(self, their):
3111 if isinstance(their, tuple):
3112 return self._value == their
3113 if not issubclass(their.__class__, ObjectIdentifier):
3116 self.tag == their.tag and
3117 self._expl == their._expl and
3118 self._value == their._value
3121 def __lt__(self, their):
3122 return self._value < their._value
3133 return self.__class__(
3135 defines=self.defines if defines is None else defines,
3136 impl=self.tag if impl is None else impl,
3137 expl=self._expl if expl is None else expl,
3138 default=self.default if default is None else default,
3139 optional=self.optional if optional is None else optional,
3143 self._assert_ready()
3145 first_value = value[1]
3146 first_arc = value[0]
3149 elif first_arc == 1:
3151 elif first_arc == 2:
3153 else: # pragma: no cover
3154 raise RuntimeError("invalid arc is stored")
3155 octets = [zero_ended_encode(first_value)]
3156 for arc in value[2:]:
3157 octets.append(zero_ended_encode(arc))
3158 v = b"".join(octets)
3159 return b"".join((self.tag, len_encode(len(v)), v))
3161 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3163 t, _, lv = tag_strip(tlv)
3164 except DecodeError as err:
3165 raise err.__class__(
3167 klass=self.__class__,
3168 decode_path=decode_path,
3173 klass=self.__class__,
3174 decode_path=decode_path,
3177 if tag_only: # pragma: no cover
3180 l, llen, v = len_decode(lv)
3181 except DecodeError as err:
3182 raise err.__class__(
3184 klass=self.__class__,
3185 decode_path=decode_path,
3189 raise NotEnoughData(
3190 "encoded length is longer than data",
3191 klass=self.__class__,
3192 decode_path=decode_path,
3196 raise NotEnoughData(
3198 klass=self.__class__,
3199 decode_path=decode_path,
3202 v, tail = v[:l], v[l:]
3208 octet = indexbytes(v, i)
3209 arc = (arc << 7) | (octet & 0x7F)
3210 if octet & 0x80 == 0:
3218 klass=self.__class__,
3219 decode_path=decode_path,
3223 second_arc = arcs[0]
3224 if 0 <= second_arc <= 39:
3226 elif 40 <= second_arc <= 79:
3232 obj = self.__class__(
3233 value=tuple([first_arc, second_arc] + arcs[1:]),
3236 default=self.default,
3237 optional=self.optional,
3238 _decoded=(offset, llen, l),
3243 return pp_console_row(next(self.pps()))
3245 def pps(self, decode_path=()):
3247 asn1_type_name=self.asn1_type_name,
3248 obj_name=self.__class__.__name__,
3249 decode_path=decode_path,
3250 value=str(self) if self.ready else None,
3251 optional=self.optional,
3252 default=self == self.default,
3253 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3254 expl=None if self._expl is None else tag_decode(self._expl),
3259 expl_offset=self.expl_offset if self.expled else None,
3260 expl_tlen=self.expl_tlen if self.expled else None,
3261 expl_llen=self.expl_llen if self.expled else None,
3262 expl_vlen=self.expl_vlen if self.expled else None,
3263 expl_lenindef=self.expl_lenindef,
3266 for pp in self.pps_lenindef(decode_path):
3270 class Enumerated(Integer):
3271 """``ENUMERATED`` integer type
3273 This type is identical to :py:class:`pyderasn.Integer`, but requires
3274 schema to be specified and does not accept values missing from it.
3277 tag_default = tag_encode(10)
3278 asn1_type_name = "ENUMERATED"
3289 bounds=None, # dummy argument, workability for Integer.decode
3291 super(Enumerated, self).__init__(
3300 if len(self.specs) == 0:
3301 raise ValueError("schema must be specified")
3303 def _value_sanitize(self, value):
3304 if isinstance(value, self.__class__):
3305 value = value._value
3306 elif isinstance(value, integer_types):
3307 if value not in list(self.specs.values()):
3309 "unknown integer value: %s" % value,
3310 klass=self.__class__,
3312 elif isinstance(value, string_types):
3313 value = self.specs.get(value)
3315 raise ObjUnknown("integer value: %s" % value)
3317 raise InvalidValueType((self.__class__, int, str))
3321 obj = self.__class__(_specs=self.specs)
3322 obj._value = self._value
3323 obj._bound_min = self._bound_min
3324 obj._bound_max = self._bound_max
3326 obj._expl = self._expl
3327 obj.default = self.default
3328 obj.optional = self.optional
3329 obj.offset = self.offset
3330 obj.llen = self.llen
3331 obj.vlen = self.vlen
3343 return self.__class__(
3345 impl=self.tag if impl is None else impl,
3346 expl=self._expl if expl is None else expl,
3347 default=self.default if default is None else default,
3348 optional=self.optional if optional is None else optional,
3353 class CommonString(OctetString):
3354 """Common class for all strings
3356 Everything resembles :py:class:`pyderasn.OctetString`, except
3357 ability to deal with unicode text strings.
3359 >>> hexenc("привет мир".encode("utf-8"))
3360 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3361 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3363 >>> s = UTF8String("привет мир")
3364 UTF8String UTF8String привет мир
3366 'привет мир'
3367 >>> hexenc(bytes(s))
3368 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3370 >>> PrintableString("привет мир")
3371 Traceback (most recent call last):
3372 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3374 >>> BMPString("ада", bounds=(2, 2))
3375 Traceback (most recent call last):
3376 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3377 >>> s = BMPString("ад", bounds=(2, 2))
3380 >>> hexenc(bytes(s))
3388 * - :py:class:`pyderasn.UTF8String`
3390 * - :py:class:`pyderasn.NumericString`
3392 * - :py:class:`pyderasn.PrintableString`
3394 * - :py:class:`pyderasn.TeletexString`
3396 * - :py:class:`pyderasn.T61String`
3398 * - :py:class:`pyderasn.VideotexString`
3400 * - :py:class:`pyderasn.IA5String`
3402 * - :py:class:`pyderasn.GraphicString`
3404 * - :py:class:`pyderasn.VisibleString`
3406 * - :py:class:`pyderasn.ISO646String`
3408 * - :py:class:`pyderasn.GeneralString`
3410 * - :py:class:`pyderasn.UniversalString`
3412 * - :py:class:`pyderasn.BMPString`
3415 __slots__ = ("encoding",)
3417 def _value_sanitize(self, value):
3419 value_decoded = None
3420 if isinstance(value, self.__class__):
3421 value_raw = value._value
3422 elif isinstance(value, text_type):
3423 value_decoded = value
3424 elif isinstance(value, binary_type):
3427 raise InvalidValueType((self.__class__, text_type, binary_type))
3430 value_decoded.encode(self.encoding)
3431 if value_raw is None else value_raw
3434 value_raw.decode(self.encoding)
3435 if value_decoded is None else value_decoded
3437 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3438 raise DecodeError(str(err))
3439 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3447 def __eq__(self, their):
3448 if isinstance(their, binary_type):
3449 return self._value == their
3450 if isinstance(their, text_type):
3451 return self._value == their.encode(self.encoding)
3452 if not isinstance(their, self.__class__):
3455 self._value == their._value and
3456 self.tag == their.tag and
3457 self._expl == their._expl
3460 def __unicode__(self):
3462 return self._value.decode(self.encoding)
3463 return text_type(self._value)
3466 return pp_console_row(next(self.pps(no_unicode=PY2)))
3468 def pps(self, decode_path=(), no_unicode=False):
3471 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3473 asn1_type_name=self.asn1_type_name,
3474 obj_name=self.__class__.__name__,
3475 decode_path=decode_path,
3477 optional=self.optional,
3478 default=self == self.default,
3479 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3480 expl=None if self._expl is None else tag_decode(self._expl),
3485 expl_offset=self.expl_offset if self.expled else None,
3486 expl_tlen=self.expl_tlen if self.expled else None,
3487 expl_llen=self.expl_llen if self.expled else None,
3488 expl_vlen=self.expl_vlen if self.expled else None,
3489 expl_lenindef=self.expl_lenindef,
3490 ber_encoded=self.ber_encoded,
3493 for pp in self.pps_lenindef(decode_path):
3497 class UTF8String(CommonString):
3499 tag_default = tag_encode(12)
3501 asn1_type_name = "UTF8String"
3504 class AllowableCharsMixin(object):
3506 def allowable_chars(self):
3508 return self._allowable_chars
3509 return set(six_unichr(c) for c in self._allowable_chars)
3512 class NumericString(AllowableCharsMixin, CommonString):
3515 Its value is properly sanitized: only ASCII digits with spaces can
3518 >>> NumericString().allowable_chars
3519 set(['3', '4', '7', '5', '1', '0', '8', '9', ' ', '6', '2'])
3522 tag_default = tag_encode(18)
3524 asn1_type_name = "NumericString"
3525 _allowable_chars = set(digits.encode("ascii") + b" ")
3527 def _value_sanitize(self, value):
3528 value = super(NumericString, self)._value_sanitize(value)
3529 if not set(value) <= self._allowable_chars:
3530 raise DecodeError("non-numeric value")
3534 class PrintableString(AllowableCharsMixin, CommonString):
3537 Its value is properly sanitized: see X.680 41.4 table 10.
3539 >>> PrintableString().allowable_chars
3540 >>> set([' ', "'", ..., 'z'])
3543 tag_default = tag_encode(19)
3545 asn1_type_name = "PrintableString"
3546 _allowable_chars = set(
3547 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3550 def _value_sanitize(self, value):
3551 value = super(PrintableString, self)._value_sanitize(value)
3552 if not set(value) <= self._allowable_chars:
3553 raise DecodeError("non-printable value")
3557 class TeletexString(CommonString):
3559 tag_default = tag_encode(20)
3561 asn1_type_name = "TeletexString"
3564 class T61String(TeletexString):
3566 asn1_type_name = "T61String"
3569 class VideotexString(CommonString):
3571 tag_default = tag_encode(21)
3572 encoding = "iso-8859-1"
3573 asn1_type_name = "VideotexString"
3576 class IA5String(CommonString):
3578 tag_default = tag_encode(22)
3580 asn1_type_name = "IA5"
3583 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3584 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3585 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3588 class UTCTime(CommonString):
3589 """``UTCTime`` datetime type
3591 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3592 UTCTime UTCTime 2017-09-30T22:07:50
3598 datetime.datetime(2017, 9, 30, 22, 7, 50)
3599 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3600 datetime.datetime(1957, 9, 30, 22, 7, 50)
3603 tag_default = tag_encode(23)
3605 asn1_type_name = "UTCTime"
3607 fmt = "%y%m%d%H%M%SZ"
3617 bounds=None, # dummy argument, workability for OctetString.decode
3620 :param value: set the value. Either datetime type, or
3621 :py:class:`pyderasn.UTCTime` object
3622 :param bytes impl: override default tag with ``IMPLICIT`` one
3623 :param bytes expl: override default tag with ``EXPLICIT`` one
3624 :param default: set default value. Type same as in ``value``
3625 :param bool optional: is object ``OPTIONAL`` in sequence
3627 super(UTCTime, self).__init__(
3635 if value is not None:
3636 self._value = self._value_sanitize(value)
3637 if default is not None:
3638 default = self._value_sanitize(default)
3639 self.default = self.__class__(
3644 if self._value is None:
3645 self._value = default
3647 def _value_sanitize(self, value):
3648 if isinstance(value, self.__class__):
3650 if isinstance(value, datetime):
3651 return value.strftime(self.fmt).encode("ascii")
3652 if isinstance(value, binary_type):
3654 value_decoded = value.decode("ascii")
3655 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3656 raise DecodeError("invalid UTCTime encoding")
3657 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3659 datetime.strptime(value_decoded, self.fmt)
3660 except (TypeError, ValueError):
3661 raise DecodeError("invalid UTCTime format")
3664 raise DecodeError("invalid UTCTime length")
3665 raise InvalidValueType((self.__class__, datetime))
3667 def __eq__(self, their):
3668 if isinstance(their, binary_type):
3669 return self._value == their
3670 if isinstance(their, datetime):
3671 return self.todatetime() == their
3672 if not isinstance(their, self.__class__):
3675 self._value == their._value and
3676 self.tag == their.tag and
3677 self._expl == their._expl
3680 def todatetime(self):
3681 """Convert to datetime
3685 Pay attention that UTCTime can not hold full year, so all years
3686 having < 50 years are treated as 20xx, 19xx otherwise, according
3687 to X.509 recomendation.
3689 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3690 year = value.year % 100
3692 year=(2000 + year) if year < 50 else (1900 + year),
3696 minute=value.minute,
3697 second=value.second,
3701 return pp_console_row(next(self.pps()))
3703 def pps(self, decode_path=()):
3705 asn1_type_name=self.asn1_type_name,
3706 obj_name=self.__class__.__name__,
3707 decode_path=decode_path,
3708 value=self.todatetime().isoformat() if self.ready else None,
3709 optional=self.optional,
3710 default=self == self.default,
3711 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3712 expl=None if self._expl is None else tag_decode(self._expl),
3717 expl_offset=self.expl_offset if self.expled else None,
3718 expl_tlen=self.expl_tlen if self.expled else None,
3719 expl_llen=self.expl_llen if self.expled else None,
3720 expl_vlen=self.expl_vlen if self.expled else None,
3721 expl_lenindef=self.expl_lenindef,
3722 ber_encoded=self.ber_encoded,
3725 for pp in self.pps_lenindef(decode_path):
3729 class GeneralizedTime(UTCTime):
3730 """``GeneralizedTime`` datetime type
3732 This type is similar to :py:class:`pyderasn.UTCTime`.
3734 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3735 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3737 '20170930220750.000123Z'
3738 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3739 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3742 tag_default = tag_encode(24)
3743 asn1_type_name = "GeneralizedTime"
3745 fmt = "%Y%m%d%H%M%SZ"
3746 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3748 def _value_sanitize(self, value):
3749 if isinstance(value, self.__class__):
3751 if isinstance(value, datetime):
3752 return value.strftime(
3753 self.fmt_ms if value.microsecond > 0 else self.fmt
3755 if isinstance(value, binary_type):
3757 value_decoded = value.decode("ascii")
3758 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3759 raise DecodeError("invalid GeneralizedTime encoding")
3760 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3762 datetime.strptime(value_decoded, self.fmt)
3763 except (TypeError, ValueError):
3765 "invalid GeneralizedTime (without ms) format",
3768 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3770 datetime.strptime(value_decoded, self.fmt_ms)
3771 except (TypeError, ValueError):
3773 "invalid GeneralizedTime (with ms) format",
3778 "invalid GeneralizedTime length",
3779 klass=self.__class__,
3781 raise InvalidValueType((self.__class__, datetime))
3783 def todatetime(self):
3784 value = self._value.decode("ascii")
3785 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3786 return datetime.strptime(value, self.fmt)
3787 return datetime.strptime(value, self.fmt_ms)
3790 class GraphicString(CommonString):
3792 tag_default = tag_encode(25)
3793 encoding = "iso-8859-1"
3794 asn1_type_name = "GraphicString"
3797 class VisibleString(CommonString):
3799 tag_default = tag_encode(26)
3801 asn1_type_name = "VisibleString"
3804 class ISO646String(VisibleString):
3806 asn1_type_name = "ISO646String"
3809 class GeneralString(CommonString):
3811 tag_default = tag_encode(27)
3812 encoding = "iso-8859-1"
3813 asn1_type_name = "GeneralString"
3816 class UniversalString(CommonString):
3818 tag_default = tag_encode(28)
3819 encoding = "utf-32-be"
3820 asn1_type_name = "UniversalString"
3823 class BMPString(CommonString):
3825 tag_default = tag_encode(30)
3826 encoding = "utf-16-be"
3827 asn1_type_name = "BMPString"
3831 """``CHOICE`` special type
3835 class GeneralName(Choice):
3837 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3838 ("dNSName", IA5String(impl=tag_ctxp(2))),
3841 >>> gn = GeneralName()
3843 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3844 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3845 >>> gn["dNSName"] = IA5String("bar.baz")
3846 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3847 >>> gn["rfc822Name"]
3850 [2] IA5String IA5 bar.baz
3853 >>> gn.value == gn["dNSName"]
3856 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3858 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3859 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3861 __slots__ = ("specs",)
3863 asn1_type_name = "CHOICE"
3876 :param value: set the value. Either ``(choice, value)`` tuple, or
3877 :py:class:`pyderasn.Choice` object
3878 :param bytes impl: can not be set, do **not** use it
3879 :param bytes expl: override default tag with ``EXPLICIT`` one
3880 :param default: set default value. Type same as in ``value``
3881 :param bool optional: is object ``OPTIONAL`` in sequence
3883 if impl is not None:
3884 raise ValueError("no implicit tag allowed for CHOICE")
3885 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3887 schema = getattr(self, "schema", ())
3888 if len(schema) == 0:
3889 raise ValueError("schema must be specified")
3891 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3894 if value is not None:
3895 self._value = self._value_sanitize(value)
3896 if default is not None:
3897 default_value = self._value_sanitize(default)
3898 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3899 default_obj.specs = self.specs
3900 default_obj._value = default_value
3901 self.default = default_obj
3903 self._value = default_obj.copy()._value
3905 def _value_sanitize(self, value):
3906 if isinstance(value, self.__class__):
3908 if isinstance(value, tuple) and len(value) == 2:
3910 spec = self.specs.get(choice)
3912 raise ObjUnknown(choice)
3913 if not isinstance(obj, spec.__class__):
3914 raise InvalidValueType((spec,))
3915 return (choice, spec(obj))
3916 raise InvalidValueType((self.__class__, tuple))
3920 return self._value is not None and self._value[1].ready
3924 return self.expl_lenindef or (
3925 (self._value is not None) and
3926 self._value[1].bered
3930 obj = self.__class__(schema=self.specs)
3931 obj._expl = self._expl
3932 obj.default = self.default
3933 obj.optional = self.optional
3934 obj.offset = self.offset
3935 obj.llen = self.llen
3936 obj.vlen = self.vlen
3938 if value is not None:
3939 obj._value = (value[0], value[1].copy())
3942 def __eq__(self, their):
3943 if isinstance(their, tuple) and len(their) == 2:
3944 return self._value == their
3945 if not isinstance(their, self.__class__):
3948 self.specs == their.specs and
3949 self._value == their._value
3959 return self.__class__(
3962 expl=self._expl if expl is None else expl,
3963 default=self.default if default is None else default,
3964 optional=self.optional if optional is None else optional,
3969 self._assert_ready()
3970 return self._value[0]
3974 self._assert_ready()
3975 return self._value[1]
3977 def __getitem__(self, key):
3978 if key not in self.specs:
3979 raise ObjUnknown(key)
3980 if self._value is None:
3982 choice, value = self._value
3987 def __setitem__(self, key, value):
3988 spec = self.specs.get(key)
3990 raise ObjUnknown(key)
3991 if not isinstance(value, spec.__class__):
3992 raise InvalidValueType((spec.__class__,))
3993 self._value = (key, spec(value))
4001 return self._value[1].decoded if self.ready else False
4004 self._assert_ready()
4005 return self._value[1].encode()
4007 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4008 for choice, spec in self.specs.items():
4009 sub_decode_path = decode_path + (choice,)
4015 decode_path=sub_decode_path,
4018 _ctx_immutable=False,
4025 klass=self.__class__,
4026 decode_path=decode_path,
4029 if tag_only: # pragma: no cover
4031 value, tail = spec.decode(
4035 decode_path=sub_decode_path,
4037 _ctx_immutable=False,
4039 obj = self.__class__(
4042 default=self.default,
4043 optional=self.optional,
4044 _decoded=(offset, 0, value.fulllen),
4046 obj._value = (choice, value)
4050 value = pp_console_row(next(self.pps()))
4052 value = "%s[%r]" % (value, self.value)
4055 def pps(self, decode_path=()):
4057 asn1_type_name=self.asn1_type_name,
4058 obj_name=self.__class__.__name__,
4059 decode_path=decode_path,
4060 value=self.choice if self.ready else None,
4061 optional=self.optional,
4062 default=self == self.default,
4063 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4064 expl=None if self._expl is None else tag_decode(self._expl),
4069 expl_lenindef=self.expl_lenindef,
4073 yield self.value.pps(decode_path=decode_path + (self.choice,))
4074 for pp in self.pps_lenindef(decode_path):
4078 class PrimitiveTypes(Choice):
4079 """Predefined ``CHOICE`` for all generic primitive types
4081 It could be useful for general decoding of some unspecified values:
4083 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4084 OCTET STRING 3 bytes 666f6f
4085 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4089 schema = tuple((klass.__name__, klass()) for klass in (
4114 """``ANY`` special type
4116 >>> Any(Integer(-123))
4118 >>> a = Any(OctetString(b"hello world").encode())
4119 ANY 040b68656c6c6f20776f726c64
4120 >>> hexenc(bytes(a))
4121 b'0x040x0bhello world'
4123 __slots__ = ("defined",)
4124 tag_default = tag_encode(0)
4125 asn1_type_name = "ANY"
4135 :param value: set the value. Either any kind of pyderasn's
4136 **ready** object, or bytes. Pay attention that
4137 **no** validation is performed is raw binary value
4139 :param bytes expl: override default tag with ``EXPLICIT`` one
4140 :param bool optional: is object ``OPTIONAL`` in sequence
4142 super(Any, self).__init__(None, expl, None, optional, _decoded)
4143 self._value = None if value is None else self._value_sanitize(value)
4146 def _value_sanitize(self, value):
4147 if isinstance(value, self.__class__):
4149 if isinstance(value, Obj):
4150 return value.encode()
4151 if isinstance(value, binary_type):
4153 raise InvalidValueType((self.__class__, Obj, binary_type))
4157 return self._value is not None
4161 if self.expl_lenindef or self.lenindef:
4163 if self.defined is None:
4165 return self.defined[1].bered
4168 obj = self.__class__()
4169 obj._value = self._value
4171 obj._expl = self._expl
4172 obj.optional = self.optional
4173 obj.offset = self.offset
4174 obj.llen = self.llen
4175 obj.vlen = self.vlen
4178 def __eq__(self, their):
4179 if isinstance(their, binary_type):
4180 return self._value == their
4181 if issubclass(their.__class__, Any):
4182 return self._value == their._value
4191 return self.__class__(
4193 expl=self._expl if expl is None else expl,
4194 optional=self.optional if optional is None else optional,
4197 def __bytes__(self):
4198 self._assert_ready()
4206 self._assert_ready()
4209 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4211 t, tlen, lv = tag_strip(tlv)
4212 except DecodeError as err:
4213 raise err.__class__(
4215 klass=self.__class__,
4216 decode_path=decode_path,
4220 l, llen, v = len_decode(lv)
4221 except LenIndefForm as err:
4222 if not ctx.get("bered", False):
4223 raise err.__class__(
4225 klass=self.__class__,
4226 decode_path=decode_path,
4229 llen, vlen, v = 1, 0, lv[1:]
4230 sub_offset = offset + tlen + llen
4232 while v[:EOC_LEN].tobytes() != EOC:
4233 chunk, v = Any().decode(
4236 decode_path=decode_path + (str(chunk_i),),
4239 _ctx_immutable=False,
4241 vlen += chunk.tlvlen
4242 sub_offset += chunk.tlvlen
4244 tlvlen = tlen + llen + vlen + EOC_LEN
4245 obj = self.__class__(
4246 value=tlv[:tlvlen].tobytes(),
4248 optional=self.optional,
4249 _decoded=(offset, 0, tlvlen),
4253 return obj, v[EOC_LEN:]
4254 except DecodeError as err:
4255 raise err.__class__(
4257 klass=self.__class__,
4258 decode_path=decode_path,
4262 raise NotEnoughData(
4263 "encoded length is longer than data",
4264 klass=self.__class__,
4265 decode_path=decode_path,
4268 tlvlen = tlen + llen + l
4269 v, tail = tlv[:tlvlen], v[l:]
4270 obj = self.__class__(
4273 optional=self.optional,
4274 _decoded=(offset, 0, tlvlen),
4280 return pp_console_row(next(self.pps()))
4282 def pps(self, decode_path=()):
4284 asn1_type_name=self.asn1_type_name,
4285 obj_name=self.__class__.__name__,
4286 decode_path=decode_path,
4287 blob=self._value if self.ready else None,
4288 optional=self.optional,
4289 default=self == self.default,
4290 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4291 expl=None if self._expl is None else tag_decode(self._expl),
4296 expl_offset=self.expl_offset if self.expled else None,
4297 expl_tlen=self.expl_tlen if self.expled else None,
4298 expl_llen=self.expl_llen if self.expled else None,
4299 expl_vlen=self.expl_vlen if self.expled else None,
4300 expl_lenindef=self.expl_lenindef,
4301 lenindef=self.lenindef,
4304 defined_by, defined = self.defined or (None, None)
4305 if defined_by is not None:
4307 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4309 for pp in self.pps_lenindef(decode_path):
4313 ########################################################################
4314 # ASN.1 constructed types
4315 ########################################################################
4317 def get_def_by_path(defines_by_path, sub_decode_path):
4318 """Get define by decode path
4320 for path, define in defines_by_path:
4321 if len(path) != len(sub_decode_path):
4323 for p1, p2 in zip(path, sub_decode_path):
4324 if (p1 != any) and (p1 != p2):
4330 def abs_decode_path(decode_path, rel_path):
4331 """Create an absolute decode path from current and relative ones
4333 :param decode_path: current decode path, starting point. Tuple of strings
4334 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4335 If first tuple's element is "/", then treat it as
4336 an absolute path, ignoring ``decode_path`` as
4337 starting point. Also this tuple can contain ".."
4338 elements, stripping the leading element from
4341 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4342 ("foo", "bar", "baz", "whatever")
4343 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4345 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4348 if rel_path[0] == "/":
4350 if rel_path[0] == "..":
4351 return abs_decode_path(decode_path[:-1], rel_path[1:])
4352 return decode_path + rel_path
4355 class Sequence(Obj):
4356 """``SEQUENCE`` structure type
4358 You have to make specification of sequence::
4360 class Extension(Sequence):
4362 ("extnID", ObjectIdentifier()),
4363 ("critical", Boolean(default=False)),
4364 ("extnValue", OctetString()),
4367 Then, you can work with it as with dictionary.
4369 >>> ext = Extension()
4370 >>> Extension().specs
4372 ('extnID', OBJECT IDENTIFIER),
4373 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4374 ('extnValue', OCTET STRING),
4376 >>> ext["extnID"] = "1.2.3"
4377 Traceback (most recent call last):
4378 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4379 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4381 You can determine if sequence is ready to be encoded:
4386 Traceback (most recent call last):
4387 pyderasn.ObjNotReady: object is not ready: extnValue
4388 >>> ext["extnValue"] = OctetString(b"foobar")
4392 Value you want to assign, must have the same **type** as in
4393 corresponding specification, but it can have different tags,
4394 optional/default attributes -- they will be taken from specification
4397 class TBSCertificate(Sequence):
4399 ("version", Version(expl=tag_ctxc(0), default="v1")),
4402 >>> tbs = TBSCertificate()
4403 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4405 Assign ``None`` to remove value from sequence.
4407 You can set values in Sequence during its initialization:
4409 >>> AlgorithmIdentifier((
4410 ("algorithm", ObjectIdentifier("1.2.3")),
4411 ("parameters", Any(Null()))
4413 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4415 You can determine if value exists/set in the sequence and take its value:
4417 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4420 OBJECT IDENTIFIER 1.2.3
4422 But pay attention that if value has default, then it won't be (not
4423 in) in the sequence (because ``DEFAULT`` must not be encoded in
4424 DER), but you can read its value:
4426 >>> "critical" in ext, ext["critical"]
4427 (False, BOOLEAN False)
4428 >>> ext["critical"] = Boolean(True)
4429 >>> "critical" in ext, ext["critical"]
4430 (True, BOOLEAN True)
4432 All defaulted values are always optional.
4434 .. _allow_default_values_ctx:
4436 DER prohibits default value encoding and will raise an error if
4437 default value is unexpectedly met during decode.
4438 If :ref:`bered <bered_ctx>` context option is set, then no error
4439 will be raised, but ``bered`` attribute set. You can disable strict
4440 defaulted values existence validation by setting
4441 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4443 Two sequences are equal if they have equal specification (schema),
4444 implicit/explicit tagging and the same values.
4446 __slots__ = ("specs",)
4447 tag_default = tag_encode(form=TagFormConstructed, num=16)
4448 asn1_type_name = "SEQUENCE"
4460 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4462 schema = getattr(self, "schema", ())
4464 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4467 if value is not None:
4468 if issubclass(value.__class__, Sequence):
4469 self._value = value._value
4470 elif hasattr(value, "__iter__"):
4471 for seq_key, seq_value in value:
4472 self[seq_key] = seq_value
4474 raise InvalidValueType((Sequence,))
4475 if default is not None:
4476 if not issubclass(default.__class__, Sequence):
4477 raise InvalidValueType((Sequence,))
4478 default_value = default._value
4479 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4480 default_obj.specs = self.specs
4481 default_obj._value = default_value
4482 self.default = default_obj
4484 self._value = default_obj.copy()._value
4488 for name, spec in self.specs.items():
4489 value = self._value.get(name)
4501 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4503 return any(value.bered for value in self._value.values())
4506 obj = self.__class__(schema=self.specs)
4508 obj._expl = self._expl
4509 obj.default = self.default
4510 obj.optional = self.optional
4511 obj.offset = self.offset
4512 obj.llen = self.llen
4513 obj.vlen = self.vlen
4514 obj._value = {k: v.copy() for k, v in self._value.items()}
4517 def __eq__(self, their):
4518 if not isinstance(their, self.__class__):
4521 self.specs == their.specs and
4522 self.tag == their.tag and
4523 self._expl == their._expl and
4524 self._value == their._value
4535 return self.__class__(
4538 impl=self.tag if impl is None else impl,
4539 expl=self._expl if expl is None else expl,
4540 default=self.default if default is None else default,
4541 optional=self.optional if optional is None else optional,
4544 def __contains__(self, key):
4545 return key in self._value
4547 def __setitem__(self, key, value):
4548 spec = self.specs.get(key)
4550 raise ObjUnknown(key)
4552 self._value.pop(key, None)
4554 if not isinstance(value, spec.__class__):
4555 raise InvalidValueType((spec.__class__,))
4556 value = spec(value=value)
4557 if spec.default is not None and value == spec.default:
4558 self._value.pop(key, None)
4560 self._value[key] = value
4562 def __getitem__(self, key):
4563 value = self._value.get(key)
4564 if value is not None:
4566 spec = self.specs.get(key)
4568 raise ObjUnknown(key)
4569 if spec.default is not None:
4573 def _encoded_values(self):
4575 for name, spec in self.specs.items():
4576 value = self._value.get(name)
4580 raise ObjNotReady(name)
4581 raws.append(value.encode())
4585 v = b"".join(self._encoded_values())
4586 return b"".join((self.tag, len_encode(len(v)), v))
4588 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4590 t, tlen, lv = tag_strip(tlv)
4591 except DecodeError as err:
4592 raise err.__class__(
4594 klass=self.__class__,
4595 decode_path=decode_path,
4600 klass=self.__class__,
4601 decode_path=decode_path,
4604 if tag_only: # pragma: no cover
4607 ctx_bered = ctx.get("bered", False)
4609 l, llen, v = len_decode(lv)
4610 except LenIndefForm as err:
4612 raise err.__class__(
4614 klass=self.__class__,
4615 decode_path=decode_path,
4618 l, llen, v = 0, 1, lv[1:]
4620 except DecodeError as err:
4621 raise err.__class__(
4623 klass=self.__class__,
4624 decode_path=decode_path,
4628 raise NotEnoughData(
4629 "encoded length is longer than data",
4630 klass=self.__class__,
4631 decode_path=decode_path,
4635 v, tail = v[:l], v[l:]
4637 sub_offset = offset + tlen + llen
4640 ctx_allow_default_values = ctx.get("allow_default_values", False)
4641 for name, spec in self.specs.items():
4642 if spec.optional and (
4643 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4647 sub_decode_path = decode_path + (name,)
4649 value, v_tail = spec.decode(
4653 decode_path=sub_decode_path,
4655 _ctx_immutable=False,
4662 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4663 if defined is not None:
4664 defined_by, defined_spec = defined
4665 if issubclass(value.__class__, SequenceOf):
4666 for i, _value in enumerate(value):
4667 sub_sub_decode_path = sub_decode_path + (
4669 DecodePathDefBy(defined_by),
4671 defined_value, defined_tail = defined_spec.decode(
4672 memoryview(bytes(_value)),
4674 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4675 if value.expled else (value.tlen + value.llen)
4678 decode_path=sub_sub_decode_path,
4680 _ctx_immutable=False,
4682 if len(defined_tail) > 0:
4685 klass=self.__class__,
4686 decode_path=sub_sub_decode_path,
4689 _value.defined = (defined_by, defined_value)
4691 defined_value, defined_tail = defined_spec.decode(
4692 memoryview(bytes(value)),
4694 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4695 if value.expled else (value.tlen + value.llen)
4698 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4700 _ctx_immutable=False,
4702 if len(defined_tail) > 0:
4705 klass=self.__class__,
4706 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4709 value.defined = (defined_by, defined_value)
4711 value_len = value.fulllen
4713 sub_offset += value_len
4715 if spec.default is not None and value == spec.default:
4716 if ctx_bered or ctx_allow_default_values:
4720 "DEFAULT value met",
4721 klass=self.__class__,
4722 decode_path=sub_decode_path,
4725 values[name] = value
4727 spec_defines = getattr(spec, "defines", ())
4728 if len(spec_defines) == 0:
4729 defines_by_path = ctx.get("defines_by_path", ())
4730 if len(defines_by_path) > 0:
4731 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4732 if spec_defines is not None and len(spec_defines) > 0:
4733 for rel_path, schema in spec_defines:
4734 defined = schema.get(value, None)
4735 if defined is not None:
4736 ctx.setdefault("_defines", []).append((
4737 abs_decode_path(sub_decode_path[:-1], rel_path),
4741 if v[:EOC_LEN].tobytes() != EOC:
4744 klass=self.__class__,
4745 decode_path=decode_path,
4753 klass=self.__class__,
4754 decode_path=decode_path,
4757 obj = self.__class__(
4761 default=self.default,
4762 optional=self.optional,
4763 _decoded=(offset, llen, vlen),
4766 obj.lenindef = lenindef
4767 obj.ber_encoded = ber_encoded
4771 value = pp_console_row(next(self.pps()))
4773 for name in self.specs:
4774 _value = self._value.get(name)
4777 cols.append("%s: %s" % (name, repr(_value)))
4778 return "%s[%s]" % (value, "; ".join(cols))
4780 def pps(self, decode_path=()):
4782 asn1_type_name=self.asn1_type_name,
4783 obj_name=self.__class__.__name__,
4784 decode_path=decode_path,
4785 optional=self.optional,
4786 default=self == self.default,
4787 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4788 expl=None if self._expl is None else tag_decode(self._expl),
4793 expl_offset=self.expl_offset if self.expled else None,
4794 expl_tlen=self.expl_tlen if self.expled else None,
4795 expl_llen=self.expl_llen if self.expled else None,
4796 expl_vlen=self.expl_vlen if self.expled else None,
4797 expl_lenindef=self.expl_lenindef,
4798 lenindef=self.lenindef,
4799 ber_encoded=self.ber_encoded,
4802 for name in self.specs:
4803 value = self._value.get(name)
4806 yield value.pps(decode_path=decode_path + (name,))
4807 for pp in self.pps_lenindef(decode_path):
4811 class Set(Sequence):
4812 """``SET`` structure type
4814 Its usage is identical to :py:class:`pyderasn.Sequence`.
4816 .. _allow_unordered_set_ctx:
4818 DER prohibits unordered values encoding and will raise an error
4819 during decode. If If :ref:`bered <bered_ctx>` context option is set,
4820 then no error will occure. Also you can disable strict values
4821 ordering check by setting ``"allow_unordered_set": True``
4822 :ref:`context <ctx>` option.
4825 tag_default = tag_encode(form=TagFormConstructed, num=17)
4826 asn1_type_name = "SET"
4829 raws = self._encoded_values()
4832 return b"".join((self.tag, len_encode(len(v)), v))
4834 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4836 t, tlen, lv = tag_strip(tlv)
4837 except DecodeError as err:
4838 raise err.__class__(
4840 klass=self.__class__,
4841 decode_path=decode_path,
4846 klass=self.__class__,
4847 decode_path=decode_path,
4853 ctx_bered = ctx.get("bered", False)
4855 l, llen, v = len_decode(lv)
4856 except LenIndefForm as err:
4858 raise err.__class__(
4860 klass=self.__class__,
4861 decode_path=decode_path,
4864 l, llen, v = 0, 1, lv[1:]
4866 except DecodeError as err:
4867 raise err.__class__(
4869 klass=self.__class__,
4870 decode_path=decode_path,
4874 raise NotEnoughData(
4875 "encoded length is longer than data",
4876 klass=self.__class__,
4880 v, tail = v[:l], v[l:]
4882 sub_offset = offset + tlen + llen
4885 ctx_allow_default_values = ctx.get("allow_default_values", False)
4886 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4887 value_prev = memoryview(v[:0])
4888 specs_items = self.specs.items
4890 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4892 for name, spec in specs_items():
4893 sub_decode_path = decode_path + (name,)
4899 decode_path=sub_decode_path,
4902 _ctx_immutable=False,
4909 klass=self.__class__,
4910 decode_path=decode_path,
4913 value, v_tail = spec.decode(
4917 decode_path=sub_decode_path,
4919 _ctx_immutable=False,
4921 value_len = value.fulllen
4922 if value_prev.tobytes() > v[:value_len].tobytes():
4923 if ctx_bered or ctx_allow_unordered_set:
4927 "unordered " + self.asn1_type_name,
4928 klass=self.__class__,
4929 decode_path=sub_decode_path,
4932 if spec.default is None or value != spec.default:
4934 elif ctx_bered or ctx_allow_default_values:
4938 "DEFAULT value met",
4939 klass=self.__class__,
4940 decode_path=sub_decode_path,
4943 values[name] = value
4944 value_prev = v[:value_len]
4945 sub_offset += value_len
4948 obj = self.__class__(
4952 default=self.default,
4953 optional=self.optional,
4954 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4957 if v[:EOC_LEN].tobytes() != EOC:
4960 klass=self.__class__,
4961 decode_path=decode_path,
4969 "not all values are ready",
4970 klass=self.__class__,
4971 decode_path=decode_path,
4974 obj.ber_encoded = ber_encoded
4978 class SequenceOf(Obj):
4979 """``SEQUENCE OF`` sequence type
4981 For that kind of type you must specify the object it will carry on
4982 (bounds are for example here, not required)::
4984 class Ints(SequenceOf):
4989 >>> ints.append(Integer(123))
4990 >>> ints.append(Integer(234))
4992 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4993 >>> [int(i) for i in ints]
4995 >>> ints.append(Integer(345))
4996 Traceback (most recent call last):
4997 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5000 >>> ints[1] = Integer(345)
5002 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5004 Also you can initialize sequence with preinitialized values:
5006 >>> ints = Ints([Integer(123), Integer(234)])
5008 __slots__ = ("spec", "_bound_min", "_bound_max")
5009 tag_default = tag_encode(form=TagFormConstructed, num=16)
5010 asn1_type_name = "SEQUENCE OF"
5023 super(SequenceOf, self).__init__(
5031 schema = getattr(self, "schema", None)
5033 raise ValueError("schema must be specified")
5035 self._bound_min, self._bound_max = getattr(
5039 ) if bounds is None else bounds
5041 if value is not None:
5042 self._value = self._value_sanitize(value)
5043 if default is not None:
5044 default_value = self._value_sanitize(default)
5045 default_obj = self.__class__(
5050 default_obj._value = default_value
5051 self.default = default_obj
5053 self._value = default_obj.copy()._value
5055 def _value_sanitize(self, value):
5056 if issubclass(value.__class__, SequenceOf):
5057 value = value._value
5058 elif hasattr(value, "__iter__"):
5061 raise InvalidValueType((self.__class__, iter))
5062 if not self._bound_min <= len(value) <= self._bound_max:
5063 raise BoundsError(self._bound_min, len(value), self._bound_max)
5065 if not isinstance(v, self.spec.__class__):
5066 raise InvalidValueType((self.spec.__class__,))
5071 return all(v.ready for v in self._value)
5075 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5077 return any(v.bered for v in self._value)
5080 obj = self.__class__(schema=self.spec)
5081 obj._bound_min = self._bound_min
5082 obj._bound_max = self._bound_max
5084 obj._expl = self._expl
5085 obj.default = self.default
5086 obj.optional = self.optional
5087 obj.offset = self.offset
5088 obj.llen = self.llen
5089 obj.vlen = self.vlen
5090 obj._value = [v.copy() for v in self._value]
5093 def __eq__(self, their):
5094 if isinstance(their, self.__class__):
5096 self.spec == their.spec and
5097 self.tag == their.tag and
5098 self._expl == their._expl and
5099 self._value == their._value
5101 if hasattr(their, "__iter__"):
5102 return self._value == list(their)
5114 return self.__class__(
5118 (self._bound_min, self._bound_max)
5119 if bounds is None else bounds
5121 impl=self.tag if impl is None else impl,
5122 expl=self._expl if expl is None else expl,
5123 default=self.default if default is None else default,
5124 optional=self.optional if optional is None else optional,
5127 def __contains__(self, key):
5128 return key in self._value
5130 def append(self, value):
5131 if not isinstance(value, self.spec.__class__):
5132 raise InvalidValueType((self.spec.__class__,))
5133 if len(self._value) + 1 > self._bound_max:
5136 len(self._value) + 1,
5139 self._value.append(value)
5142 self._assert_ready()
5143 return iter(self._value)
5146 self._assert_ready()
5147 return len(self._value)
5149 def __setitem__(self, key, value):
5150 if not isinstance(value, self.spec.__class__):
5151 raise InvalidValueType((self.spec.__class__,))
5152 self._value[key] = self.spec(value=value)
5154 def __getitem__(self, key):
5155 return self._value[key]
5157 def _encoded_values(self):
5158 return [v.encode() for v in self._value]
5161 v = b"".join(self._encoded_values())
5162 return b"".join((self.tag, len_encode(len(v)), v))
5164 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5166 t, tlen, lv = tag_strip(tlv)
5167 except DecodeError as err:
5168 raise err.__class__(
5170 klass=self.__class__,
5171 decode_path=decode_path,
5176 klass=self.__class__,
5177 decode_path=decode_path,
5183 ctx_bered = ctx.get("bered", False)
5185 l, llen, v = len_decode(lv)
5186 except LenIndefForm as err:
5188 raise err.__class__(
5190 klass=self.__class__,
5191 decode_path=decode_path,
5194 l, llen, v = 0, 1, lv[1:]
5196 except DecodeError as err:
5197 raise err.__class__(
5199 klass=self.__class__,
5200 decode_path=decode_path,
5204 raise NotEnoughData(
5205 "encoded length is longer than data",
5206 klass=self.__class__,
5207 decode_path=decode_path,
5211 v, tail = v[:l], v[l:]
5213 sub_offset = offset + tlen + llen
5215 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5216 value_prev = memoryview(v[:0])
5220 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5222 sub_decode_path = decode_path + (str(len(_value)),)
5223 value, v_tail = spec.decode(
5227 decode_path=sub_decode_path,
5229 _ctx_immutable=False,
5231 value_len = value.fulllen
5233 if value_prev.tobytes() > v[:value_len].tobytes():
5234 if ctx_bered or ctx_allow_unordered_set:
5238 "unordered " + self.asn1_type_name,
5239 klass=self.__class__,
5240 decode_path=sub_decode_path,
5243 value_prev = v[:value_len]
5244 _value.append(value)
5245 sub_offset += value_len
5249 obj = self.__class__(
5252 bounds=(self._bound_min, self._bound_max),
5255 default=self.default,
5256 optional=self.optional,
5257 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5259 except BoundsError as err:
5262 klass=self.__class__,
5263 decode_path=decode_path,
5267 if v[:EOC_LEN].tobytes() != EOC:
5270 klass=self.__class__,
5271 decode_path=decode_path,
5276 obj.ber_encoded = ber_encoded
5281 pp_console_row(next(self.pps())),
5282 ", ".join(repr(v) for v in self._value),
5285 def pps(self, decode_path=()):
5287 asn1_type_name=self.asn1_type_name,
5288 obj_name=self.__class__.__name__,
5289 decode_path=decode_path,
5290 optional=self.optional,
5291 default=self == self.default,
5292 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5293 expl=None if self._expl is None else tag_decode(self._expl),
5298 expl_offset=self.expl_offset if self.expled else None,
5299 expl_tlen=self.expl_tlen if self.expled else None,
5300 expl_llen=self.expl_llen if self.expled else None,
5301 expl_vlen=self.expl_vlen if self.expled else None,
5302 expl_lenindef=self.expl_lenindef,
5303 lenindef=self.lenindef,
5304 ber_encoded=self.ber_encoded,
5307 for i, value in enumerate(self._value):
5308 yield value.pps(decode_path=decode_path + (str(i),))
5309 for pp in self.pps_lenindef(decode_path):
5313 class SetOf(SequenceOf):
5314 """``SET OF`` sequence type
5316 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5319 tag_default = tag_encode(form=TagFormConstructed, num=17)
5320 asn1_type_name = "SET OF"
5323 raws = self._encoded_values()
5326 return b"".join((self.tag, len_encode(len(v)), v))
5328 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5329 return super(SetOf, self)._decode(
5335 ordering_check=True,
5339 def obj_by_path(pypath): # pragma: no cover
5340 """Import object specified as string Python path
5342 Modules must be separated from classes/functions with ``:``.
5344 >>> obj_by_path("foo.bar:Baz")
5345 <class 'foo.bar.Baz'>
5346 >>> obj_by_path("foo.bar:Baz.boo")
5347 <classmethod 'foo.bar.Baz.boo'>
5349 mod, objs = pypath.rsplit(":", 1)
5350 from importlib import import_module
5351 obj = import_module(mod)
5352 for obj_name in objs.split("."):
5353 obj = getattr(obj, obj_name)
5357 def generic_decoder(): # pragma: no cover
5358 # All of this below is a big hack with self references
5359 choice = PrimitiveTypes()
5360 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5361 choice.specs["SetOf"] = SetOf(schema=choice)
5363 choice.specs["SequenceOf%d" % i] = SequenceOf(
5367 choice.specs["Any"] = Any()
5369 # Class name equals to type name, to omit it from output
5370 class SEQUENCEOF(SequenceOf):
5378 with_decode_path=False,
5379 decode_path_only=(),
5381 def _pprint_pps(pps):
5383 if hasattr(pp, "_fields"):
5385 decode_path_only != () and
5386 pp.decode_path[:len(decode_path_only)] != decode_path_only
5389 if pp.asn1_type_name == Choice.asn1_type_name:
5391 pp_kwargs = pp._asdict()
5392 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5393 pp = _pp(**pp_kwargs)
5394 yield pp_console_row(
5399 with_colours=with_colours,
5400 with_decode_path=with_decode_path,
5401 decode_path_len_decrease=len(decode_path_only),
5403 for row in pp_console_blob(
5405 decode_path_len_decrease=len(decode_path_only),
5409 for row in _pprint_pps(pp):
5411 return "\n".join(_pprint_pps(obj.pps()))
5412 return SEQUENCEOF(), pprint_any
5415 def main(): # pragma: no cover
5417 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5418 parser.add_argument(
5422 help="Skip that number of bytes from the beginning",
5424 parser.add_argument(
5426 help="Python path to dictionary with OIDs",
5428 parser.add_argument(
5430 help="Python path to schema definition to use",
5432 parser.add_argument(
5433 "--defines-by-path",
5434 help="Python path to decoder's defines_by_path",
5436 parser.add_argument(
5438 action="store_true",
5439 help="Disallow BER encoding",
5441 parser.add_argument(
5442 "--print-decode-path",
5443 action="store_true",
5444 help="Print decode paths",
5446 parser.add_argument(
5447 "--decode-path-only",
5448 help="Print only specified decode path",
5450 parser.add_argument(
5452 action="store_true",
5453 help="Allow explicit tag out-of-bound",
5455 parser.add_argument(
5457 type=argparse.FileType("rb"),
5458 help="Path to DER file you want to decode",
5460 args = parser.parse_args()
5461 args.DERFile.seek(args.skip)
5462 der = memoryview(args.DERFile.read())
5463 args.DERFile.close()
5464 oids = obj_by_path(args.oids) if args.oids else {}
5466 schema = obj_by_path(args.schema)
5467 from functools import partial
5468 pprinter = partial(pprint, big_blobs=True)
5470 schema, pprinter = generic_decoder()
5472 "bered": not args.nobered,
5473 "allow_expl_oob": args.allow_expl_oob,
5475 if args.defines_by_path is not None:
5476 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5477 obj, tail = schema().decode(der, ctx=ctx)
5481 with_colours=True if environ.get("NO_COLOR") is None else False,
5482 with_decode_path=args.print_decode_path,
5484 () if args.decode_path_only is None else
5485 tuple(args.decode_path_only.split(":"))
5489 print("\nTrailing data: %s" % hexenc(tail))
5492 if __name__ == "__main__":