3 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
4 # Copyright (C) 2017-2019 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 """Python ASN.1 DER/BER codec with abstract structures
21 This library allows you to marshal various structures in ASN.1 DER
22 format, unmarshal them in BER/CER/DER ones.
26 >>> Integer().decode(raw) == i
29 There are primitive types, holding single values
30 (:py:class:`pyderasn.BitString`,
31 :py:class:`pyderasn.Boolean`,
32 :py:class:`pyderasn.Enumerated`,
33 :py:class:`pyderasn.GeneralizedTime`,
34 :py:class:`pyderasn.Integer`,
35 :py:class:`pyderasn.Null`,
36 :py:class:`pyderasn.ObjectIdentifier`,
37 :py:class:`pyderasn.OctetString`,
38 :py:class:`pyderasn.UTCTime`,
39 :py:class:`various strings <pyderasn.CommonString>`
40 (:py:class:`pyderasn.BMPString`,
41 :py:class:`pyderasn.GeneralString`,
42 :py:class:`pyderasn.GraphicString`,
43 :py:class:`pyderasn.IA5String`,
44 :py:class:`pyderasn.ISO646String`,
45 :py:class:`pyderasn.NumericString`,
46 :py:class:`pyderasn.PrintableString`,
47 :py:class:`pyderasn.T61String`,
48 :py:class:`pyderasn.TeletexString`,
49 :py:class:`pyderasn.UniversalString`,
50 :py:class:`pyderasn.UTF8String`,
51 :py:class:`pyderasn.VideotexString`,
52 :py:class:`pyderasn.VisibleString`)),
53 constructed types, holding multiple primitive types
54 (:py:class:`pyderasn.Sequence`,
55 :py:class:`pyderasn.SequenceOf`,
56 :py:class:`pyderasn.Set`,
57 :py:class:`pyderasn.SetOf`),
58 and special types like
59 :py:class:`pyderasn.Any` and
60 :py:class:`pyderasn.Choice`.
68 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
69 the default tag used during coding process. You can override it with
70 either ``IMPLICIT`` (using ``impl`` keyword argument), or
71 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments take
72 raw binary string, containing that tag. You can **not** set implicit and
73 explicit tags simultaneously.
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
78 number. Pay attention that explicit tags always have *constructed* tag
79 (``tag_ctxc``), but implicit tags for primitive types are primitive
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two objects of the same type, but with different implicit/explicit tags
94 You can get object's effective tag (either default or implicited) through
95 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
112 Many objects in sequences could be ``OPTIONAL`` and could have
113 ``DEFAULT`` value. You can specify that object's property using
114 corresponding keyword arguments.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
119 Those specifications do not play any role in primitive value encoding,
120 but are taken into account when dealing with sequences holding them. For
121 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
143 Some objects give ability to set value size constraints. This is either
144 possible integer value, or allowed length of various strings and
145 sequences. Constraints are set in the following way::
150 And values satisfaction is checked as: ``MIN <= X <= MAX``.
152 For simplicity you can also set bounds the following way::
154 bounded_x = X(bounds=(MIN, MAX))
156 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
162 All objects have ``ready`` boolean property, that tells if object is
163 ready to be encoded. If that kind of action is performed on unready
164 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
166 All objects have ``copy()`` method, that returns their copy, that can be
174 Decoding is performed using ``decode()`` method. ``offset`` optional
175 argument could be used to set initial object's offset in the binary
176 data, for convenience. It returns decoded object and remaining
177 unmarshalled data (tail). Internally all work is done on
178 ``memoryview(data)``, and you can leave returning tail as a memoryview,
179 by specifying ``leavemm=True`` argument.
181 When object is decoded, ``decoded`` property is true and you can safely
182 use following properties:
184 * ``offset`` -- position including initial offset where object's tag starts
185 * ``tlen`` -- length of object's tag
186 * ``llen`` -- length of object's length value
187 * ``vlen`` -- length of object's value
188 * ``tlvlen`` -- length of the whole object
190 Pay attention that those values do **not** include anything related to
191 explicit tag. If you want to know information about it, then use:
193 * ``expled`` -- to know if explicit tag is set
194 * ``expl_offset`` (it is lesser than ``offset``)
197 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
198 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
200 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
203 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
210 You can specify so called context keyword argument during ``decode()``
211 invocation. It is dictionary containing various options governing
214 Currently available context options:
216 * :ref:`allow_default_values <allow_default_values_ctx>`
217 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
218 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
219 * :ref:`bered <bered_ctx>`
220 * :ref:`defines_by_path <defines_by_path_ctx>`
227 All objects have ``pps()`` method, that is a generator of
228 :py:class:`pyderasn.PP` namedtuple, holding various raw information
229 about the object. If ``pps`` is called on sequences, then all underlying
230 ``PP`` will be yielded.
232 You can use :py:func:`pyderasn.pp_console_row` function, converting
233 those ``PP`` to human readable string. Actually exactly it is used for
234 all object ``repr``. But it is easy to write custom formatters.
236 >>> from pyderasn import pprint
237 >>> encoded = Integer(-12345).encode()
238 >>> obj, tail = Integer().decode(encoded)
239 >>> print(pprint(obj))
240 0 [1,1, 2] INTEGER -12345
247 ASN.1 structures often have ANY and OCTET STRING fields, that are
248 DEFINED BY some previously met ObjectIdentifier. This library provides
249 ability to specify mapping between some OID and field that must be
250 decoded with specific specification.
255 :py:class:`pyderasn.ObjectIdentifier` field inside
256 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
257 necessary for decoding structures. For example, CMS (:rfc:`5652`)
260 class ContentInfo(Sequence):
262 ("contentType", ContentType(defines=((("content",), {
263 id_digestedData: DigestedData(),
264 id_signedData: SignedData(),
266 ("content", Any(expl=tag_ctxc(0))),
269 ``contentType`` field tells that it defines that ``content`` must be
270 decoded with ``SignedData`` specification, if ``contentType`` equals to
271 ``id-signedData``. The same applies to ``DigestedData``. If
272 ``contentType`` contains unknown OID, then no automatic decoding is
275 You can specify multiple fields, that will be autodecoded -- that is why
276 ``defines`` kwarg is a sequence. You can specify defined field
277 relatively or absolutely to current decode path. For example ``defines``
278 for AlgorithmIdentifier of X.509's
279 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
283 id_ecPublicKey: ECParameters(),
284 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
286 (("..", "subjectPublicKey"), {
287 id_rsaEncryption: RSAPublicKey(),
288 id_GostR3410_2001: OctetString(),
292 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
293 autodecode its parameters inside SPKI's algorithm and its public key
296 Following types can be automatically decoded (DEFINED BY):
298 * :py:class:`pyderasn.Any`
299 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
300 * :py:class:`pyderasn.OctetString`
301 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
302 ``Any``/``BitString``/``OctetString``-s
304 When any of those fields is automatically decoded, then ``.defined``
305 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
306 was defined, ``value`` contains corresponding decoded value. For example
307 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
309 .. _defines_by_path_ctx:
311 defines_by_path context option
312 ______________________________
314 Sometimes you either can not or do not want to explicitly set *defines*
315 in the scheme. You can dynamically apply those definitions when calling
316 ``.decode()`` method.
318 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
319 value must be sequence of following tuples::
321 (decode_path, defines)
323 where ``decode_path`` is a tuple holding so-called decode path to the
324 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
325 ``defines``, holding exactly the same value as accepted in its keyword
328 For example, again for CMS, you want to automatically decode
329 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
330 structures it may hold. Also, automatically decode ``controlSequence``
333 content_info, tail = ContentInfo().decode(data, defines_by_path=(
336 ((("content",), {id_signedData: SignedData()}),),
341 DecodePathDefBy(id_signedData),
346 id_cct_PKIData: PKIData(),
347 id_cct_PKIResponse: PKIResponse(),
353 DecodePathDefBy(id_signedData),
356 DecodePathDefBy(id_cct_PKIResponse),
362 id_cmc_recipientNonce: RecipientNonce(),
363 id_cmc_senderNonce: SenderNonce(),
364 id_cmc_statusInfoV2: CMCStatusInfoV2(),
365 id_cmc_transactionId: TransactionId(),
370 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
371 First function is useful for path construction when some automatic
372 decoding is already done. ``any`` means literally any value it meet --
373 useful for SEQUENCE/SET OF-s.
380 By default PyDERASN accepts only DER encoded data. It always encodes to
381 DER. But you can optionally enable BER decoding with setting ``bered``
382 :ref:`context <ctx>` argument to True. Indefinite lengths and
383 constructed primitive types should be parsed successfully.
385 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
386 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
387 STRING``, ``SEQUENCE``, ``SET``, ``SET OF`` can contain it.
388 * If object has an indefinite length encoding, then its ``lenindef``
389 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
390 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
392 * If object has an indefinite length encoded explicit tag, then
393 ``expl_lenindef`` is set to True.
394 * If object has either any of BER-related encoding (explicit tag
395 indefinite length, object's indefinite length, BER-encoding) or any
396 underlying component has that kind of encoding, then ``bered``
397 attribute is set to True. For example SignedData CMS can have
398 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
399 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
401 EOC (end-of-contents) token's length is taken in advance in object's
404 .. _allow_expl_oob_ctx:
406 Allow explicit tag out-of-bound
407 -------------------------------
409 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
410 one value, more than one object. If you set ``allow_expl_oob`` context
411 option to True, then no error will be raised and that invalid encoding
412 will be silently further processed. But pay attention that offsets and
413 lengths will be invalid in that case.
417 This option should be used only for skipping some decode errors, just
418 to see the decoded structure somehow.
425 .. autoclass:: pyderasn.Boolean
430 .. autoclass:: pyderasn.Integer
435 .. autoclass:: pyderasn.BitString
440 .. autoclass:: pyderasn.OctetString
445 .. autoclass:: pyderasn.Null
450 .. autoclass:: pyderasn.ObjectIdentifier
455 .. autoclass:: pyderasn.Enumerated
459 .. autoclass:: pyderasn.CommonString
463 .. autoclass:: pyderasn.NumericString
467 .. autoclass:: pyderasn.UTCTime
468 :members: __init__, todatetime
472 .. autoclass:: pyderasn.GeneralizedTime
479 .. autoclass:: pyderasn.Choice
484 .. autoclass:: PrimitiveTypes
488 .. autoclass:: pyderasn.Any
496 .. autoclass:: pyderasn.Sequence
501 .. autoclass:: pyderasn.Set
506 .. autoclass:: pyderasn.SequenceOf
511 .. autoclass:: pyderasn.SetOf
517 .. autofunction:: pyderasn.abs_decode_path
518 .. autofunction:: pyderasn.colonize_hex
519 .. autofunction:: pyderasn.hexenc
520 .. autofunction:: pyderasn.hexdec
521 .. autofunction:: pyderasn.tag_encode
522 .. autofunction:: pyderasn.tag_decode
523 .. autofunction:: pyderasn.tag_ctxp
524 .. autofunction:: pyderasn.tag_ctxc
525 .. autoclass:: pyderasn.Obj
526 .. autoclass:: pyderasn.DecodeError
528 .. autoclass:: pyderasn.NotEnoughData
529 .. autoclass:: pyderasn.LenIndefForm
530 .. autoclass:: pyderasn.TagMismatch
531 .. autoclass:: pyderasn.InvalidLength
532 .. autoclass:: pyderasn.InvalidOID
533 .. autoclass:: pyderasn.ObjUnknown
534 .. autoclass:: pyderasn.ObjNotReady
535 .. autoclass:: pyderasn.InvalidValueType
536 .. autoclass:: pyderasn.BoundsError
539 from codecs import getdecoder
540 from codecs import getencoder
541 from collections import namedtuple
542 from collections import OrderedDict
543 from copy import copy
544 from datetime import datetime
545 from math import ceil
546 from os import environ
547 from string import ascii_letters
548 from string import digits
550 from six import add_metaclass
551 from six import binary_type
552 from six import byte2int
553 from six import indexbytes
554 from six import int2byte
555 from six import integer_types
556 from six import iterbytes
558 from six import string_types
559 from six import text_type
560 from six import unichr as six_unichr
561 from six.moves import xrange as six_xrange
565 from termcolor import colored
566 except ImportError: # pragma: no cover
567 def colored(what, *args):
611 "TagClassApplication",
615 "TagFormConstructed",
626 TagClassUniversal = 0
627 TagClassApplication = 1 << 6
628 TagClassContext = 1 << 7
629 TagClassPrivate = 1 << 6 | 1 << 7
631 TagFormConstructed = 1 << 5
634 TagClassApplication: "APPLICATION ",
635 TagClassPrivate: "PRIVATE ",
636 TagClassUniversal: "UNIV ",
640 LENINDEF = b"\x80" # length indefinite mark
641 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
644 ########################################################################
646 ########################################################################
648 class ASN1Error(ValueError):
652 class DecodeError(ASN1Error):
653 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
655 :param str msg: reason of decode failing
656 :param klass: optional exact DecodeError inherited class (like
657 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
658 :py:exc:`InvalidLength`)
659 :param decode_path: tuple of strings. It contains human
660 readable names of the fields through which
661 decoding process has passed
662 :param int offset: binary offset where failure happened
664 super(DecodeError, self).__init__()
667 self.decode_path = decode_path
673 "" if self.klass is None else self.klass.__name__,
675 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
676 if len(self.decode_path) > 0 else ""
678 ("(at %d)" % self.offset) if self.offset > 0 else "",
684 return "%s(%s)" % (self.__class__.__name__, self)
687 class NotEnoughData(DecodeError):
691 class LenIndefForm(DecodeError):
695 class TagMismatch(DecodeError):
699 class InvalidLength(DecodeError):
703 class InvalidOID(DecodeError):
707 class ObjUnknown(ASN1Error):
708 def __init__(self, name):
709 super(ObjUnknown, self).__init__()
713 return "object is unknown: %s" % self.name
716 return "%s(%s)" % (self.__class__.__name__, self)
719 class ObjNotReady(ASN1Error):
720 def __init__(self, name):
721 super(ObjNotReady, self).__init__()
725 return "object is not ready: %s" % self.name
728 return "%s(%s)" % (self.__class__.__name__, self)
731 class InvalidValueType(ASN1Error):
732 def __init__(self, expected_types):
733 super(InvalidValueType, self).__init__()
734 self.expected_types = expected_types
737 return "invalid value type, expected: %s" % ", ".join(
738 [repr(t) for t in self.expected_types]
742 return "%s(%s)" % (self.__class__.__name__, self)
745 class BoundsError(ASN1Error):
746 def __init__(self, bound_min, value, bound_max):
747 super(BoundsError, self).__init__()
748 self.bound_min = bound_min
750 self.bound_max = bound_max
753 return "unsatisfied bounds: %s <= %s <= %s" % (
760 return "%s(%s)" % (self.__class__.__name__, self)
763 ########################################################################
765 ########################################################################
767 _hexdecoder = getdecoder("hex")
768 _hexencoder = getencoder("hex")
772 """Binary data to hexadecimal string convert
774 return _hexdecoder(data)[0]
778 """Hexadecimal string to binary data convert
780 return _hexencoder(data)[0].decode("ascii")
783 def int_bytes_len(num, byte_len=8):
786 return int(ceil(float(num.bit_length()) / byte_len))
789 def zero_ended_encode(num):
790 octets = bytearray(int_bytes_len(num, 7))
792 octets[i] = num & 0x7F
796 octets[i] = 0x80 | (num & 0x7F)
802 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
803 """Encode tag to binary form
805 :param int num: tag's number
806 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
807 :py:data:`pyderasn.TagClassContext`,
808 :py:data:`pyderasn.TagClassApplication`,
809 :py:data:`pyderasn.TagClassPrivate`)
810 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
811 :py:data:`pyderasn.TagFormConstructed`)
815 return int2byte(klass | form | num)
816 # [XX|X|11111][1.......][1.......] ... [0.......]
817 return int2byte(klass | form | 31) + zero_ended_encode(num)
821 """Decode tag from binary form
825 No validation is performed, assuming that it has already passed.
827 It returns tuple with three integers, as
828 :py:func:`pyderasn.tag_encode` accepts.
830 first_octet = byte2int(tag)
831 klass = first_octet & 0xC0
832 form = first_octet & 0x20
833 if first_octet & 0x1F < 0x1F:
834 return (klass, form, first_octet & 0x1F)
836 for octet in iterbytes(tag[1:]):
839 return (klass, form, num)
843 """Create CONTEXT PRIMITIVE tag
845 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
849 """Create CONTEXT CONSTRUCTED tag
851 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
855 """Take off tag from the data
857 :returns: (encoded tag, tag length, remaining data)
860 raise NotEnoughData("no data at all")
861 if byte2int(data) & 0x1F < 31:
862 return data[:1], 1, data[1:]
867 raise DecodeError("unfinished tag")
868 if indexbytes(data, i) & 0x80 == 0:
871 return data[:i], i, data[i:]
877 octets = bytearray(int_bytes_len(l) + 1)
878 octets[0] = 0x80 | (len(octets) - 1)
879 for i in six_xrange(len(octets) - 1, 0, -1):
885 def len_decode(data):
888 :returns: (decoded length, length's length, remaining data)
889 :raises LenIndefForm: if indefinite form encoding is met
892 raise NotEnoughData("no data at all")
893 first_octet = byte2int(data)
894 if first_octet & 0x80 == 0:
895 return first_octet, 1, data[1:]
896 octets_num = first_octet & 0x7F
897 if octets_num + 1 > len(data):
898 raise NotEnoughData("encoded length is longer than data")
901 if byte2int(data[1:]) == 0:
902 raise DecodeError("leading zeros")
904 for v in iterbytes(data[1:1 + octets_num]):
907 raise DecodeError("long form instead of short one")
908 return l, 1 + octets_num, data[1 + octets_num:]
911 ########################################################################
913 ########################################################################
915 class AutoAddSlots(type):
916 def __new__(mcs, name, bases, _dict):
917 _dict["__slots__"] = _dict.get("__slots__", ())
918 return type.__new__(mcs, name, bases, _dict)
921 @add_metaclass(AutoAddSlots)
923 """Common ASN.1 object class
925 All ASN.1 types are inherited from it. It has metaclass that
926 automatically adds ``__slots__`` to all inherited classes.
950 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
951 self._expl = getattr(self, "expl", None) if expl is None else expl
952 if self.tag != self.tag_default and self._expl is not None:
953 raise ValueError("implicit and explicit tags can not be set simultaneously")
954 if default is not None:
956 self.optional = optional
957 self.offset, self.llen, self.vlen = _decoded
959 self.expl_lenindef = False
960 self.lenindef = False
961 self.ber_encoded = False
964 def ready(self): # pragma: no cover
965 """Is object ready to be encoded?
967 raise NotImplementedError()
969 def _assert_ready(self):
971 raise ObjNotReady(self.__class__.__name__)
975 """Is either object or any elements inside is BER encoded?
977 return self.expl_lenindef or self.lenindef or self.ber_encoded
981 """Is object decoded?
983 return (self.llen + self.vlen) > 0
985 def copy(self): # pragma: no cover
986 """Make a copy of object, safe to be mutated
988 raise NotImplementedError()
996 return self.tlen + self.llen + self.vlen
998 def __str__(self): # pragma: no cover
999 return self.__bytes__() if PY2 else self.__unicode__()
1001 def __ne__(self, their):
1002 return not(self == their)
1004 def __gt__(self, their): # pragma: no cover
1005 return not(self < their)
1007 def __le__(self, their): # pragma: no cover
1008 return (self == their) or (self < their)
1010 def __ge__(self, their): # pragma: no cover
1011 return (self == their) or (self > their)
1013 def _encode(self): # pragma: no cover
1014 raise NotImplementedError()
1016 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1017 raise NotImplementedError()
1020 raw = self._encode()
1021 if self._expl is None:
1023 return b"".join((self._expl, len_encode(len(raw)), raw))
1033 _ctx_immutable=True,
1037 :param data: either binary or memoryview
1038 :param int offset: initial data's offset
1039 :param bool leavemm: do we need to leave memoryview of remaining
1040 data as is, or convert it to bytes otherwise
1041 :param ctx: optional :ref:`context <ctx>` governing decoding process
1042 :param tag_only: decode only the tag, without length and contents
1043 (used only in Choice and Set structures, trying to
1044 determine if tag satisfies the scheme)
1045 :param _ctx_immutable: do we need to copy ``ctx`` before using it
1046 :returns: (Obj, remaining data)
1050 elif _ctx_immutable:
1052 tlv = memoryview(data)
1053 if self._expl is None:
1054 result = self._decode(
1057 decode_path=decode_path,
1066 t, tlen, lv = tag_strip(tlv)
1067 except DecodeError as err:
1068 raise err.__class__(
1070 klass=self.__class__,
1071 decode_path=decode_path,
1076 klass=self.__class__,
1077 decode_path=decode_path,
1081 l, llen, v = len_decode(lv)
1082 except LenIndefForm as err:
1083 if not ctx.get("bered", False):
1084 raise err.__class__(
1086 klass=self.__class__,
1087 decode_path=decode_path,
1091 offset += tlen + llen
1092 result = self._decode(
1095 decode_path=decode_path,
1099 if tag_only: # pragma: no cover
1102 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1103 if eoc_expected.tobytes() != EOC:
1106 klass=self.__class__,
1107 decode_path=decode_path,
1111 obj.expl_lenindef = True
1112 except DecodeError as err:
1113 raise err.__class__(
1115 klass=self.__class__,
1116 decode_path=decode_path,
1121 raise NotEnoughData(
1122 "encoded length is longer than data",
1123 klass=self.__class__,
1124 decode_path=decode_path,
1127 result = self._decode(
1129 offset=offset + tlen + llen,
1130 decode_path=decode_path,
1134 if tag_only: # pragma: no cover
1137 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1139 "explicit tag out-of-bound, longer than data",
1140 klass=self.__class__,
1141 decode_path=decode_path,
1144 return obj, (tail if leavemm else tail.tobytes())
1148 return self._expl is not None
1155 def expl_tlen(self):
1156 return len(self._expl)
1159 def expl_llen(self):
1160 if self.expl_lenindef:
1162 return len(len_encode(self.tlvlen))
1165 def expl_offset(self):
1166 return self.offset - self.expl_tlen - self.expl_llen
1169 def expl_vlen(self):
1173 def expl_tlvlen(self):
1174 return self.expl_tlen + self.expl_llen + self.expl_vlen
1177 def fulloffset(self):
1178 return self.expl_offset if self.expled else self.offset
1182 return self.expl_tlvlen if self.expled else self.tlvlen
1184 def pps_lenindef(self, decode_path):
1185 if self.lenindef and not (
1186 getattr(self, "defined", None) is not None and
1187 self.defined[1].lenindef
1190 asn1_type_name="EOC",
1192 decode_path=decode_path,
1194 self.offset + self.tlvlen -
1195 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1203 if self.expl_lenindef:
1205 asn1_type_name="EOC",
1206 obj_name="EXPLICIT",
1207 decode_path=decode_path,
1208 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1217 class DecodePathDefBy(object):
1218 """DEFINED BY representation inside decode path
1220 __slots__ = ("defined_by",)
1222 def __init__(self, defined_by):
1223 self.defined_by = defined_by
1225 def __ne__(self, their):
1226 return not(self == their)
1228 def __eq__(self, their):
1229 if not isinstance(their, self.__class__):
1231 return self.defined_by == their.defined_by
1234 return "DEFINED BY " + str(self.defined_by)
1237 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1240 ########################################################################
1242 ########################################################################
1244 PP = namedtuple("PP", (
1272 asn1_type_name="unknown",
1289 expl_lenindef=False,
1320 def _colourize(what, colour, with_colours, attrs=("bold",)):
1321 return colored(what, colour, attrs=attrs) if with_colours else what
1324 def colonize_hex(hexed):
1325 """Separate hexadecimal string with colons
1327 return ":".join(hexed[i:i + 2] for i in range(0, len(hexed), 2))
1336 with_decode_path=False,
1337 decode_path_len_decrease=0,
1344 " " if pp.expl_offset is None else
1345 ("-%d" % (pp.offset - pp.expl_offset))
1347 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1349 col = _colourize(col, "red", with_colours, ())
1350 col += _colourize("B", "red", with_colours) if pp.bered else " "
1352 col = "[%d,%d,%4d]%s" % (
1356 LENINDEF_PP_CHAR if pp.lenindef else " "
1358 col = _colourize(col, "green", with_colours, ())
1360 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1361 if decode_path_len > 0:
1362 cols.append(" ." * decode_path_len)
1363 ent = pp.decode_path[-1]
1364 if isinstance(ent, DecodePathDefBy):
1365 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1366 value = str(ent.defined_by)
1368 oids is not None and
1369 ent.defined_by.asn1_type_name ==
1370 ObjectIdentifier.asn1_type_name and
1373 cols.append(_colourize("%s:" % oids[value], "green", with_colours))
1375 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1377 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1378 if pp.expl is not None:
1379 klass, _, num = pp.expl
1380 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1381 cols.append(_colourize(col, "blue", with_colours))
1382 if pp.impl is not None:
1383 klass, _, num = pp.impl
1384 col = "[%s%d]" % (TagClassReprs[klass], num)
1385 cols.append(_colourize(col, "blue", with_colours))
1386 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1387 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1389 cols.append(_colourize("BER", "red", with_colours))
1390 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1391 if pp.value is not None:
1393 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1395 oids is not None and
1396 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1399 cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
1400 if pp.asn1_type_name == Integer.asn1_type_name:
1401 hex_repr = hex(int(pp.obj._value))[2:].upper()
1402 if len(hex_repr) % 2 != 0:
1403 hex_repr = "0" + hex_repr
1404 cols.append(_colourize(
1405 "(%s)" % colonize_hex(hex_repr),
1410 if isinstance(pp.blob, binary_type):
1411 cols.append(hexenc(pp.blob))
1412 elif isinstance(pp.blob, tuple):
1413 cols.append(", ".join(pp.blob))
1415 cols.append(_colourize("OPTIONAL", "red", with_colours))
1417 cols.append(_colourize("DEFAULT", "red", with_colours))
1418 if with_decode_path:
1419 cols.append(_colourize(
1420 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1424 return " ".join(cols)
1427 def pp_console_blob(pp, decode_path_len_decrease=0):
1428 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1429 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1430 if decode_path_len > 0:
1431 cols.append(" ." * (decode_path_len + 1))
1432 if isinstance(pp.blob, binary_type):
1433 blob = hexenc(pp.blob).upper()
1434 for i in range(0, len(blob), 32):
1435 chunk = blob[i:i + 32]
1436 yield " ".join(cols + [colonize_hex(chunk)])
1437 elif isinstance(pp.blob, tuple):
1438 yield " ".join(cols + [", ".join(pp.blob)])
1446 with_decode_path=False,
1447 decode_path_only=(),
1449 """Pretty print object
1451 :param Obj obj: object you want to pretty print
1452 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1453 from it is met, then its humand readable form is printed
1454 :param big_blobs: if large binary objects are met (like OctetString
1455 values), do we need to print them too, on separate
1457 :param with_colours: colourize output, if ``termcolor`` library
1459 :param with_decode_path: print decode path
1460 :param decode_path_only: print only that specified decode path
1462 def _pprint_pps(pps):
1464 if hasattr(pp, "_fields"):
1466 decode_path_only != () and
1468 str(p) for p in pp.decode_path[:len(decode_path_only)]
1469 ) != decode_path_only
1473 yield pp_console_row(
1478 with_colours=with_colours,
1479 with_decode_path=with_decode_path,
1480 decode_path_len_decrease=len(decode_path_only),
1482 for row in pp_console_blob(
1484 decode_path_len_decrease=len(decode_path_only),
1488 yield pp_console_row(
1493 with_colours=with_colours,
1494 with_decode_path=with_decode_path,
1495 decode_path_len_decrease=len(decode_path_only),
1498 for row in _pprint_pps(pp):
1500 return "\n".join(_pprint_pps(obj.pps()))
1503 ########################################################################
1504 # ASN.1 primitive types
1505 ########################################################################
1508 """``BOOLEAN`` boolean type
1510 >>> b = Boolean(True)
1512 >>> b == Boolean(True)
1518 tag_default = tag_encode(1)
1519 asn1_type_name = "BOOLEAN"
1531 :param value: set the value. Either boolean type, or
1532 :py:class:`pyderasn.Boolean` object
1533 :param bytes impl: override default tag with ``IMPLICIT`` one
1534 :param bytes expl: override default tag with ``EXPLICIT`` one
1535 :param default: set default value. Type same as in ``value``
1536 :param bool optional: is object ``OPTIONAL`` in sequence
1538 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1539 self._value = None if value is None else self._value_sanitize(value)
1540 if default is not None:
1541 default = self._value_sanitize(default)
1542 self.default = self.__class__(
1548 self._value = default
1550 def _value_sanitize(self, value):
1551 if issubclass(value.__class__, Boolean):
1553 if isinstance(value, bool):
1555 raise InvalidValueType((self.__class__, bool))
1559 return self._value is not None
1562 obj = self.__class__()
1563 obj._value = self._value
1565 obj._expl = self._expl
1566 obj.default = self.default
1567 obj.optional = self.optional
1568 obj.offset = self.offset
1569 obj.llen = self.llen
1570 obj.vlen = self.vlen
1573 def __nonzero__(self):
1574 self._assert_ready()
1578 self._assert_ready()
1581 def __eq__(self, their):
1582 if isinstance(their, bool):
1583 return self._value == their
1584 if not issubclass(their.__class__, Boolean):
1587 self._value == their._value and
1588 self.tag == their.tag and
1589 self._expl == their._expl
1600 return self.__class__(
1602 impl=self.tag if impl is None else impl,
1603 expl=self._expl if expl is None else expl,
1604 default=self.default if default is None else default,
1605 optional=self.optional if optional is None else optional,
1609 self._assert_ready()
1613 (b"\xFF" if self._value else b"\x00"),
1616 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1618 t, _, lv = tag_strip(tlv)
1619 except DecodeError as err:
1620 raise err.__class__(
1622 klass=self.__class__,
1623 decode_path=decode_path,
1628 klass=self.__class__,
1629 decode_path=decode_path,
1635 l, _, v = len_decode(lv)
1636 except DecodeError as err:
1637 raise err.__class__(
1639 klass=self.__class__,
1640 decode_path=decode_path,
1644 raise InvalidLength(
1645 "Boolean's length must be equal to 1",
1646 klass=self.__class__,
1647 decode_path=decode_path,
1651 raise NotEnoughData(
1652 "encoded length is longer than data",
1653 klass=self.__class__,
1654 decode_path=decode_path,
1657 first_octet = byte2int(v)
1659 if first_octet == 0:
1661 elif first_octet == 0xFF:
1663 elif ctx.get("bered", False):
1668 "unacceptable Boolean value",
1669 klass=self.__class__,
1670 decode_path=decode_path,
1673 obj = self.__class__(
1677 default=self.default,
1678 optional=self.optional,
1679 _decoded=(offset, 1, 1),
1681 obj.ber_encoded = ber_encoded
1685 return pp_console_row(next(self.pps()))
1687 def pps(self, decode_path=()):
1690 asn1_type_name=self.asn1_type_name,
1691 obj_name=self.__class__.__name__,
1692 decode_path=decode_path,
1693 value=str(self._value) if self.ready else None,
1694 optional=self.optional,
1695 default=self == self.default,
1696 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1697 expl=None if self._expl is None else tag_decode(self._expl),
1702 expl_offset=self.expl_offset if self.expled else None,
1703 expl_tlen=self.expl_tlen if self.expled else None,
1704 expl_llen=self.expl_llen if self.expled else None,
1705 expl_vlen=self.expl_vlen if self.expled else None,
1706 expl_lenindef=self.expl_lenindef,
1707 ber_encoded=self.ber_encoded,
1710 for pp in self.pps_lenindef(decode_path):
1715 """``INTEGER`` integer type
1717 >>> b = Integer(-123)
1719 >>> b == Integer(-123)
1724 >>> Integer(2, bounds=(1, 3))
1726 >>> Integer(5, bounds=(1, 3))
1727 Traceback (most recent call last):
1728 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1732 class Version(Integer):
1739 >>> v = Version("v1")
1746 {'v3': 2, 'v1': 0, 'v2': 1}
1748 __slots__ = ("specs", "_bound_min", "_bound_max")
1749 tag_default = tag_encode(2)
1750 asn1_type_name = "INTEGER"
1764 :param value: set the value. Either integer type, named value
1765 (if ``schema`` is specified in the class), or
1766 :py:class:`pyderasn.Integer` object
1767 :param bounds: set ``(MIN, MAX)`` value constraint.
1768 (-inf, +inf) by default
1769 :param bytes impl: override default tag with ``IMPLICIT`` one
1770 :param bytes expl: override default tag with ``EXPLICIT`` one
1771 :param default: set default value. Type same as in ``value``
1772 :param bool optional: is object ``OPTIONAL`` in sequence
1774 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1776 specs = getattr(self, "schema", {}) if _specs is None else _specs
1777 self.specs = specs if isinstance(specs, dict) else dict(specs)
1778 self._bound_min, self._bound_max = getattr(
1781 (float("-inf"), float("+inf")),
1782 ) if bounds is None else bounds
1783 if value is not None:
1784 self._value = self._value_sanitize(value)
1785 if default is not None:
1786 default = self._value_sanitize(default)
1787 self.default = self.__class__(
1793 if self._value is None:
1794 self._value = default
1796 def _value_sanitize(self, value):
1797 if issubclass(value.__class__, Integer):
1798 value = value._value
1799 elif isinstance(value, integer_types):
1801 elif isinstance(value, str):
1802 value = self.specs.get(value)
1804 raise ObjUnknown("integer value: %s" % value)
1806 raise InvalidValueType((self.__class__, int, str))
1807 if not self._bound_min <= value <= self._bound_max:
1808 raise BoundsError(self._bound_min, value, self._bound_max)
1813 return self._value is not None
1816 obj = self.__class__(_specs=self.specs)
1817 obj._value = self._value
1818 obj._bound_min = self._bound_min
1819 obj._bound_max = self._bound_max
1821 obj._expl = self._expl
1822 obj.default = self.default
1823 obj.optional = self.optional
1824 obj.offset = self.offset
1825 obj.llen = self.llen
1826 obj.vlen = self.vlen
1830 self._assert_ready()
1831 return int(self._value)
1834 self._assert_ready()
1837 bytes(self._expl or b"") +
1838 str(self._value).encode("ascii"),
1841 def __eq__(self, their):
1842 if isinstance(their, integer_types):
1843 return self._value == their
1844 if not issubclass(their.__class__, Integer):
1847 self._value == their._value and
1848 self.tag == their.tag and
1849 self._expl == their._expl
1852 def __lt__(self, their):
1853 return self._value < their._value
1857 for name, value in self.specs.items():
1858 if value == self._value:
1870 return self.__class__(
1873 (self._bound_min, self._bound_max)
1874 if bounds is None else bounds
1876 impl=self.tag if impl is None else impl,
1877 expl=self._expl if expl is None else expl,
1878 default=self.default if default is None else default,
1879 optional=self.optional if optional is None else optional,
1884 self._assert_ready()
1888 octets = bytearray([0])
1892 octets = bytearray()
1894 octets.append((value & 0xFF) ^ 0xFF)
1896 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1899 octets = bytearray()
1901 octets.append(value & 0xFF)
1903 if octets[-1] & 0x80 > 0:
1906 octets = bytes(octets)
1908 bytes_len = ceil(value.bit_length() / 8) or 1
1911 octets = value.to_bytes(
1916 except OverflowError:
1920 return b"".join((self.tag, len_encode(len(octets)), octets))
1922 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1924 t, _, lv = tag_strip(tlv)
1925 except DecodeError as err:
1926 raise err.__class__(
1928 klass=self.__class__,
1929 decode_path=decode_path,
1934 klass=self.__class__,
1935 decode_path=decode_path,
1941 l, llen, v = len_decode(lv)
1942 except DecodeError as err:
1943 raise err.__class__(
1945 klass=self.__class__,
1946 decode_path=decode_path,
1950 raise NotEnoughData(
1951 "encoded length is longer than data",
1952 klass=self.__class__,
1953 decode_path=decode_path,
1957 raise NotEnoughData(
1959 klass=self.__class__,
1960 decode_path=decode_path,
1963 v, tail = v[:l], v[l:]
1964 first_octet = byte2int(v)
1966 second_octet = byte2int(v[1:])
1968 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1969 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1972 "non normalized integer",
1973 klass=self.__class__,
1974 decode_path=decode_path,
1979 if first_octet & 0x80 > 0:
1980 octets = bytearray()
1981 for octet in bytearray(v):
1982 octets.append(octet ^ 0xFF)
1983 for octet in octets:
1984 value = (value << 8) | octet
1988 for octet in bytearray(v):
1989 value = (value << 8) | octet
1991 value = int.from_bytes(v, byteorder="big", signed=True)
1993 obj = self.__class__(
1995 bounds=(self._bound_min, self._bound_max),
1998 default=self.default,
1999 optional=self.optional,
2001 _decoded=(offset, llen, l),
2003 except BoundsError as err:
2006 klass=self.__class__,
2007 decode_path=decode_path,
2013 return pp_console_row(next(self.pps()))
2015 def pps(self, decode_path=()):
2018 asn1_type_name=self.asn1_type_name,
2019 obj_name=self.__class__.__name__,
2020 decode_path=decode_path,
2021 value=(self.named or str(self._value)) if self.ready else None,
2022 optional=self.optional,
2023 default=self == self.default,
2024 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2025 expl=None if self._expl is None else tag_decode(self._expl),
2030 expl_offset=self.expl_offset if self.expled else None,
2031 expl_tlen=self.expl_tlen if self.expled else None,
2032 expl_llen=self.expl_llen if self.expled else None,
2033 expl_vlen=self.expl_vlen if self.expled else None,
2034 expl_lenindef=self.expl_lenindef,
2037 for pp in self.pps_lenindef(decode_path):
2041 class BitString(Obj):
2042 """``BIT STRING`` bit string type
2044 >>> BitString(b"hello world")
2045 BIT STRING 88 bits 68656c6c6f20776f726c64
2048 >>> b == b"hello world"
2053 >>> BitString("'0A3B5F291CD'H")
2054 BIT STRING 44 bits 0a3b5f291cd0
2055 >>> b = BitString("'010110000000'B")
2056 BIT STRING 12 bits 5800
2059 >>> b[0], b[1], b[2], b[3]
2060 (False, True, False, True)
2064 [False, True, False, True, True, False, False, False, False, False, False, False]
2068 class KeyUsage(BitString):
2070 ("digitalSignature", 0),
2071 ("nonRepudiation", 1),
2072 ("keyEncipherment", 2),
2075 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2076 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2078 ['nonRepudiation', 'keyEncipherment']
2080 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2084 Pay attention that BIT STRING can be encoded both in primitive
2085 and constructed forms. Decoder always checks constructed form tag
2086 additionally to specified primitive one. If BER decoding is
2087 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2088 of DER restrictions.
2090 __slots__ = ("tag_constructed", "specs", "defined")
2091 tag_default = tag_encode(3)
2092 asn1_type_name = "BIT STRING"
2105 :param value: set the value. Either binary type, tuple of named
2106 values (if ``schema`` is specified in the class),
2107 string in ``'XXX...'B`` form, or
2108 :py:class:`pyderasn.BitString` object
2109 :param bytes impl: override default tag with ``IMPLICIT`` one
2110 :param bytes expl: override default tag with ``EXPLICIT`` one
2111 :param default: set default value. Type same as in ``value``
2112 :param bool optional: is object ``OPTIONAL`` in sequence
2114 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2115 specs = getattr(self, "schema", {}) if _specs is None else _specs
2116 self.specs = specs if isinstance(specs, dict) else dict(specs)
2117 self._value = None if value is None else self._value_sanitize(value)
2118 if default is not None:
2119 default = self._value_sanitize(default)
2120 self.default = self.__class__(
2126 self._value = default
2128 tag_klass, _, tag_num = tag_decode(self.tag)
2129 self.tag_constructed = tag_encode(
2131 form=TagFormConstructed,
2135 def _bits2octets(self, bits):
2136 if len(self.specs) > 0:
2137 bits = bits.rstrip("0")
2139 bits += "0" * ((8 - (bit_len % 8)) % 8)
2140 octets = bytearray(len(bits) // 8)
2141 for i in six_xrange(len(octets)):
2142 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2143 return bit_len, bytes(octets)
2145 def _value_sanitize(self, value):
2146 if issubclass(value.__class__, BitString):
2148 if isinstance(value, (string_types, binary_type)):
2150 isinstance(value, string_types) and
2151 value.startswith("'")
2153 if value.endswith("'B"):
2155 if not set(value) <= set(("0", "1")):
2156 raise ValueError("B's coding contains unacceptable chars")
2157 return self._bits2octets(value)
2158 elif value.endswith("'H"):
2162 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2164 if isinstance(value, binary_type):
2165 return (len(value) * 8, value)
2167 raise InvalidValueType((self.__class__, string_types, binary_type))
2168 if isinstance(value, tuple):
2171 isinstance(value[0], integer_types) and
2172 isinstance(value[1], binary_type)
2177 bit = self.specs.get(name)
2179 raise ObjUnknown("BitString value: %s" % name)
2182 return self._bits2octets("")
2184 return self._bits2octets("".join(
2185 ("1" if bit in bits else "0")
2186 for bit in six_xrange(max(bits) + 1)
2188 raise InvalidValueType((self.__class__, binary_type, string_types))
2192 return self._value is not None
2195 obj = self.__class__(_specs=self.specs)
2197 if value is not None:
2198 value = (value[0], value[1])
2201 obj._expl = self._expl
2202 obj.default = self.default
2203 obj.optional = self.optional
2204 obj.offset = self.offset
2205 obj.llen = self.llen
2206 obj.vlen = self.vlen
2210 self._assert_ready()
2211 for i in six_xrange(self._value[0]):
2216 self._assert_ready()
2217 return self._value[0]
2219 def __bytes__(self):
2220 self._assert_ready()
2221 return self._value[1]
2223 def __eq__(self, their):
2224 if isinstance(their, bytes):
2225 return self._value[1] == their
2226 if not issubclass(their.__class__, BitString):
2229 self._value == their._value and
2230 self.tag == their.tag and
2231 self._expl == their._expl
2236 return [name for name, bit in self.specs.items() if self[bit]]
2246 return self.__class__(
2248 impl=self.tag if impl is None else impl,
2249 expl=self._expl if expl is None else expl,
2250 default=self.default if default is None else default,
2251 optional=self.optional if optional is None else optional,
2255 def __getitem__(self, key):
2256 if isinstance(key, int):
2257 bit_len, octets = self._value
2261 byte2int(memoryview(octets)[key // 8:]) >>
2264 if isinstance(key, string_types):
2265 value = self.specs.get(key)
2267 raise ObjUnknown("BitString value: %s" % key)
2269 raise InvalidValueType((int, str))
2272 self._assert_ready()
2273 bit_len, octets = self._value
2276 len_encode(len(octets) + 1),
2277 int2byte((8 - bit_len % 8) % 8),
2281 def _decode_chunk(self, lv, offset, decode_path, ctx):
2283 l, llen, v = len_decode(lv)
2284 except DecodeError as err:
2285 raise err.__class__(
2287 klass=self.__class__,
2288 decode_path=decode_path,
2292 raise NotEnoughData(
2293 "encoded length is longer than data",
2294 klass=self.__class__,
2295 decode_path=decode_path,
2299 raise NotEnoughData(
2301 klass=self.__class__,
2302 decode_path=decode_path,
2305 pad_size = byte2int(v)
2306 if l == 1 and pad_size != 0:
2308 "invalid empty value",
2309 klass=self.__class__,
2310 decode_path=decode_path,
2316 klass=self.__class__,
2317 decode_path=decode_path,
2320 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2323 klass=self.__class__,
2324 decode_path=decode_path,
2327 v, tail = v[:l], v[l:]
2328 obj = self.__class__(
2329 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2332 default=self.default,
2333 optional=self.optional,
2335 _decoded=(offset, llen, l),
2339 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2341 t, tlen, lv = tag_strip(tlv)
2342 except DecodeError as err:
2343 raise err.__class__(
2345 klass=self.__class__,
2346 decode_path=decode_path,
2350 if tag_only: # pragma: no cover
2352 return self._decode_chunk(lv, offset, decode_path, ctx)
2353 if t == self.tag_constructed:
2354 if not ctx.get("bered", False):
2356 "unallowed BER constructed encoding",
2357 klass=self.__class__,
2358 decode_path=decode_path,
2361 if tag_only: # pragma: no cover
2365 l, llen, v = len_decode(lv)
2366 except LenIndefForm:
2367 llen, l, v = 1, 0, lv[1:]
2369 except DecodeError as err:
2370 raise err.__class__(
2372 klass=self.__class__,
2373 decode_path=decode_path,
2377 raise NotEnoughData(
2378 "encoded length is longer than data",
2379 klass=self.__class__,
2380 decode_path=decode_path,
2383 if not lenindef and l == 0:
2384 raise NotEnoughData(
2386 klass=self.__class__,
2387 decode_path=decode_path,
2391 sub_offset = offset + tlen + llen
2395 if v[:EOC_LEN].tobytes() == EOC:
2402 "chunk out of bounds",
2403 klass=self.__class__,
2404 decode_path=decode_path + (str(len(chunks) - 1),),
2405 offset=chunks[-1].offset,
2407 sub_decode_path = decode_path + (str(len(chunks)),)
2409 chunk, v_tail = BitString().decode(
2412 decode_path=sub_decode_path,
2415 _ctx_immutable=False,
2419 "expected BitString encoded chunk",
2420 klass=self.__class__,
2421 decode_path=sub_decode_path,
2424 chunks.append(chunk)
2425 sub_offset += chunk.tlvlen
2426 vlen += chunk.tlvlen
2428 if len(chunks) == 0:
2431 klass=self.__class__,
2432 decode_path=decode_path,
2437 for chunk_i, chunk in enumerate(chunks[:-1]):
2438 if chunk.bit_len % 8 != 0:
2440 "BitString chunk is not multiple of 8 bits",
2441 klass=self.__class__,
2442 decode_path=decode_path + (str(chunk_i),),
2443 offset=chunk.offset,
2445 values.append(bytes(chunk))
2446 bit_len += chunk.bit_len
2447 chunk_last = chunks[-1]
2448 values.append(bytes(chunk_last))
2449 bit_len += chunk_last.bit_len
2450 obj = self.__class__(
2451 value=(bit_len, b"".join(values)),
2454 default=self.default,
2455 optional=self.optional,
2457 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2459 obj.lenindef = lenindef
2460 obj.ber_encoded = True
2461 return obj, (v[EOC_LEN:] if lenindef else v)
2463 klass=self.__class__,
2464 decode_path=decode_path,
2469 return pp_console_row(next(self.pps()))
2471 def pps(self, decode_path=()):
2475 bit_len, blob = self._value
2476 value = "%d bits" % bit_len
2477 if len(self.specs) > 0:
2478 blob = tuple(self.named)
2481 asn1_type_name=self.asn1_type_name,
2482 obj_name=self.__class__.__name__,
2483 decode_path=decode_path,
2486 optional=self.optional,
2487 default=self == self.default,
2488 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2489 expl=None if self._expl is None else tag_decode(self._expl),
2494 expl_offset=self.expl_offset if self.expled else None,
2495 expl_tlen=self.expl_tlen if self.expled else None,
2496 expl_llen=self.expl_llen if self.expled else None,
2497 expl_vlen=self.expl_vlen if self.expled else None,
2498 expl_lenindef=self.expl_lenindef,
2499 lenindef=self.lenindef,
2500 ber_encoded=self.ber_encoded,
2503 defined_by, defined = self.defined or (None, None)
2504 if defined_by is not None:
2506 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2508 for pp in self.pps_lenindef(decode_path):
2512 class OctetString(Obj):
2513 """``OCTET STRING`` binary string type
2515 >>> s = OctetString(b"hello world")
2516 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2517 >>> s == OctetString(b"hello world")
2522 >>> OctetString(b"hello", bounds=(4, 4))
2523 Traceback (most recent call last):
2524 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2525 >>> OctetString(b"hell", bounds=(4, 4))
2526 OCTET STRING 4 bytes 68656c6c
2530 Pay attention that OCTET STRING can be encoded both in primitive
2531 and constructed forms. Decoder always checks constructed form tag
2532 additionally to specified primitive one. If BER decoding is
2533 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2534 of DER restrictions.
2536 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2537 tag_default = tag_encode(4)
2538 asn1_type_name = "OCTET STRING"
2551 :param value: set the value. Either binary type, or
2552 :py:class:`pyderasn.OctetString` object
2553 :param bounds: set ``(MIN, MAX)`` value size constraint.
2554 (-inf, +inf) by default
2555 :param bytes impl: override default tag with ``IMPLICIT`` one
2556 :param bytes expl: override default tag with ``EXPLICIT`` one
2557 :param default: set default value. Type same as in ``value``
2558 :param bool optional: is object ``OPTIONAL`` in sequence
2560 super(OctetString, self).__init__(
2568 self._bound_min, self._bound_max = getattr(
2572 ) if bounds is None else bounds
2573 if value is not None:
2574 self._value = self._value_sanitize(value)
2575 if default is not None:
2576 default = self._value_sanitize(default)
2577 self.default = self.__class__(
2582 if self._value is None:
2583 self._value = default
2585 tag_klass, _, tag_num = tag_decode(self.tag)
2586 self.tag_constructed = tag_encode(
2588 form=TagFormConstructed,
2592 def _value_sanitize(self, value):
2593 if issubclass(value.__class__, OctetString):
2594 value = value._value
2595 elif isinstance(value, binary_type):
2598 raise InvalidValueType((self.__class__, bytes))
2599 if not self._bound_min <= len(value) <= self._bound_max:
2600 raise BoundsError(self._bound_min, len(value), self._bound_max)
2605 return self._value is not None
2608 obj = self.__class__()
2609 obj._value = self._value
2610 obj._bound_min = self._bound_min
2611 obj._bound_max = self._bound_max
2613 obj._expl = self._expl
2614 obj.default = self.default
2615 obj.optional = self.optional
2616 obj.offset = self.offset
2617 obj.llen = self.llen
2618 obj.vlen = self.vlen
2621 def __bytes__(self):
2622 self._assert_ready()
2625 def __eq__(self, their):
2626 if isinstance(their, binary_type):
2627 return self._value == their
2628 if not issubclass(their.__class__, OctetString):
2631 self._value == their._value and
2632 self.tag == their.tag and
2633 self._expl == their._expl
2636 def __lt__(self, their):
2637 return self._value < their._value
2648 return self.__class__(
2651 (self._bound_min, self._bound_max)
2652 if bounds is None else bounds
2654 impl=self.tag if impl is None else impl,
2655 expl=self._expl if expl is None else expl,
2656 default=self.default if default is None else default,
2657 optional=self.optional if optional is None else optional,
2661 self._assert_ready()
2664 len_encode(len(self._value)),
2668 def _decode_chunk(self, lv, offset, decode_path, ctx):
2670 l, llen, v = len_decode(lv)
2671 except DecodeError as err:
2672 raise err.__class__(
2674 klass=self.__class__,
2675 decode_path=decode_path,
2679 raise NotEnoughData(
2680 "encoded length is longer than data",
2681 klass=self.__class__,
2682 decode_path=decode_path,
2685 v, tail = v[:l], v[l:]
2687 obj = self.__class__(
2689 bounds=(self._bound_min, self._bound_max),
2692 default=self.default,
2693 optional=self.optional,
2694 _decoded=(offset, llen, l),
2696 except DecodeError as err:
2699 klass=self.__class__,
2700 decode_path=decode_path,
2703 except BoundsError as err:
2706 klass=self.__class__,
2707 decode_path=decode_path,
2712 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2714 t, tlen, lv = tag_strip(tlv)
2715 except DecodeError as err:
2716 raise err.__class__(
2718 klass=self.__class__,
2719 decode_path=decode_path,
2725 return self._decode_chunk(lv, offset, decode_path, ctx)
2726 if t == self.tag_constructed:
2727 if not ctx.get("bered", False):
2729 "unallowed BER constructed encoding",
2730 klass=self.__class__,
2731 decode_path=decode_path,
2738 l, llen, v = len_decode(lv)
2739 except LenIndefForm:
2740 llen, l, v = 1, 0, lv[1:]
2742 except DecodeError as err:
2743 raise err.__class__(
2745 klass=self.__class__,
2746 decode_path=decode_path,
2750 raise NotEnoughData(
2751 "encoded length is longer than data",
2752 klass=self.__class__,
2753 decode_path=decode_path,
2757 sub_offset = offset + tlen + llen
2761 if v[:EOC_LEN].tobytes() == EOC:
2768 "chunk out of bounds",
2769 klass=self.__class__,
2770 decode_path=decode_path + (str(len(chunks) - 1),),
2771 offset=chunks[-1].offset,
2773 sub_decode_path = decode_path + (str(len(chunks)),)
2775 chunk, v_tail = OctetString().decode(
2778 decode_path=sub_decode_path,
2781 _ctx_immutable=False,
2785 "expected OctetString encoded chunk",
2786 klass=self.__class__,
2787 decode_path=sub_decode_path,
2790 chunks.append(chunk)
2791 sub_offset += chunk.tlvlen
2792 vlen += chunk.tlvlen
2795 obj = self.__class__(
2796 value=b"".join(bytes(chunk) for chunk in chunks),
2797 bounds=(self._bound_min, self._bound_max),
2800 default=self.default,
2801 optional=self.optional,
2802 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2804 except DecodeError as err:
2807 klass=self.__class__,
2808 decode_path=decode_path,
2811 except BoundsError as err:
2814 klass=self.__class__,
2815 decode_path=decode_path,
2818 obj.lenindef = lenindef
2819 obj.ber_encoded = True
2820 return obj, (v[EOC_LEN:] if lenindef else v)
2822 klass=self.__class__,
2823 decode_path=decode_path,
2828 return pp_console_row(next(self.pps()))
2830 def pps(self, decode_path=()):
2833 asn1_type_name=self.asn1_type_name,
2834 obj_name=self.__class__.__name__,
2835 decode_path=decode_path,
2836 value=("%d bytes" % len(self._value)) if self.ready else None,
2837 blob=self._value if self.ready else None,
2838 optional=self.optional,
2839 default=self == self.default,
2840 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2841 expl=None if self._expl is None else tag_decode(self._expl),
2846 expl_offset=self.expl_offset if self.expled else None,
2847 expl_tlen=self.expl_tlen if self.expled else None,
2848 expl_llen=self.expl_llen if self.expled else None,
2849 expl_vlen=self.expl_vlen if self.expled else None,
2850 expl_lenindef=self.expl_lenindef,
2851 lenindef=self.lenindef,
2852 ber_encoded=self.ber_encoded,
2855 defined_by, defined = self.defined or (None, None)
2856 if defined_by is not None:
2858 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2860 for pp in self.pps_lenindef(decode_path):
2865 """``NULL`` null object
2873 tag_default = tag_encode(5)
2874 asn1_type_name = "NULL"
2878 value=None, # unused, but Sequence passes it
2885 :param bytes impl: override default tag with ``IMPLICIT`` one
2886 :param bytes expl: override default tag with ``EXPLICIT`` one
2887 :param bool optional: is object ``OPTIONAL`` in sequence
2889 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2897 obj = self.__class__()
2899 obj._expl = self._expl
2900 obj.default = self.default
2901 obj.optional = self.optional
2902 obj.offset = self.offset
2903 obj.llen = self.llen
2904 obj.vlen = self.vlen
2907 def __eq__(self, their):
2908 if not issubclass(their.__class__, Null):
2911 self.tag == their.tag and
2912 self._expl == their._expl
2922 return self.__class__(
2923 impl=self.tag if impl is None else impl,
2924 expl=self._expl if expl is None else expl,
2925 optional=self.optional if optional is None else optional,
2929 return self.tag + len_encode(0)
2931 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2933 t, _, lv = tag_strip(tlv)
2934 except DecodeError as err:
2935 raise err.__class__(
2937 klass=self.__class__,
2938 decode_path=decode_path,
2943 klass=self.__class__,
2944 decode_path=decode_path,
2947 if tag_only: # pragma: no cover
2950 l, _, v = len_decode(lv)
2951 except DecodeError as err:
2952 raise err.__class__(
2954 klass=self.__class__,
2955 decode_path=decode_path,
2959 raise InvalidLength(
2960 "Null must have zero length",
2961 klass=self.__class__,
2962 decode_path=decode_path,
2965 obj = self.__class__(
2968 optional=self.optional,
2969 _decoded=(offset, 1, 0),
2974 return pp_console_row(next(self.pps()))
2976 def pps(self, decode_path=()):
2979 asn1_type_name=self.asn1_type_name,
2980 obj_name=self.__class__.__name__,
2981 decode_path=decode_path,
2982 optional=self.optional,
2983 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2984 expl=None if self._expl is None else tag_decode(self._expl),
2989 expl_offset=self.expl_offset if self.expled else None,
2990 expl_tlen=self.expl_tlen if self.expled else None,
2991 expl_llen=self.expl_llen if self.expled else None,
2992 expl_vlen=self.expl_vlen if self.expled else None,
2993 expl_lenindef=self.expl_lenindef,
2996 for pp in self.pps_lenindef(decode_path):
3000 class ObjectIdentifier(Obj):
3001 """``OBJECT IDENTIFIER`` OID type
3003 >>> oid = ObjectIdentifier((1, 2, 3))
3004 OBJECT IDENTIFIER 1.2.3
3005 >>> oid == ObjectIdentifier("1.2.3")
3011 >>> oid + (4, 5) + ObjectIdentifier("1.7")
3012 OBJECT IDENTIFIER 1.2.3.4.5.1.7
3014 >>> str(ObjectIdentifier((3, 1)))
3015 Traceback (most recent call last):
3016 pyderasn.InvalidOID: unacceptable first arc value
3018 __slots__ = ("defines",)
3019 tag_default = tag_encode(6)
3020 asn1_type_name = "OBJECT IDENTIFIER"
3033 :param value: set the value. Either tuples of integers,
3034 string of "."-concatenated integers, or
3035 :py:class:`pyderasn.ObjectIdentifier` object
3036 :param defines: sequence of tuples. Each tuple has two elements.
3037 First one is relative to current one decode
3038 path, aiming to the field defined by that OID.
3039 Read about relative path in
3040 :py:func:`pyderasn.abs_decode_path`. Second
3041 tuple element is ``{OID: pyderasn.Obj()}``
3042 dictionary, mapping between current OID value
3043 and structure applied to defined field.
3044 :ref:`Read about DEFINED BY <definedby>`
3045 :param bytes impl: override default tag with ``IMPLICIT`` one
3046 :param bytes expl: override default tag with ``EXPLICIT`` one
3047 :param default: set default value. Type same as in ``value``
3048 :param bool optional: is object ``OPTIONAL`` in sequence
3050 super(ObjectIdentifier, self).__init__(
3058 if value is not None:
3059 self._value = self._value_sanitize(value)
3060 if default is not None:
3061 default = self._value_sanitize(default)
3062 self.default = self.__class__(
3067 if self._value is None:
3068 self._value = default
3069 self.defines = defines
3071 def __add__(self, their):
3072 if isinstance(their, self.__class__):
3073 return self.__class__(self._value + their._value)
3074 if isinstance(their, tuple):
3075 return self.__class__(self._value + their)
3076 raise InvalidValueType((self.__class__, tuple))
3078 def _value_sanitize(self, value):
3079 if issubclass(value.__class__, ObjectIdentifier):
3081 if isinstance(value, string_types):
3083 value = tuple(int(arc) for arc in value.split("."))
3085 raise InvalidOID("unacceptable arcs values")
3086 if isinstance(value, tuple):
3088 raise InvalidOID("less than 2 arcs")
3089 first_arc = value[0]
3090 if first_arc in (0, 1):
3091 if not (0 <= value[1] <= 39):
3092 raise InvalidOID("second arc is too wide")
3093 elif first_arc == 2:
3096 raise InvalidOID("unacceptable first arc value")
3098 raise InvalidValueType((self.__class__, str, tuple))
3102 return self._value is not None
3105 obj = self.__class__()
3106 obj._value = self._value
3107 obj.defines = self.defines
3109 obj._expl = self._expl
3110 obj.default = self.default
3111 obj.optional = self.optional
3112 obj.offset = self.offset
3113 obj.llen = self.llen
3114 obj.vlen = self.vlen
3118 self._assert_ready()
3119 return iter(self._value)
3122 return ".".join(str(arc) for arc in self._value or ())
3125 self._assert_ready()
3128 bytes(self._expl or b"") +
3129 str(self._value).encode("ascii"),
3132 def __eq__(self, their):
3133 if isinstance(their, tuple):
3134 return self._value == their
3135 if not issubclass(their.__class__, ObjectIdentifier):
3138 self.tag == their.tag and
3139 self._expl == their._expl and
3140 self._value == their._value
3143 def __lt__(self, their):
3144 return self._value < their._value
3155 return self.__class__(
3157 defines=self.defines if defines is None else defines,
3158 impl=self.tag if impl is None else impl,
3159 expl=self._expl if expl is None else expl,
3160 default=self.default if default is None else default,
3161 optional=self.optional if optional is None else optional,
3165 self._assert_ready()
3167 first_value = value[1]
3168 first_arc = value[0]
3171 elif first_arc == 1:
3173 elif first_arc == 2:
3175 else: # pragma: no cover
3176 raise RuntimeError("invalid arc is stored")
3177 octets = [zero_ended_encode(first_value)]
3178 for arc in value[2:]:
3179 octets.append(zero_ended_encode(arc))
3180 v = b"".join(octets)
3181 return b"".join((self.tag, len_encode(len(v)), v))
3183 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3185 t, _, lv = tag_strip(tlv)
3186 except DecodeError as err:
3187 raise err.__class__(
3189 klass=self.__class__,
3190 decode_path=decode_path,
3195 klass=self.__class__,
3196 decode_path=decode_path,
3199 if tag_only: # pragma: no cover
3202 l, llen, v = len_decode(lv)
3203 except DecodeError as err:
3204 raise err.__class__(
3206 klass=self.__class__,
3207 decode_path=decode_path,
3211 raise NotEnoughData(
3212 "encoded length is longer than data",
3213 klass=self.__class__,
3214 decode_path=decode_path,
3218 raise NotEnoughData(
3220 klass=self.__class__,
3221 decode_path=decode_path,
3224 v, tail = v[:l], v[l:]
3230 octet = indexbytes(v, i)
3231 if i == 0 and octet == 0x80 and not ctx.get("bered", False):
3232 raise DecodeError("non normalized arc encoding")
3233 arc = (arc << 7) | (octet & 0x7F)
3234 if octet & 0x80 == 0:
3242 klass=self.__class__,
3243 decode_path=decode_path,
3247 second_arc = arcs[0]
3248 if 0 <= second_arc <= 39:
3250 elif 40 <= second_arc <= 79:
3256 obj = self.__class__(
3257 value=tuple([first_arc, second_arc] + arcs[1:]),
3260 default=self.default,
3261 optional=self.optional,
3262 _decoded=(offset, llen, l),
3267 return pp_console_row(next(self.pps()))
3269 def pps(self, decode_path=()):
3272 asn1_type_name=self.asn1_type_name,
3273 obj_name=self.__class__.__name__,
3274 decode_path=decode_path,
3275 value=str(self) if self.ready else None,
3276 optional=self.optional,
3277 default=self == self.default,
3278 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3279 expl=None if self._expl is None else tag_decode(self._expl),
3284 expl_offset=self.expl_offset if self.expled else None,
3285 expl_tlen=self.expl_tlen if self.expled else None,
3286 expl_llen=self.expl_llen if self.expled else None,
3287 expl_vlen=self.expl_vlen if self.expled else None,
3288 expl_lenindef=self.expl_lenindef,
3291 for pp in self.pps_lenindef(decode_path):
3295 class Enumerated(Integer):
3296 """``ENUMERATED`` integer type
3298 This type is identical to :py:class:`pyderasn.Integer`, but requires
3299 schema to be specified and does not accept values missing from it.
3302 tag_default = tag_encode(10)
3303 asn1_type_name = "ENUMERATED"
3314 bounds=None, # dummy argument, workability for Integer.decode
3316 super(Enumerated, self).__init__(
3325 if len(self.specs) == 0:
3326 raise ValueError("schema must be specified")
3328 def _value_sanitize(self, value):
3329 if isinstance(value, self.__class__):
3330 value = value._value
3331 elif isinstance(value, integer_types):
3332 if value not in list(self.specs.values()):
3334 "unknown integer value: %s" % value,
3335 klass=self.__class__,
3337 elif isinstance(value, string_types):
3338 value = self.specs.get(value)
3340 raise ObjUnknown("integer value: %s" % value)
3342 raise InvalidValueType((self.__class__, int, str))
3346 obj = self.__class__(_specs=self.specs)
3347 obj._value = self._value
3348 obj._bound_min = self._bound_min
3349 obj._bound_max = self._bound_max
3351 obj._expl = self._expl
3352 obj.default = self.default
3353 obj.optional = self.optional
3354 obj.offset = self.offset
3355 obj.llen = self.llen
3356 obj.vlen = self.vlen
3368 return self.__class__(
3370 impl=self.tag if impl is None else impl,
3371 expl=self._expl if expl is None else expl,
3372 default=self.default if default is None else default,
3373 optional=self.optional if optional is None else optional,
3378 class CommonString(OctetString):
3379 """Common class for all strings
3381 Everything resembles :py:class:`pyderasn.OctetString`, except
3382 ability to deal with unicode text strings.
3384 >>> hexenc("привет мир".encode("utf-8"))
3385 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3386 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3388 >>> s = UTF8String("привет мир")
3389 UTF8String UTF8String привет мир
3391 'привет мир'
3392 >>> hexenc(bytes(s))
3393 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3395 >>> PrintableString("привет мир")
3396 Traceback (most recent call last):
3397 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3399 >>> BMPString("ада", bounds=(2, 2))
3400 Traceback (most recent call last):
3401 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3402 >>> s = BMPString("ад", bounds=(2, 2))
3405 >>> hexenc(bytes(s))
3413 * - :py:class:`pyderasn.UTF8String`
3415 * - :py:class:`pyderasn.NumericString`
3417 * - :py:class:`pyderasn.PrintableString`
3419 * - :py:class:`pyderasn.TeletexString`
3421 * - :py:class:`pyderasn.T61String`
3423 * - :py:class:`pyderasn.VideotexString`
3425 * - :py:class:`pyderasn.IA5String`
3427 * - :py:class:`pyderasn.GraphicString`
3429 * - :py:class:`pyderasn.VisibleString`
3431 * - :py:class:`pyderasn.ISO646String`
3433 * - :py:class:`pyderasn.GeneralString`
3435 * - :py:class:`pyderasn.UniversalString`
3437 * - :py:class:`pyderasn.BMPString`
3440 __slots__ = ("encoding",)
3442 def _value_sanitize(self, value):
3444 value_decoded = None
3445 if isinstance(value, self.__class__):
3446 value_raw = value._value
3447 elif isinstance(value, text_type):
3448 value_decoded = value
3449 elif isinstance(value, binary_type):
3452 raise InvalidValueType((self.__class__, text_type, binary_type))
3455 value_decoded.encode(self.encoding)
3456 if value_raw is None else value_raw
3459 value_raw.decode(self.encoding)
3460 if value_decoded is None else value_decoded
3462 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3463 raise DecodeError(str(err))
3464 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3472 def __eq__(self, their):
3473 if isinstance(their, binary_type):
3474 return self._value == their
3475 if isinstance(their, text_type):
3476 return self._value == their.encode(self.encoding)
3477 if not isinstance(their, self.__class__):
3480 self._value == their._value and
3481 self.tag == their.tag and
3482 self._expl == their._expl
3485 def __unicode__(self):
3487 return self._value.decode(self.encoding)
3488 return text_type(self._value)
3491 return pp_console_row(next(self.pps(no_unicode=PY2)))
3493 def pps(self, decode_path=(), no_unicode=False):
3496 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3499 asn1_type_name=self.asn1_type_name,
3500 obj_name=self.__class__.__name__,
3501 decode_path=decode_path,
3503 optional=self.optional,
3504 default=self == self.default,
3505 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3506 expl=None if self._expl is None else tag_decode(self._expl),
3511 expl_offset=self.expl_offset if self.expled else None,
3512 expl_tlen=self.expl_tlen if self.expled else None,
3513 expl_llen=self.expl_llen if self.expled else None,
3514 expl_vlen=self.expl_vlen if self.expled else None,
3515 expl_lenindef=self.expl_lenindef,
3516 ber_encoded=self.ber_encoded,
3519 for pp in self.pps_lenindef(decode_path):
3523 class UTF8String(CommonString):
3525 tag_default = tag_encode(12)
3527 asn1_type_name = "UTF8String"
3530 class AllowableCharsMixin(object):
3532 def allowable_chars(self):
3534 return self._allowable_chars
3535 return set(six_unichr(c) for c in self._allowable_chars)
3538 class NumericString(AllowableCharsMixin, CommonString):
3541 Its value is properly sanitized: only ASCII digits with spaces can
3544 >>> NumericString().allowable_chars
3545 set(['3', '4', '7', '5', '1', '0', '8', '9', ' ', '6', '2'])
3548 tag_default = tag_encode(18)
3550 asn1_type_name = "NumericString"
3551 _allowable_chars = set(digits.encode("ascii") + b" ")
3553 def _value_sanitize(self, value):
3554 value = super(NumericString, self)._value_sanitize(value)
3555 if not set(value) <= self._allowable_chars:
3556 raise DecodeError("non-numeric value")
3560 class PrintableString(AllowableCharsMixin, CommonString):
3563 Its value is properly sanitized: see X.680 41.4 table 10.
3565 >>> PrintableString().allowable_chars
3566 >>> set([' ', "'", ..., 'z'])
3569 tag_default = tag_encode(19)
3571 asn1_type_name = "PrintableString"
3572 _allowable_chars = set(
3573 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3576 def _value_sanitize(self, value):
3577 value = super(PrintableString, self)._value_sanitize(value)
3578 if not set(value) <= self._allowable_chars:
3579 raise DecodeError("non-printable value")
3583 class TeletexString(CommonString):
3585 tag_default = tag_encode(20)
3587 asn1_type_name = "TeletexString"
3590 class T61String(TeletexString):
3592 asn1_type_name = "T61String"
3595 class VideotexString(CommonString):
3597 tag_default = tag_encode(21)
3598 encoding = "iso-8859-1"
3599 asn1_type_name = "VideotexString"
3602 class IA5String(CommonString):
3604 tag_default = tag_encode(22)
3606 asn1_type_name = "IA5"
3609 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3610 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3611 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3614 class UTCTime(CommonString):
3615 """``UTCTime`` datetime type
3617 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3618 UTCTime UTCTime 2017-09-30T22:07:50
3624 datetime.datetime(2017, 9, 30, 22, 7, 50)
3625 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3626 datetime.datetime(1957, 9, 30, 22, 7, 50)
3629 tag_default = tag_encode(23)
3631 asn1_type_name = "UTCTime"
3633 fmt = "%y%m%d%H%M%SZ"
3643 bounds=None, # dummy argument, workability for OctetString.decode
3646 :param value: set the value. Either datetime type, or
3647 :py:class:`pyderasn.UTCTime` object
3648 :param bytes impl: override default tag with ``IMPLICIT`` one
3649 :param bytes expl: override default tag with ``EXPLICIT`` one
3650 :param default: set default value. Type same as in ``value``
3651 :param bool optional: is object ``OPTIONAL`` in sequence
3653 super(UTCTime, self).__init__(
3661 if value is not None:
3662 self._value = self._value_sanitize(value)
3663 if default is not None:
3664 default = self._value_sanitize(default)
3665 self.default = self.__class__(
3670 if self._value is None:
3671 self._value = default
3673 def _value_sanitize(self, value):
3674 if isinstance(value, self.__class__):
3676 if isinstance(value, datetime):
3677 return value.strftime(self.fmt).encode("ascii")
3678 if isinstance(value, binary_type):
3680 value_decoded = value.decode("ascii")
3681 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3682 raise DecodeError("invalid UTCTime encoding")
3683 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3685 datetime.strptime(value_decoded, self.fmt)
3686 except (TypeError, ValueError):
3687 raise DecodeError("invalid UTCTime format")
3690 raise DecodeError("invalid UTCTime length")
3691 raise InvalidValueType((self.__class__, datetime))
3693 def __eq__(self, their):
3694 if isinstance(their, binary_type):
3695 return self._value == their
3696 if isinstance(their, datetime):
3697 return self.todatetime() == their
3698 if not isinstance(their, self.__class__):
3701 self._value == their._value and
3702 self.tag == their.tag and
3703 self._expl == their._expl
3706 def todatetime(self):
3707 """Convert to datetime
3711 Pay attention that UTCTime can not hold full year, so all years
3712 having < 50 years are treated as 20xx, 19xx otherwise, according
3713 to X.509 recomendation.
3715 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3716 year = value.year % 100
3718 year=(2000 + year) if year < 50 else (1900 + year),
3722 minute=value.minute,
3723 second=value.second,
3727 return pp_console_row(next(self.pps()))
3729 def pps(self, decode_path=()):
3732 asn1_type_name=self.asn1_type_name,
3733 obj_name=self.__class__.__name__,
3734 decode_path=decode_path,
3735 value=self.todatetime().isoformat() if self.ready else None,
3736 optional=self.optional,
3737 default=self == self.default,
3738 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3739 expl=None if self._expl is None else tag_decode(self._expl),
3744 expl_offset=self.expl_offset if self.expled else None,
3745 expl_tlen=self.expl_tlen if self.expled else None,
3746 expl_llen=self.expl_llen if self.expled else None,
3747 expl_vlen=self.expl_vlen if self.expled else None,
3748 expl_lenindef=self.expl_lenindef,
3749 ber_encoded=self.ber_encoded,
3752 for pp in self.pps_lenindef(decode_path):
3756 class GeneralizedTime(UTCTime):
3757 """``GeneralizedTime`` datetime type
3759 This type is similar to :py:class:`pyderasn.UTCTime`.
3761 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3762 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3764 '20170930220750.000123Z'
3765 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3766 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3769 tag_default = tag_encode(24)
3770 asn1_type_name = "GeneralizedTime"
3772 fmt = "%Y%m%d%H%M%SZ"
3773 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3775 def _value_sanitize(self, value):
3776 if isinstance(value, self.__class__):
3778 if isinstance(value, datetime):
3779 return value.strftime(
3780 self.fmt_ms if value.microsecond > 0 else self.fmt
3782 if isinstance(value, binary_type):
3784 value_decoded = value.decode("ascii")
3785 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3786 raise DecodeError("invalid GeneralizedTime encoding")
3787 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3789 datetime.strptime(value_decoded, self.fmt)
3790 except (TypeError, ValueError):
3792 "invalid GeneralizedTime (without ms) format",
3795 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3797 datetime.strptime(value_decoded, self.fmt_ms)
3798 except (TypeError, ValueError):
3800 "invalid GeneralizedTime (with ms) format",
3805 "invalid GeneralizedTime length",
3806 klass=self.__class__,
3808 raise InvalidValueType((self.__class__, datetime))
3810 def todatetime(self):
3811 value = self._value.decode("ascii")
3812 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3813 return datetime.strptime(value, self.fmt)
3814 return datetime.strptime(value, self.fmt_ms)
3817 class GraphicString(CommonString):
3819 tag_default = tag_encode(25)
3820 encoding = "iso-8859-1"
3821 asn1_type_name = "GraphicString"
3824 class VisibleString(CommonString):
3826 tag_default = tag_encode(26)
3828 asn1_type_name = "VisibleString"
3831 class ISO646String(VisibleString):
3833 asn1_type_name = "ISO646String"
3836 class GeneralString(CommonString):
3838 tag_default = tag_encode(27)
3839 encoding = "iso-8859-1"
3840 asn1_type_name = "GeneralString"
3843 class UniversalString(CommonString):
3845 tag_default = tag_encode(28)
3846 encoding = "utf-32-be"
3847 asn1_type_name = "UniversalString"
3850 class BMPString(CommonString):
3852 tag_default = tag_encode(30)
3853 encoding = "utf-16-be"
3854 asn1_type_name = "BMPString"
3858 """``CHOICE`` special type
3862 class GeneralName(Choice):
3864 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3865 ("dNSName", IA5String(impl=tag_ctxp(2))),
3868 >>> gn = GeneralName()
3870 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3871 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3872 >>> gn["dNSName"] = IA5String("bar.baz")
3873 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3874 >>> gn["rfc822Name"]
3877 [2] IA5String IA5 bar.baz
3880 >>> gn.value == gn["dNSName"]
3883 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3885 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3886 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3888 __slots__ = ("specs",)
3890 asn1_type_name = "CHOICE"
3903 :param value: set the value. Either ``(choice, value)`` tuple, or
3904 :py:class:`pyderasn.Choice` object
3905 :param bytes impl: can not be set, do **not** use it
3906 :param bytes expl: override default tag with ``EXPLICIT`` one
3907 :param default: set default value. Type same as in ``value``
3908 :param bool optional: is object ``OPTIONAL`` in sequence
3910 if impl is not None:
3911 raise ValueError("no implicit tag allowed for CHOICE")
3912 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3914 schema = getattr(self, "schema", ())
3915 if len(schema) == 0:
3916 raise ValueError("schema must be specified")
3918 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3921 if value is not None:
3922 self._value = self._value_sanitize(value)
3923 if default is not None:
3924 default_value = self._value_sanitize(default)
3925 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3926 default_obj.specs = self.specs
3927 default_obj._value = default_value
3928 self.default = default_obj
3930 self._value = default_obj.copy()._value
3932 def _value_sanitize(self, value):
3933 if isinstance(value, self.__class__):
3935 if isinstance(value, tuple) and len(value) == 2:
3937 spec = self.specs.get(choice)
3939 raise ObjUnknown(choice)
3940 if not isinstance(obj, spec.__class__):
3941 raise InvalidValueType((spec,))
3942 return (choice, spec(obj))
3943 raise InvalidValueType((self.__class__, tuple))
3947 return self._value is not None and self._value[1].ready
3951 return self.expl_lenindef or (
3952 (self._value is not None) and
3953 self._value[1].bered
3957 obj = self.__class__(schema=self.specs)
3958 obj._expl = self._expl
3959 obj.default = self.default
3960 obj.optional = self.optional
3961 obj.offset = self.offset
3962 obj.llen = self.llen
3963 obj.vlen = self.vlen
3965 if value is not None:
3966 obj._value = (value[0], value[1].copy())
3969 def __eq__(self, their):
3970 if isinstance(their, tuple) and len(their) == 2:
3971 return self._value == their
3972 if not isinstance(their, self.__class__):
3975 self.specs == their.specs and
3976 self._value == their._value
3986 return self.__class__(
3989 expl=self._expl if expl is None else expl,
3990 default=self.default if default is None else default,
3991 optional=self.optional if optional is None else optional,
3996 self._assert_ready()
3997 return self._value[0]
4001 self._assert_ready()
4002 return self._value[1]
4004 def __getitem__(self, key):
4005 if key not in self.specs:
4006 raise ObjUnknown(key)
4007 if self._value is None:
4009 choice, value = self._value
4014 def __setitem__(self, key, value):
4015 spec = self.specs.get(key)
4017 raise ObjUnknown(key)
4018 if not isinstance(value, spec.__class__):
4019 raise InvalidValueType((spec.__class__,))
4020 self._value = (key, spec(value))
4028 return self._value[1].decoded if self.ready else False
4031 self._assert_ready()
4032 return self._value[1].encode()
4034 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4035 for choice, spec in self.specs.items():
4036 sub_decode_path = decode_path + (choice,)
4042 decode_path=sub_decode_path,
4045 _ctx_immutable=False,
4052 klass=self.__class__,
4053 decode_path=decode_path,
4056 if tag_only: # pragma: no cover
4058 value, tail = spec.decode(
4062 decode_path=sub_decode_path,
4064 _ctx_immutable=False,
4066 obj = self.__class__(
4069 default=self.default,
4070 optional=self.optional,
4071 _decoded=(offset, 0, value.fulllen),
4073 obj._value = (choice, value)
4077 value = pp_console_row(next(self.pps()))
4079 value = "%s[%r]" % (value, self.value)
4082 def pps(self, decode_path=()):
4085 asn1_type_name=self.asn1_type_name,
4086 obj_name=self.__class__.__name__,
4087 decode_path=decode_path,
4088 value=self.choice if self.ready else None,
4089 optional=self.optional,
4090 default=self == self.default,
4091 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4092 expl=None if self._expl is None else tag_decode(self._expl),
4097 expl_lenindef=self.expl_lenindef,
4101 yield self.value.pps(decode_path=decode_path + (self.choice,))
4102 for pp in self.pps_lenindef(decode_path):
4106 class PrimitiveTypes(Choice):
4107 """Predefined ``CHOICE`` for all generic primitive types
4109 It could be useful for general decoding of some unspecified values:
4111 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4112 OCTET STRING 3 bytes 666f6f
4113 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4117 schema = tuple((klass.__name__, klass()) for klass in (
4142 """``ANY`` special type
4144 >>> Any(Integer(-123))
4146 >>> a = Any(OctetString(b"hello world").encode())
4147 ANY 040b68656c6c6f20776f726c64
4148 >>> hexenc(bytes(a))
4149 b'0x040x0bhello world'
4151 __slots__ = ("defined",)
4152 tag_default = tag_encode(0)
4153 asn1_type_name = "ANY"
4163 :param value: set the value. Either any kind of pyderasn's
4164 **ready** object, or bytes. Pay attention that
4165 **no** validation is performed is raw binary value
4167 :param bytes expl: override default tag with ``EXPLICIT`` one
4168 :param bool optional: is object ``OPTIONAL`` in sequence
4170 super(Any, self).__init__(None, expl, None, optional, _decoded)
4171 self._value = None if value is None else self._value_sanitize(value)
4174 def _value_sanitize(self, value):
4175 if isinstance(value, self.__class__):
4177 if isinstance(value, Obj):
4178 return value.encode()
4179 if isinstance(value, binary_type):
4181 raise InvalidValueType((self.__class__, Obj, binary_type))
4185 return self._value is not None
4189 if self.expl_lenindef or self.lenindef:
4191 if self.defined is None:
4193 return self.defined[1].bered
4196 obj = self.__class__()
4197 obj._value = self._value
4199 obj._expl = self._expl
4200 obj.optional = self.optional
4201 obj.offset = self.offset
4202 obj.llen = self.llen
4203 obj.vlen = self.vlen
4206 def __eq__(self, their):
4207 if isinstance(their, binary_type):
4208 return self._value == their
4209 if issubclass(their.__class__, Any):
4210 return self._value == their._value
4219 return self.__class__(
4221 expl=self._expl if expl is None else expl,
4222 optional=self.optional if optional is None else optional,
4225 def __bytes__(self):
4226 self._assert_ready()
4234 self._assert_ready()
4237 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4239 t, tlen, lv = tag_strip(tlv)
4240 except DecodeError as err:
4241 raise err.__class__(
4243 klass=self.__class__,
4244 decode_path=decode_path,
4248 l, llen, v = len_decode(lv)
4249 except LenIndefForm as err:
4250 if not ctx.get("bered", False):
4251 raise err.__class__(
4253 klass=self.__class__,
4254 decode_path=decode_path,
4257 llen, vlen, v = 1, 0, lv[1:]
4258 sub_offset = offset + tlen + llen
4260 while v[:EOC_LEN].tobytes() != EOC:
4261 chunk, v = Any().decode(
4264 decode_path=decode_path + (str(chunk_i),),
4267 _ctx_immutable=False,
4269 vlen += chunk.tlvlen
4270 sub_offset += chunk.tlvlen
4272 tlvlen = tlen + llen + vlen + EOC_LEN
4273 obj = self.__class__(
4274 value=tlv[:tlvlen].tobytes(),
4276 optional=self.optional,
4277 _decoded=(offset, 0, tlvlen),
4281 return obj, v[EOC_LEN:]
4282 except DecodeError as err:
4283 raise err.__class__(
4285 klass=self.__class__,
4286 decode_path=decode_path,
4290 raise NotEnoughData(
4291 "encoded length is longer than data",
4292 klass=self.__class__,
4293 decode_path=decode_path,
4296 tlvlen = tlen + llen + l
4297 v, tail = tlv[:tlvlen], v[l:]
4298 obj = self.__class__(
4301 optional=self.optional,
4302 _decoded=(offset, 0, tlvlen),
4308 return pp_console_row(next(self.pps()))
4310 def pps(self, decode_path=()):
4313 asn1_type_name=self.asn1_type_name,
4314 obj_name=self.__class__.__name__,
4315 decode_path=decode_path,
4316 blob=self._value if self.ready else None,
4317 optional=self.optional,
4318 default=self == self.default,
4319 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4320 expl=None if self._expl is None else tag_decode(self._expl),
4325 expl_offset=self.expl_offset if self.expled else None,
4326 expl_tlen=self.expl_tlen if self.expled else None,
4327 expl_llen=self.expl_llen if self.expled else None,
4328 expl_vlen=self.expl_vlen if self.expled else None,
4329 expl_lenindef=self.expl_lenindef,
4330 lenindef=self.lenindef,
4333 defined_by, defined = self.defined or (None, None)
4334 if defined_by is not None:
4336 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4338 for pp in self.pps_lenindef(decode_path):
4342 ########################################################################
4343 # ASN.1 constructed types
4344 ########################################################################
4346 def get_def_by_path(defines_by_path, sub_decode_path):
4347 """Get define by decode path
4349 for path, define in defines_by_path:
4350 if len(path) != len(sub_decode_path):
4352 for p1, p2 in zip(path, sub_decode_path):
4353 if (p1 != any) and (p1 != p2):
4359 def abs_decode_path(decode_path, rel_path):
4360 """Create an absolute decode path from current and relative ones
4362 :param decode_path: current decode path, starting point. Tuple of strings
4363 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4364 If first tuple's element is "/", then treat it as
4365 an absolute path, ignoring ``decode_path`` as
4366 starting point. Also this tuple can contain ".."
4367 elements, stripping the leading element from
4370 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4371 ("foo", "bar", "baz", "whatever")
4372 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4374 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4377 if rel_path[0] == "/":
4379 if rel_path[0] == "..":
4380 return abs_decode_path(decode_path[:-1], rel_path[1:])
4381 return decode_path + rel_path
4384 class Sequence(Obj):
4385 """``SEQUENCE`` structure type
4387 You have to make specification of sequence::
4389 class Extension(Sequence):
4391 ("extnID", ObjectIdentifier()),
4392 ("critical", Boolean(default=False)),
4393 ("extnValue", OctetString()),
4396 Then, you can work with it as with dictionary.
4398 >>> ext = Extension()
4399 >>> Extension().specs
4401 ('extnID', OBJECT IDENTIFIER),
4402 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4403 ('extnValue', OCTET STRING),
4405 >>> ext["extnID"] = "1.2.3"
4406 Traceback (most recent call last):
4407 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4408 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4410 You can determine if sequence is ready to be encoded:
4415 Traceback (most recent call last):
4416 pyderasn.ObjNotReady: object is not ready: extnValue
4417 >>> ext["extnValue"] = OctetString(b"foobar")
4421 Value you want to assign, must have the same **type** as in
4422 corresponding specification, but it can have different tags,
4423 optional/default attributes -- they will be taken from specification
4426 class TBSCertificate(Sequence):
4428 ("version", Version(expl=tag_ctxc(0), default="v1")),
4431 >>> tbs = TBSCertificate()
4432 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4434 Assign ``None`` to remove value from sequence.
4436 You can set values in Sequence during its initialization:
4438 >>> AlgorithmIdentifier((
4439 ("algorithm", ObjectIdentifier("1.2.3")),
4440 ("parameters", Any(Null()))
4442 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4444 You can determine if value exists/set in the sequence and take its value:
4446 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4449 OBJECT IDENTIFIER 1.2.3
4451 But pay attention that if value has default, then it won't be (not
4452 in) in the sequence (because ``DEFAULT`` must not be encoded in
4453 DER), but you can read its value:
4455 >>> "critical" in ext, ext["critical"]
4456 (False, BOOLEAN False)
4457 >>> ext["critical"] = Boolean(True)
4458 >>> "critical" in ext, ext["critical"]
4459 (True, BOOLEAN True)
4461 All defaulted values are always optional.
4463 .. _allow_default_values_ctx:
4465 DER prohibits default value encoding and will raise an error if
4466 default value is unexpectedly met during decode.
4467 If :ref:`bered <bered_ctx>` context option is set, then no error
4468 will be raised, but ``bered`` attribute set. You can disable strict
4469 defaulted values existence validation by setting
4470 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4472 Two sequences are equal if they have equal specification (schema),
4473 implicit/explicit tagging and the same values.
4475 __slots__ = ("specs",)
4476 tag_default = tag_encode(form=TagFormConstructed, num=16)
4477 asn1_type_name = "SEQUENCE"
4489 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4491 schema = getattr(self, "schema", ())
4493 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4496 if value is not None:
4497 if issubclass(value.__class__, Sequence):
4498 self._value = value._value
4499 elif hasattr(value, "__iter__"):
4500 for seq_key, seq_value in value:
4501 self[seq_key] = seq_value
4503 raise InvalidValueType((Sequence,))
4504 if default is not None:
4505 if not issubclass(default.__class__, Sequence):
4506 raise InvalidValueType((Sequence,))
4507 default_value = default._value
4508 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4509 default_obj.specs = self.specs
4510 default_obj._value = default_value
4511 self.default = default_obj
4513 self._value = default_obj.copy()._value
4517 for name, spec in self.specs.items():
4518 value = self._value.get(name)
4530 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4532 return any(value.bered for value in self._value.values())
4535 obj = self.__class__(schema=self.specs)
4537 obj._expl = self._expl
4538 obj.default = self.default
4539 obj.optional = self.optional
4540 obj.offset = self.offset
4541 obj.llen = self.llen
4542 obj.vlen = self.vlen
4543 obj._value = {k: v.copy() for k, v in self._value.items()}
4546 def __eq__(self, their):
4547 if not isinstance(their, self.__class__):
4550 self.specs == their.specs and
4551 self.tag == their.tag and
4552 self._expl == their._expl and
4553 self._value == their._value
4564 return self.__class__(
4567 impl=self.tag if impl is None else impl,
4568 expl=self._expl if expl is None else expl,
4569 default=self.default if default is None else default,
4570 optional=self.optional if optional is None else optional,
4573 def __contains__(self, key):
4574 return key in self._value
4576 def __setitem__(self, key, value):
4577 spec = self.specs.get(key)
4579 raise ObjUnknown(key)
4581 self._value.pop(key, None)
4583 if not isinstance(value, spec.__class__):
4584 raise InvalidValueType((spec.__class__,))
4585 value = spec(value=value)
4586 if spec.default is not None and value == spec.default:
4587 self._value.pop(key, None)
4589 self._value[key] = value
4591 def __getitem__(self, key):
4592 value = self._value.get(key)
4593 if value is not None:
4595 spec = self.specs.get(key)
4597 raise ObjUnknown(key)
4598 if spec.default is not None:
4602 def _encoded_values(self):
4604 for name, spec in self.specs.items():
4605 value = self._value.get(name)
4609 raise ObjNotReady(name)
4610 raws.append(value.encode())
4614 v = b"".join(self._encoded_values())
4615 return b"".join((self.tag, len_encode(len(v)), v))
4617 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4619 t, tlen, lv = tag_strip(tlv)
4620 except DecodeError as err:
4621 raise err.__class__(
4623 klass=self.__class__,
4624 decode_path=decode_path,
4629 klass=self.__class__,
4630 decode_path=decode_path,
4633 if tag_only: # pragma: no cover
4636 ctx_bered = ctx.get("bered", False)
4638 l, llen, v = len_decode(lv)
4639 except LenIndefForm as err:
4641 raise err.__class__(
4643 klass=self.__class__,
4644 decode_path=decode_path,
4647 l, llen, v = 0, 1, lv[1:]
4649 except DecodeError as err:
4650 raise err.__class__(
4652 klass=self.__class__,
4653 decode_path=decode_path,
4657 raise NotEnoughData(
4658 "encoded length is longer than data",
4659 klass=self.__class__,
4660 decode_path=decode_path,
4664 v, tail = v[:l], v[l:]
4666 sub_offset = offset + tlen + llen
4669 ctx_allow_default_values = ctx.get("allow_default_values", False)
4670 for name, spec in self.specs.items():
4671 if spec.optional and (
4672 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4676 sub_decode_path = decode_path + (name,)
4678 value, v_tail = spec.decode(
4682 decode_path=sub_decode_path,
4684 _ctx_immutable=False,
4691 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4692 if defined is not None:
4693 defined_by, defined_spec = defined
4694 if issubclass(value.__class__, SequenceOf):
4695 for i, _value in enumerate(value):
4696 sub_sub_decode_path = sub_decode_path + (
4698 DecodePathDefBy(defined_by),
4700 defined_value, defined_tail = defined_spec.decode(
4701 memoryview(bytes(_value)),
4703 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4704 if value.expled else (value.tlen + value.llen)
4707 decode_path=sub_sub_decode_path,
4709 _ctx_immutable=False,
4711 if len(defined_tail) > 0:
4714 klass=self.__class__,
4715 decode_path=sub_sub_decode_path,
4718 _value.defined = (defined_by, defined_value)
4720 defined_value, defined_tail = defined_spec.decode(
4721 memoryview(bytes(value)),
4723 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4724 if value.expled else (value.tlen + value.llen)
4727 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4729 _ctx_immutable=False,
4731 if len(defined_tail) > 0:
4734 klass=self.__class__,
4735 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4738 value.defined = (defined_by, defined_value)
4740 value_len = value.fulllen
4742 sub_offset += value_len
4744 if spec.default is not None and value == spec.default:
4745 if ctx_bered or ctx_allow_default_values:
4749 "DEFAULT value met",
4750 klass=self.__class__,
4751 decode_path=sub_decode_path,
4754 values[name] = value
4756 spec_defines = getattr(spec, "defines", ())
4757 if len(spec_defines) == 0:
4758 defines_by_path = ctx.get("defines_by_path", ())
4759 if len(defines_by_path) > 0:
4760 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4761 if spec_defines is not None and len(spec_defines) > 0:
4762 for rel_path, schema in spec_defines:
4763 defined = schema.get(value, None)
4764 if defined is not None:
4765 ctx.setdefault("_defines", []).append((
4766 abs_decode_path(sub_decode_path[:-1], rel_path),
4770 if v[:EOC_LEN].tobytes() != EOC:
4773 klass=self.__class__,
4774 decode_path=decode_path,
4782 klass=self.__class__,
4783 decode_path=decode_path,
4786 obj = self.__class__(
4790 default=self.default,
4791 optional=self.optional,
4792 _decoded=(offset, llen, vlen),
4795 obj.lenindef = lenindef
4796 obj.ber_encoded = ber_encoded
4800 value = pp_console_row(next(self.pps()))
4802 for name in self.specs:
4803 _value = self._value.get(name)
4806 cols.append("%s: %s" % (name, repr(_value)))
4807 return "%s[%s]" % (value, "; ".join(cols))
4809 def pps(self, decode_path=()):
4812 asn1_type_name=self.asn1_type_name,
4813 obj_name=self.__class__.__name__,
4814 decode_path=decode_path,
4815 optional=self.optional,
4816 default=self == self.default,
4817 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4818 expl=None if self._expl is None else tag_decode(self._expl),
4823 expl_offset=self.expl_offset if self.expled else None,
4824 expl_tlen=self.expl_tlen if self.expled else None,
4825 expl_llen=self.expl_llen if self.expled else None,
4826 expl_vlen=self.expl_vlen if self.expled else None,
4827 expl_lenindef=self.expl_lenindef,
4828 lenindef=self.lenindef,
4829 ber_encoded=self.ber_encoded,
4832 for name in self.specs:
4833 value = self._value.get(name)
4836 yield value.pps(decode_path=decode_path + (name,))
4837 for pp in self.pps_lenindef(decode_path):
4841 class Set(Sequence):
4842 """``SET`` structure type
4844 Its usage is identical to :py:class:`pyderasn.Sequence`.
4846 .. _allow_unordered_set_ctx:
4848 DER prohibits unordered values encoding and will raise an error
4849 during decode. If If :ref:`bered <bered_ctx>` context option is set,
4850 then no error will occure. Also you can disable strict values
4851 ordering check by setting ``"allow_unordered_set": True``
4852 :ref:`context <ctx>` option.
4855 tag_default = tag_encode(form=TagFormConstructed, num=17)
4856 asn1_type_name = "SET"
4859 raws = self._encoded_values()
4862 return b"".join((self.tag, len_encode(len(v)), v))
4864 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4866 t, tlen, lv = tag_strip(tlv)
4867 except DecodeError as err:
4868 raise err.__class__(
4870 klass=self.__class__,
4871 decode_path=decode_path,
4876 klass=self.__class__,
4877 decode_path=decode_path,
4883 ctx_bered = ctx.get("bered", False)
4885 l, llen, v = len_decode(lv)
4886 except LenIndefForm as err:
4888 raise err.__class__(
4890 klass=self.__class__,
4891 decode_path=decode_path,
4894 l, llen, v = 0, 1, lv[1:]
4896 except DecodeError as err:
4897 raise err.__class__(
4899 klass=self.__class__,
4900 decode_path=decode_path,
4904 raise NotEnoughData(
4905 "encoded length is longer than data",
4906 klass=self.__class__,
4910 v, tail = v[:l], v[l:]
4912 sub_offset = offset + tlen + llen
4915 ctx_allow_default_values = ctx.get("allow_default_values", False)
4916 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4917 value_prev = memoryview(v[:0])
4918 specs_items = self.specs.items
4920 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4922 for name, spec in specs_items():
4923 sub_decode_path = decode_path + (name,)
4929 decode_path=sub_decode_path,
4932 _ctx_immutable=False,
4939 klass=self.__class__,
4940 decode_path=decode_path,
4943 value, v_tail = spec.decode(
4947 decode_path=sub_decode_path,
4949 _ctx_immutable=False,
4951 value_len = value.fulllen
4952 if value_prev.tobytes() > v[:value_len].tobytes():
4953 if ctx_bered or ctx_allow_unordered_set:
4957 "unordered " + self.asn1_type_name,
4958 klass=self.__class__,
4959 decode_path=sub_decode_path,
4962 if spec.default is None or value != spec.default:
4964 elif ctx_bered or ctx_allow_default_values:
4968 "DEFAULT value met",
4969 klass=self.__class__,
4970 decode_path=sub_decode_path,
4973 values[name] = value
4974 value_prev = v[:value_len]
4975 sub_offset += value_len
4978 obj = self.__class__(
4982 default=self.default,
4983 optional=self.optional,
4984 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4987 if v[:EOC_LEN].tobytes() != EOC:
4990 klass=self.__class__,
4991 decode_path=decode_path,
4999 "not all values are ready",
5000 klass=self.__class__,
5001 decode_path=decode_path,
5004 obj.ber_encoded = ber_encoded
5008 class SequenceOf(Obj):
5009 """``SEQUENCE OF`` sequence type
5011 For that kind of type you must specify the object it will carry on
5012 (bounds are for example here, not required)::
5014 class Ints(SequenceOf):
5019 >>> ints.append(Integer(123))
5020 >>> ints.append(Integer(234))
5022 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5023 >>> [int(i) for i in ints]
5025 >>> ints.append(Integer(345))
5026 Traceback (most recent call last):
5027 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5030 >>> ints[1] = Integer(345)
5032 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5034 Also you can initialize sequence with preinitialized values:
5036 >>> ints = Ints([Integer(123), Integer(234)])
5038 __slots__ = ("spec", "_bound_min", "_bound_max")
5039 tag_default = tag_encode(form=TagFormConstructed, num=16)
5040 asn1_type_name = "SEQUENCE OF"
5053 super(SequenceOf, self).__init__(
5061 schema = getattr(self, "schema", None)
5063 raise ValueError("schema must be specified")
5065 self._bound_min, self._bound_max = getattr(
5069 ) if bounds is None else bounds
5071 if value is not None:
5072 self._value = self._value_sanitize(value)
5073 if default is not None:
5074 default_value = self._value_sanitize(default)
5075 default_obj = self.__class__(
5080 default_obj._value = default_value
5081 self.default = default_obj
5083 self._value = default_obj.copy()._value
5085 def _value_sanitize(self, value):
5086 if issubclass(value.__class__, SequenceOf):
5087 value = value._value
5088 elif hasattr(value, "__iter__"):
5091 raise InvalidValueType((self.__class__, iter))
5092 if not self._bound_min <= len(value) <= self._bound_max:
5093 raise BoundsError(self._bound_min, len(value), self._bound_max)
5095 if not isinstance(v, self.spec.__class__):
5096 raise InvalidValueType((self.spec.__class__,))
5101 return all(v.ready for v in self._value)
5105 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5107 return any(v.bered for v in self._value)
5110 obj = self.__class__(schema=self.spec)
5111 obj._bound_min = self._bound_min
5112 obj._bound_max = self._bound_max
5114 obj._expl = self._expl
5115 obj.default = self.default
5116 obj.optional = self.optional
5117 obj.offset = self.offset
5118 obj.llen = self.llen
5119 obj.vlen = self.vlen
5120 obj._value = [v.copy() for v in self._value]
5123 def __eq__(self, their):
5124 if isinstance(their, self.__class__):
5126 self.spec == their.spec and
5127 self.tag == their.tag and
5128 self._expl == their._expl and
5129 self._value == their._value
5131 if hasattr(their, "__iter__"):
5132 return self._value == list(their)
5144 return self.__class__(
5148 (self._bound_min, self._bound_max)
5149 if bounds is None else bounds
5151 impl=self.tag if impl is None else impl,
5152 expl=self._expl if expl is None else expl,
5153 default=self.default if default is None else default,
5154 optional=self.optional if optional is None else optional,
5157 def __contains__(self, key):
5158 return key in self._value
5160 def append(self, value):
5161 if not isinstance(value, self.spec.__class__):
5162 raise InvalidValueType((self.spec.__class__,))
5163 if len(self._value) + 1 > self._bound_max:
5166 len(self._value) + 1,
5169 self._value.append(value)
5172 self._assert_ready()
5173 return iter(self._value)
5176 self._assert_ready()
5177 return len(self._value)
5179 def __setitem__(self, key, value):
5180 if not isinstance(value, self.spec.__class__):
5181 raise InvalidValueType((self.spec.__class__,))
5182 self._value[key] = self.spec(value=value)
5184 def __getitem__(self, key):
5185 return self._value[key]
5187 def _encoded_values(self):
5188 return [v.encode() for v in self._value]
5191 v = b"".join(self._encoded_values())
5192 return b"".join((self.tag, len_encode(len(v)), v))
5194 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5196 t, tlen, lv = tag_strip(tlv)
5197 except DecodeError as err:
5198 raise err.__class__(
5200 klass=self.__class__,
5201 decode_path=decode_path,
5206 klass=self.__class__,
5207 decode_path=decode_path,
5213 ctx_bered = ctx.get("bered", False)
5215 l, llen, v = len_decode(lv)
5216 except LenIndefForm as err:
5218 raise err.__class__(
5220 klass=self.__class__,
5221 decode_path=decode_path,
5224 l, llen, v = 0, 1, lv[1:]
5226 except DecodeError as err:
5227 raise err.__class__(
5229 klass=self.__class__,
5230 decode_path=decode_path,
5234 raise NotEnoughData(
5235 "encoded length is longer than data",
5236 klass=self.__class__,
5237 decode_path=decode_path,
5241 v, tail = v[:l], v[l:]
5243 sub_offset = offset + tlen + llen
5245 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5246 value_prev = memoryview(v[:0])
5250 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5252 sub_decode_path = decode_path + (str(len(_value)),)
5253 value, v_tail = spec.decode(
5257 decode_path=sub_decode_path,
5259 _ctx_immutable=False,
5261 value_len = value.fulllen
5263 if value_prev.tobytes() > v[:value_len].tobytes():
5264 if ctx_bered or ctx_allow_unordered_set:
5268 "unordered " + self.asn1_type_name,
5269 klass=self.__class__,
5270 decode_path=sub_decode_path,
5273 value_prev = v[:value_len]
5274 _value.append(value)
5275 sub_offset += value_len
5279 obj = self.__class__(
5282 bounds=(self._bound_min, self._bound_max),
5285 default=self.default,
5286 optional=self.optional,
5287 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5289 except BoundsError as err:
5292 klass=self.__class__,
5293 decode_path=decode_path,
5297 if v[:EOC_LEN].tobytes() != EOC:
5300 klass=self.__class__,
5301 decode_path=decode_path,
5306 obj.ber_encoded = ber_encoded
5311 pp_console_row(next(self.pps())),
5312 ", ".join(repr(v) for v in self._value),
5315 def pps(self, decode_path=()):
5318 asn1_type_name=self.asn1_type_name,
5319 obj_name=self.__class__.__name__,
5320 decode_path=decode_path,
5321 optional=self.optional,
5322 default=self == self.default,
5323 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5324 expl=None if self._expl is None else tag_decode(self._expl),
5329 expl_offset=self.expl_offset if self.expled else None,
5330 expl_tlen=self.expl_tlen if self.expled else None,
5331 expl_llen=self.expl_llen if self.expled else None,
5332 expl_vlen=self.expl_vlen if self.expled else None,
5333 expl_lenindef=self.expl_lenindef,
5334 lenindef=self.lenindef,
5335 ber_encoded=self.ber_encoded,
5338 for i, value in enumerate(self._value):
5339 yield value.pps(decode_path=decode_path + (str(i),))
5340 for pp in self.pps_lenindef(decode_path):
5344 class SetOf(SequenceOf):
5345 """``SET OF`` sequence type
5347 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5350 tag_default = tag_encode(form=TagFormConstructed, num=17)
5351 asn1_type_name = "SET OF"
5354 raws = self._encoded_values()
5357 return b"".join((self.tag, len_encode(len(v)), v))
5359 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5360 return super(SetOf, self)._decode(
5366 ordering_check=True,
5370 def obj_by_path(pypath): # pragma: no cover
5371 """Import object specified as string Python path
5373 Modules must be separated from classes/functions with ``:``.
5375 >>> obj_by_path("foo.bar:Baz")
5376 <class 'foo.bar.Baz'>
5377 >>> obj_by_path("foo.bar:Baz.boo")
5378 <classmethod 'foo.bar.Baz.boo'>
5380 mod, objs = pypath.rsplit(":", 1)
5381 from importlib import import_module
5382 obj = import_module(mod)
5383 for obj_name in objs.split("."):
5384 obj = getattr(obj, obj_name)
5388 def generic_decoder(): # pragma: no cover
5389 # All of this below is a big hack with self references
5390 choice = PrimitiveTypes()
5391 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5392 choice.specs["SetOf"] = SetOf(schema=choice)
5394 choice.specs["SequenceOf%d" % i] = SequenceOf(
5398 choice.specs["Any"] = Any()
5400 # Class name equals to type name, to omit it from output
5401 class SEQUENCEOF(SequenceOf):
5409 with_decode_path=False,
5410 decode_path_only=(),
5412 def _pprint_pps(pps):
5414 if hasattr(pp, "_fields"):
5416 decode_path_only != () and
5417 pp.decode_path[:len(decode_path_only)] != decode_path_only
5420 if pp.asn1_type_name == Choice.asn1_type_name:
5422 pp_kwargs = pp._asdict()
5423 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5424 pp = _pp(**pp_kwargs)
5425 yield pp_console_row(
5430 with_colours=with_colours,
5431 with_decode_path=with_decode_path,
5432 decode_path_len_decrease=len(decode_path_only),
5434 for row in pp_console_blob(
5436 decode_path_len_decrease=len(decode_path_only),
5440 for row in _pprint_pps(pp):
5442 return "\n".join(_pprint_pps(obj.pps()))
5443 return SEQUENCEOF(), pprint_any
5446 def main(): # pragma: no cover
5448 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5449 parser.add_argument(
5453 help="Skip that number of bytes from the beginning",
5455 parser.add_argument(
5457 help="Python path to dictionary with OIDs",
5459 parser.add_argument(
5461 help="Python path to schema definition to use",
5463 parser.add_argument(
5464 "--defines-by-path",
5465 help="Python path to decoder's defines_by_path",
5467 parser.add_argument(
5469 action="store_true",
5470 help="Disallow BER encoding",
5472 parser.add_argument(
5473 "--print-decode-path",
5474 action="store_true",
5475 help="Print decode paths",
5477 parser.add_argument(
5478 "--decode-path-only",
5479 help="Print only specified decode path",
5481 parser.add_argument(
5483 action="store_true",
5484 help="Allow explicit tag out-of-bound",
5486 parser.add_argument(
5488 type=argparse.FileType("rb"),
5489 help="Path to DER file you want to decode",
5491 args = parser.parse_args()
5492 args.DERFile.seek(args.skip)
5493 der = memoryview(args.DERFile.read())
5494 args.DERFile.close()
5495 oids = obj_by_path(args.oids) if args.oids else {}
5497 schema = obj_by_path(args.schema)
5498 from functools import partial
5499 pprinter = partial(pprint, big_blobs=True)
5501 schema, pprinter = generic_decoder()
5503 "bered": not args.nobered,
5504 "allow_expl_oob": args.allow_expl_oob,
5506 if args.defines_by_path is not None:
5507 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5508 obj, tail = schema().decode(der, ctx=ctx)
5512 with_colours=True if environ.get("NO_COLOR") is None else False,
5513 with_decode_path=args.print_decode_path,
5515 () if args.decode_path_only is None else
5516 tuple(args.decode_path_only.split(":"))
5520 print("\nTrailing data: %s" % hexenc(tail))
5523 if __name__ == "__main__":