3 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
4 # Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 """Python ASN.1 DER/BER codec with abstract structures
21 This library allows you to marshal various structures in ASN.1 DER
22 format, unmarshal them in BER/CER/DER ones.
26 >>> Integer().decode(raw) == i
29 There are primitive types, holding single values
30 (:py:class:`pyderasn.BitString`,
31 :py:class:`pyderasn.Boolean`,
32 :py:class:`pyderasn.Enumerated`,
33 :py:class:`pyderasn.GeneralizedTime`,
34 :py:class:`pyderasn.Integer`,
35 :py:class:`pyderasn.Null`,
36 :py:class:`pyderasn.ObjectIdentifier`,
37 :py:class:`pyderasn.OctetString`,
38 :py:class:`pyderasn.UTCTime`,
39 :py:class:`various strings <pyderasn.CommonString>`
40 (:py:class:`pyderasn.BMPString`,
41 :py:class:`pyderasn.GeneralString`,
42 :py:class:`pyderasn.GraphicString`,
43 :py:class:`pyderasn.IA5String`,
44 :py:class:`pyderasn.ISO646String`,
45 :py:class:`pyderasn.NumericString`,
46 :py:class:`pyderasn.PrintableString`,
47 :py:class:`pyderasn.T61String`,
48 :py:class:`pyderasn.TeletexString`,
49 :py:class:`pyderasn.UniversalString`,
50 :py:class:`pyderasn.UTF8String`,
51 :py:class:`pyderasn.VideotexString`,
52 :py:class:`pyderasn.VisibleString`)),
53 constructed types, holding multiple primitive types
54 (:py:class:`pyderasn.Sequence`,
55 :py:class:`pyderasn.SequenceOf`,
56 :py:class:`pyderasn.Set`,
57 :py:class:`pyderasn.SetOf`),
58 and special types like
59 :py:class:`pyderasn.Any` and
60 :py:class:`pyderasn.Choice`.
68 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
69 the default tag used during coding process. You can override it with
70 either ``IMPLICIT`` (using ``impl`` keyword argument), or
71 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments take
72 raw binary string, containing that tag. You can **not** set implicit and
73 explicit tags simultaneously.
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
78 number. Pay attention that explicit tags always have *constructed* tag
79 (``tag_ctxc``), but implicit tags for primitive types are primitive
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two objects of the same type, but with different implicit/explicit tags
94 You can get object's effective tag (either default or implicited) through
95 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
112 Many objects in sequences could be ``OPTIONAL`` and could have
113 ``DEFAULT`` value. You can specify that object's property using
114 corresponding keyword arguments.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
119 Those specifications do not play any role in primitive value encoding,
120 but are taken into account when dealing with sequences holding them. For
121 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
143 Some objects give ability to set value size constraints. This is either
144 possible integer value, or allowed length of various strings and
145 sequences. Constraints are set in the following way::
150 And values satisfaction is checked as: ``MIN <= X <= MAX``.
152 For simplicity you can also set bounds the following way::
154 bounded_x = X(bounds=(MIN, MAX))
156 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
162 All objects have ``ready`` boolean property, that tells if object is
163 ready to be encoded. If that kind of action is performed on unready
164 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
166 All objects have ``copy()`` method, that returns their copy, that can be
174 Decoding is performed using ``decode()`` method. ``offset`` optional
175 argument could be used to set initial object's offset in the binary
176 data, for convenience. It returns decoded object and remaining
177 unmarshalled data (tail). Internally all work is done on
178 ``memoryview(data)``, and you can leave returning tail as a memoryview,
179 by specifying ``leavemm=True`` argument.
181 When object is decoded, ``decoded`` property is true and you can safely
182 use following properties:
184 * ``offset`` -- position including initial offset where object's tag starts
185 * ``tlen`` -- length of object's tag
186 * ``llen`` -- length of object's length value
187 * ``vlen`` -- length of object's value
188 * ``tlvlen`` -- length of the whole object
190 Pay attention that those values do **not** include anything related to
191 explicit tag. If you want to know information about it, then use:
193 * ``expled`` -- to know if explicit tag is set
194 * ``expl_offset`` (it is lesser than ``offset``)
197 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
198 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
200 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
203 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
210 You can specify so called context keyword argument during ``decode()``
211 invocation. It is dictionary containing various options governing
214 Currently available context options:
216 * :ref:`allow_default_values <allow_default_values_ctx>`
217 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
218 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
219 * :ref:`bered <bered_ctx>`
220 * :ref:`defines_by_path <defines_by_path_ctx>`
227 All objects have ``pps()`` method, that is a generator of
228 :py:class:`pyderasn.PP` namedtuple, holding various raw information
229 about the object. If ``pps`` is called on sequences, then all underlying
230 ``PP`` will be yielded.
232 You can use :py:func:`pyderasn.pp_console_row` function, converting
233 those ``PP`` to human readable string. Actually exactly it is used for
234 all object ``repr``. But it is easy to write custom formatters.
236 >>> from pyderasn import pprint
237 >>> encoded = Integer(-12345).encode()
238 >>> obj, tail = Integer().decode(encoded)
239 >>> print(pprint(obj))
240 0 [1,1, 2] INTEGER -12345
247 ASN.1 structures often have ANY and OCTET STRING fields, that are
248 DEFINED BY some previously met ObjectIdentifier. This library provides
249 ability to specify mapping between some OID and field that must be
250 decoded with specific specification.
255 :py:class:`pyderasn.ObjectIdentifier` field inside
256 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
257 necessary for decoding structures. For example, CMS (:rfc:`5652`)
260 class ContentInfo(Sequence):
262 ("contentType", ContentType(defines=((("content",), {
263 id_digestedData: DigestedData(),
264 id_signedData: SignedData(),
266 ("content", Any(expl=tag_ctxc(0))),
269 ``contentType`` field tells that it defines that ``content`` must be
270 decoded with ``SignedData`` specification, if ``contentType`` equals to
271 ``id-signedData``. The same applies to ``DigestedData``. If
272 ``contentType`` contains unknown OID, then no automatic decoding is
275 You can specify multiple fields, that will be autodecoded -- that is why
276 ``defines`` kwarg is a sequence. You can specify defined field
277 relatively or absolutely to current decode path. For example ``defines``
278 for AlgorithmIdentifier of X.509's
279 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
283 id_ecPublicKey: ECParameters(),
284 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
286 (("..", "subjectPublicKey"), {
287 id_rsaEncryption: RSAPublicKey(),
288 id_GostR3410_2001: OctetString(),
292 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
293 autodecode its parameters inside SPKI's algorithm and its public key
296 Following types can be automatically decoded (DEFINED BY):
298 * :py:class:`pyderasn.Any`
299 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
300 * :py:class:`pyderasn.OctetString`
301 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
302 ``Any``/``BitString``/``OctetString``-s
304 When any of those fields is automatically decoded, then ``.defined``
305 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
306 was defined, ``value`` contains corresponding decoded value. For example
307 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
309 .. _defines_by_path_ctx:
311 defines_by_path context option
312 ______________________________
314 Sometimes you either can not or do not want to explicitly set *defines*
315 in the scheme. You can dynamically apply those definitions when calling
316 ``.decode()`` method.
318 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
319 value must be sequence of following tuples::
321 (decode_path, defines)
323 where ``decode_path`` is a tuple holding so-called decode path to the
324 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
325 ``defines``, holding exactly the same value as accepted in its keyword
328 For example, again for CMS, you want to automatically decode
329 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
330 structures it may hold. Also, automatically decode ``controlSequence``
333 content_info, tail = ContentInfo().decode(data, defines_by_path=(
336 ((("content",), {id_signedData: SignedData()}),),
341 DecodePathDefBy(id_signedData),
346 id_cct_PKIData: PKIData(),
347 id_cct_PKIResponse: PKIResponse(),
353 DecodePathDefBy(id_signedData),
356 DecodePathDefBy(id_cct_PKIResponse),
362 id_cmc_recipientNonce: RecipientNonce(),
363 id_cmc_senderNonce: SenderNonce(),
364 id_cmc_statusInfoV2: CMCStatusInfoV2(),
365 id_cmc_transactionId: TransactionId(),
370 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
371 First function is useful for path construction when some automatic
372 decoding is already done. ``any`` means literally any value it meet --
373 useful for SEQUENCE/SET OF-s.
380 By default PyDERASN accepts only DER encoded data. It always encodes to
381 DER. But you can optionally enable BER decoding with setting ``bered``
382 :ref:`context <ctx>` argument to True. Indefinite lengths and
383 constructed primitive types should be parsed successfully.
385 * If object is encoded in BER form (not the DER one), then ``bered``
386 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
387 STRING``, ``SEQUENCE``, ``SET`` 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.
395 EOC (end-of-contents) token's length is taken in advance in object's
398 .. _allow_expl_oob_ctx:
400 Allow explicit tag out-of-bound
401 -------------------------------
403 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
404 one value, more than one object. If you set ``allow_expl_oob`` context
405 option to True, then no error will be raised and that invalid encoding
406 will be silently further processed. But pay attention that offsets and
407 lengths will be invalid in that case.
411 This option should be used only for skipping some decode errors, just
412 to see the decoded structure somehow.
419 .. autoclass:: pyderasn.Boolean
424 .. autoclass:: pyderasn.Integer
429 .. autoclass:: pyderasn.BitString
434 .. autoclass:: pyderasn.OctetString
439 .. autoclass:: pyderasn.Null
444 .. autoclass:: pyderasn.ObjectIdentifier
449 .. autoclass:: pyderasn.Enumerated
453 .. autoclass:: pyderasn.CommonString
457 .. autoclass:: pyderasn.NumericString
461 .. autoclass:: pyderasn.UTCTime
462 :members: __init__, todatetime
466 .. autoclass:: pyderasn.GeneralizedTime
473 .. autoclass:: pyderasn.Choice
478 .. autoclass:: PrimitiveTypes
482 .. autoclass:: pyderasn.Any
490 .. autoclass:: pyderasn.Sequence
495 .. autoclass:: pyderasn.Set
500 .. autoclass:: pyderasn.SequenceOf
505 .. autoclass:: pyderasn.SetOf
511 .. autofunction:: pyderasn.abs_decode_path
512 .. autofunction:: pyderasn.hexenc
513 .. autofunction:: pyderasn.hexdec
514 .. autofunction:: pyderasn.tag_encode
515 .. autofunction:: pyderasn.tag_decode
516 .. autofunction:: pyderasn.tag_ctxp
517 .. autofunction:: pyderasn.tag_ctxc
518 .. autoclass:: pyderasn.Obj
519 .. autoclass:: pyderasn.DecodeError
521 .. autoclass:: pyderasn.NotEnoughData
522 .. autoclass:: pyderasn.LenIndefForm
523 .. autoclass:: pyderasn.TagMismatch
524 .. autoclass:: pyderasn.InvalidLength
525 .. autoclass:: pyderasn.InvalidOID
526 .. autoclass:: pyderasn.ObjUnknown
527 .. autoclass:: pyderasn.ObjNotReady
528 .. autoclass:: pyderasn.InvalidValueType
529 .. autoclass:: pyderasn.BoundsError
532 from codecs import getdecoder
533 from codecs import getencoder
534 from collections import namedtuple
535 from collections import OrderedDict
536 from datetime import datetime
537 from math import ceil
538 from os import environ
539 from string import digits
541 from six import add_metaclass
542 from six import binary_type
543 from six import byte2int
544 from six import indexbytes
545 from six import int2byte
546 from six import integer_types
547 from six import iterbytes
549 from six import string_types
550 from six import text_type
551 from six.moves import xrange as six_xrange
555 from termcolor import colored
557 def colored(what, *args):
601 "TagClassApplication",
605 "TagFormConstructed",
616 TagClassUniversal = 0
617 TagClassApplication = 1 << 6
618 TagClassContext = 1 << 7
619 TagClassPrivate = 1 << 6 | 1 << 7
621 TagFormConstructed = 1 << 5
624 TagClassApplication: "APPLICATION ",
625 TagClassPrivate: "PRIVATE ",
626 TagClassUniversal: "UNIV ",
630 LENINDEF = b"\x80" # length indefinite mark
631 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
634 ########################################################################
636 ########################################################################
638 class DecodeError(Exception):
639 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
641 :param str msg: reason of decode failing
642 :param klass: optional exact DecodeError inherited class (like
643 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
644 :py:exc:`InvalidLength`)
645 :param decode_path: tuple of strings. It contains human
646 readable names of the fields through which
647 decoding process has passed
648 :param int offset: binary offset where failure happened
650 super(DecodeError, self).__init__()
653 self.decode_path = decode_path
659 "" if self.klass is None else self.klass.__name__,
661 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
662 if len(self.decode_path) > 0 else ""
664 ("(at %d)" % self.offset) if self.offset > 0 else "",
670 return "%s(%s)" % (self.__class__.__name__, self)
673 class NotEnoughData(DecodeError):
677 class LenIndefForm(DecodeError):
681 class TagMismatch(DecodeError):
685 class InvalidLength(DecodeError):
689 class InvalidOID(DecodeError):
693 class ObjUnknown(ValueError):
694 def __init__(self, name):
695 super(ObjUnknown, self).__init__()
699 return "object is unknown: %s" % self.name
702 return "%s(%s)" % (self.__class__.__name__, self)
705 class ObjNotReady(ValueError):
706 def __init__(self, name):
707 super(ObjNotReady, self).__init__()
711 return "object is not ready: %s" % self.name
714 return "%s(%s)" % (self.__class__.__name__, self)
717 class InvalidValueType(ValueError):
718 def __init__(self, expected_types):
719 super(InvalidValueType, self).__init__()
720 self.expected_types = expected_types
723 return "invalid value type, expected: %s" % ", ".join(
724 [repr(t) for t in self.expected_types]
728 return "%s(%s)" % (self.__class__.__name__, self)
731 class BoundsError(ValueError):
732 def __init__(self, bound_min, value, bound_max):
733 super(BoundsError, self).__init__()
734 self.bound_min = bound_min
736 self.bound_max = bound_max
739 return "unsatisfied bounds: %s <= %s <= %s" % (
746 return "%s(%s)" % (self.__class__.__name__, self)
749 ########################################################################
751 ########################################################################
753 _hexdecoder = getdecoder("hex")
754 _hexencoder = getencoder("hex")
758 """Binary data to hexadecimal string convert
760 return _hexdecoder(data)[0]
764 """Hexadecimal string to binary data convert
766 return _hexencoder(data)[0].decode("ascii")
769 def int_bytes_len(num, byte_len=8):
772 return int(ceil(float(num.bit_length()) / byte_len))
775 def zero_ended_encode(num):
776 octets = bytearray(int_bytes_len(num, 7))
778 octets[i] = num & 0x7F
782 octets[i] = 0x80 | (num & 0x7F)
788 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
789 """Encode tag to binary form
791 :param int num: tag's number
792 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
793 :py:data:`pyderasn.TagClassContext`,
794 :py:data:`pyderasn.TagClassApplication`,
795 :py:data:`pyderasn.TagClassPrivate`)
796 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
797 :py:data:`pyderasn.TagFormConstructed`)
801 return int2byte(klass | form | num)
802 # [XX|X|11111][1.......][1.......] ... [0.......]
803 return int2byte(klass | form | 31) + zero_ended_encode(num)
807 """Decode tag from binary form
811 No validation is performed, assuming that it has already passed.
813 It returns tuple with three integers, as
814 :py:func:`pyderasn.tag_encode` accepts.
816 first_octet = byte2int(tag)
817 klass = first_octet & 0xC0
818 form = first_octet & 0x20
819 if first_octet & 0x1F < 0x1F:
820 return (klass, form, first_octet & 0x1F)
822 for octet in iterbytes(tag[1:]):
825 return (klass, form, num)
829 """Create CONTEXT PRIMITIVE tag
831 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
835 """Create CONTEXT CONSTRUCTED tag
837 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
841 """Take off tag from the data
843 :returns: (encoded tag, tag length, remaining data)
846 raise NotEnoughData("no data at all")
847 if byte2int(data) & 0x1F < 31:
848 return data[:1], 1, data[1:]
853 raise DecodeError("unfinished tag")
854 if indexbytes(data, i) & 0x80 == 0:
857 return data[:i], i, data[i:]
863 octets = bytearray(int_bytes_len(l) + 1)
864 octets[0] = 0x80 | (len(octets) - 1)
865 for i in six_xrange(len(octets) - 1, 0, -1):
871 def len_decode(data):
874 :returns: (decoded length, length's length, remaining data)
875 :raises LenIndefForm: if indefinite form encoding is met
878 raise NotEnoughData("no data at all")
879 first_octet = byte2int(data)
880 if first_octet & 0x80 == 0:
881 return first_octet, 1, data[1:]
882 octets_num = first_octet & 0x7F
883 if octets_num + 1 > len(data):
884 raise NotEnoughData("encoded length is longer than data")
887 if byte2int(data[1:]) == 0:
888 raise DecodeError("leading zeros")
890 for v in iterbytes(data[1:1 + octets_num]):
893 raise DecodeError("long form instead of short one")
894 return l, 1 + octets_num, data[1 + octets_num:]
897 ########################################################################
899 ########################################################################
901 class AutoAddSlots(type):
902 def __new__(mcs, name, bases, _dict):
903 _dict["__slots__"] = _dict.get("__slots__", ())
904 return type.__new__(mcs, name, bases, _dict)
907 @add_metaclass(AutoAddSlots)
909 """Common ASN.1 object class
911 All ASN.1 types are inherited from it. It has metaclass that
912 automatically adds ``__slots__`` to all inherited classes.
936 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
937 self._expl = getattr(self, "expl", None) if expl is None else expl
938 if self.tag != self.tag_default and self._expl is not None:
939 raise ValueError("implicit and explicit tags can not be set simultaneously")
940 if default is not None:
942 self.optional = optional
943 self.offset, self.llen, self.vlen = _decoded
945 self.expl_lenindef = False
946 self.lenindef = False
950 def ready(self): # pragma: no cover
951 """Is object ready to be encoded?
953 raise NotImplementedError()
955 def _assert_ready(self):
957 raise ObjNotReady(self.__class__.__name__)
961 """Is object decoded?
963 return (self.llen + self.vlen) > 0
965 def copy(self): # pragma: no cover
966 """Make a copy of object, safe to be mutated
968 raise NotImplementedError()
976 return self.tlen + self.llen + self.vlen
978 def __str__(self): # pragma: no cover
979 return self.__bytes__() if PY2 else self.__unicode__()
981 def __ne__(self, their):
982 return not(self == their)
984 def __gt__(self, their): # pragma: no cover
985 return not(self < their)
987 def __le__(self, their): # pragma: no cover
988 return (self == their) or (self < their)
990 def __ge__(self, their): # pragma: no cover
991 return (self == their) or (self > their)
993 def _encode(self): # pragma: no cover
994 raise NotImplementedError()
996 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
997 raise NotImplementedError()
1000 raw = self._encode()
1001 if self._expl is None:
1003 return b"".join((self._expl, len_encode(len(raw)), raw))
1016 :param data: either binary or memoryview
1017 :param int offset: initial data's offset
1018 :param bool leavemm: do we need to leave memoryview of remaining
1019 data as is, or convert it to bytes otherwise
1020 :param ctx: optional :ref:`context <ctx>` governing decoding process.
1021 :param tag_only: decode only the tag, without length and contents
1022 (used only in Choice and Set structures, trying to
1023 determine if tag satisfies the scheme)
1024 :returns: (Obj, remaining data)
1028 tlv = memoryview(data)
1029 if self._expl is None:
1030 result = self._decode(
1033 decode_path=decode_path,
1042 t, tlen, lv = tag_strip(tlv)
1043 except DecodeError as err:
1044 raise err.__class__(
1046 klass=self.__class__,
1047 decode_path=decode_path,
1052 klass=self.__class__,
1053 decode_path=decode_path,
1057 l, llen, v = len_decode(lv)
1058 except LenIndefForm as err:
1059 if not ctx.get("bered", False):
1060 raise err.__class__(
1062 klass=self.__class__,
1063 decode_path=decode_path,
1067 offset += tlen + llen
1068 result = self._decode(
1071 decode_path=decode_path,
1078 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1079 if eoc_expected.tobytes() != EOC:
1082 klass=self.__class__,
1083 decode_path=decode_path,
1087 obj.expl_lenindef = True
1088 except DecodeError as err:
1089 raise err.__class__(
1091 klass=self.__class__,
1092 decode_path=decode_path,
1097 raise NotEnoughData(
1098 "encoded length is longer than data",
1099 klass=self.__class__,
1100 decode_path=decode_path,
1103 result = self._decode(
1105 offset=offset + tlen + llen,
1106 decode_path=decode_path,
1113 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1115 "explicit tag out-of-bound, longer than data",
1116 klass=self.__class__,
1117 decode_path=decode_path,
1120 return obj, (tail if leavemm else tail.tobytes())
1124 return self._expl is not None
1131 def expl_tlen(self):
1132 return len(self._expl)
1135 def expl_llen(self):
1136 if self.expl_lenindef:
1138 return len(len_encode(self.tlvlen))
1141 def expl_offset(self):
1142 return self.offset - self.expl_tlen - self.expl_llen
1145 def expl_vlen(self):
1149 def expl_tlvlen(self):
1150 return self.expl_tlen + self.expl_llen + self.expl_vlen
1153 def fulloffset(self):
1154 return self.expl_offset if self.expled else self.offset
1158 return self.expl_tlvlen if self.expled else self.tlvlen
1160 def pps_lenindef(self, decode_path):
1163 asn1_type_name="EOC",
1165 decode_path=decode_path,
1167 self.offset + self.tlvlen -
1168 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1175 if self.expl_lenindef:
1177 asn1_type_name="EOC",
1178 obj_name="EXPLICIT",
1179 decode_path=decode_path,
1180 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1188 class DecodePathDefBy(object):
1189 """DEFINED BY representation inside decode path
1191 __slots__ = ("defined_by",)
1193 def __init__(self, defined_by):
1194 self.defined_by = defined_by
1196 def __ne__(self, their):
1197 return not(self == their)
1199 def __eq__(self, their):
1200 if not isinstance(their, self.__class__):
1202 return self.defined_by == their.defined_by
1205 return "DEFINED BY " + str(self.defined_by)
1208 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1211 ########################################################################
1213 ########################################################################
1215 PP = namedtuple("PP", (
1240 asn1_type_name="unknown",
1257 expl_lenindef=False,
1285 def _colourize(what, colour, with_colours, attrs=("bold",)):
1286 return colored(what, colour, attrs=attrs) if with_colours else what
1295 with_decode_path=False,
1296 decode_path_len_decrease=0,
1303 " " if pp.expl_offset is None else
1304 ("-%d" % (pp.offset - pp.expl_offset))
1306 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1308 cols.append(_colourize(col, "red", with_colours, ()))
1309 col = "[%d,%d,%4d]%s" % (
1313 LENINDEF_PP_CHAR if pp.lenindef else " "
1315 col = _colourize(col, "green", with_colours, ())
1317 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1318 if decode_path_len > 0:
1319 cols.append(" ." * decode_path_len)
1320 ent = pp.decode_path[-1]
1321 if isinstance(ent, DecodePathDefBy):
1322 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1323 value = str(ent.defined_by)
1325 oids is not None and
1326 ent.defined_by.asn1_type_name ==
1327 ObjectIdentifier.asn1_type_name and
1330 cols.append(_colourize("%s:" % oids[value], "green", with_colours))
1332 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1334 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1335 if pp.expl is not None:
1336 klass, _, num = pp.expl
1337 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1338 cols.append(_colourize(col, "blue", with_colours))
1339 if pp.impl is not None:
1340 klass, _, num = pp.impl
1341 col = "[%s%d]" % (TagClassReprs[klass], num)
1342 cols.append(_colourize(col, "blue", with_colours))
1343 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1344 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1346 cols.append(_colourize("BER", "red", with_colours))
1347 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1348 if pp.value is not None:
1350 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1352 oids is not None and
1353 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1356 cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
1358 if isinstance(pp.blob, binary_type):
1359 cols.append(hexenc(pp.blob))
1360 elif isinstance(pp.blob, tuple):
1361 cols.append(", ".join(pp.blob))
1363 cols.append(_colourize("OPTIONAL", "red", with_colours))
1365 cols.append(_colourize("DEFAULT", "red", with_colours))
1366 if with_decode_path:
1367 cols.append(_colourize(
1368 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1372 return " ".join(cols)
1375 def pp_console_blob(pp, decode_path_len_decrease=0):
1376 cols = [" " * len("XXXXXYYZ [X,X,XXXX]Z")]
1377 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1378 if decode_path_len > 0:
1379 cols.append(" ." * (decode_path_len + 1))
1380 if isinstance(pp.blob, binary_type):
1381 blob = hexenc(pp.blob).upper()
1382 for i in range(0, len(blob), 32):
1383 chunk = blob[i:i + 32]
1384 yield " ".join(cols + [":".join(
1385 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1387 elif isinstance(pp.blob, tuple):
1388 yield " ".join(cols + [", ".join(pp.blob)])
1396 with_decode_path=False,
1397 decode_path_only=(),
1399 """Pretty print object
1401 :param Obj obj: object you want to pretty print
1402 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1403 from it is met, then its humand readable form is printed
1404 :param big_blobs: if large binary objects are met (like OctetString
1405 values), do we need to print them too, on separate
1407 :param with_colours: colourize output, if ``termcolor`` library
1409 :param with_decode_path: print decode path
1410 :param decode_path_only: print only that specified decode path
1412 def _pprint_pps(pps):
1414 if hasattr(pp, "_fields"):
1416 decode_path_only != () and
1418 str(p) for p in pp.decode_path[:len(decode_path_only)]
1419 ) != decode_path_only
1423 yield pp_console_row(
1428 with_colours=with_colours,
1429 with_decode_path=with_decode_path,
1430 decode_path_len_decrease=len(decode_path_only),
1432 for row in pp_console_blob(
1434 decode_path_len_decrease=len(decode_path_only),
1438 yield pp_console_row(
1443 with_colours=with_colours,
1444 with_decode_path=with_decode_path,
1445 decode_path_len_decrease=len(decode_path_only),
1448 for row in _pprint_pps(pp):
1450 return "\n".join(_pprint_pps(obj.pps()))
1453 ########################################################################
1454 # ASN.1 primitive types
1455 ########################################################################
1458 """``BOOLEAN`` boolean type
1460 >>> b = Boolean(True)
1462 >>> b == Boolean(True)
1468 tag_default = tag_encode(1)
1469 asn1_type_name = "BOOLEAN"
1481 :param value: set the value. Either boolean type, or
1482 :py:class:`pyderasn.Boolean` object
1483 :param bytes impl: override default tag with ``IMPLICIT`` one
1484 :param bytes expl: override default tag with ``EXPLICIT`` one
1485 :param default: set default value. Type same as in ``value``
1486 :param bool optional: is object ``OPTIONAL`` in sequence
1488 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1489 self._value = None if value is None else self._value_sanitize(value)
1490 if default is not None:
1491 default = self._value_sanitize(default)
1492 self.default = self.__class__(
1498 self._value = default
1500 def _value_sanitize(self, value):
1501 if issubclass(value.__class__, Boolean):
1503 if isinstance(value, bool):
1505 raise InvalidValueType((self.__class__, bool))
1509 return self._value is not None
1512 obj = self.__class__()
1513 obj._value = self._value
1515 obj._expl = self._expl
1516 obj.default = self.default
1517 obj.optional = self.optional
1518 obj.offset = self.offset
1519 obj.llen = self.llen
1520 obj.vlen = self.vlen
1523 def __nonzero__(self):
1524 self._assert_ready()
1528 self._assert_ready()
1531 def __eq__(self, their):
1532 if isinstance(their, bool):
1533 return self._value == their
1534 if not issubclass(their.__class__, Boolean):
1537 self._value == their._value and
1538 self.tag == their.tag and
1539 self._expl == their._expl
1550 return self.__class__(
1552 impl=self.tag if impl is None else impl,
1553 expl=self._expl if expl is None else expl,
1554 default=self.default if default is None else default,
1555 optional=self.optional if optional is None else optional,
1559 self._assert_ready()
1563 (b"\xFF" if self._value else b"\x00"),
1566 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1568 t, _, lv = tag_strip(tlv)
1569 except DecodeError as err:
1570 raise err.__class__(
1572 klass=self.__class__,
1573 decode_path=decode_path,
1578 klass=self.__class__,
1579 decode_path=decode_path,
1585 l, _, v = len_decode(lv)
1586 except DecodeError as err:
1587 raise err.__class__(
1589 klass=self.__class__,
1590 decode_path=decode_path,
1594 raise InvalidLength(
1595 "Boolean's length must be equal to 1",
1596 klass=self.__class__,
1597 decode_path=decode_path,
1601 raise NotEnoughData(
1602 "encoded length is longer than data",
1603 klass=self.__class__,
1604 decode_path=decode_path,
1607 first_octet = byte2int(v)
1609 if first_octet == 0:
1611 elif first_octet == 0xFF:
1613 elif ctx.get("bered", False):
1618 "unacceptable Boolean value",
1619 klass=self.__class__,
1620 decode_path=decode_path,
1623 obj = self.__class__(
1627 default=self.default,
1628 optional=self.optional,
1629 _decoded=(offset, 1, 1),
1635 return pp_console_row(next(self.pps()))
1637 def pps(self, decode_path=()):
1639 asn1_type_name=self.asn1_type_name,
1640 obj_name=self.__class__.__name__,
1641 decode_path=decode_path,
1642 value=str(self._value) if self.ready else None,
1643 optional=self.optional,
1644 default=self == self.default,
1645 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1646 expl=None if self._expl is None else tag_decode(self._expl),
1651 expl_offset=self.expl_offset if self.expled else None,
1652 expl_tlen=self.expl_tlen if self.expled else None,
1653 expl_llen=self.expl_llen if self.expled else None,
1654 expl_vlen=self.expl_vlen if self.expled else None,
1655 expl_lenindef=self.expl_lenindef,
1658 for pp in self.pps_lenindef(decode_path):
1663 """``INTEGER`` integer type
1665 >>> b = Integer(-123)
1667 >>> b == Integer(-123)
1672 >>> Integer(2, bounds=(1, 3))
1674 >>> Integer(5, bounds=(1, 3))
1675 Traceback (most recent call last):
1676 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1680 class Version(Integer):
1687 >>> v = Version("v1")
1694 {'v3': 2, 'v1': 0, 'v2': 1}
1696 __slots__ = ("specs", "_bound_min", "_bound_max")
1697 tag_default = tag_encode(2)
1698 asn1_type_name = "INTEGER"
1712 :param value: set the value. Either integer type, named value
1713 (if ``schema`` is specified in the class), or
1714 :py:class:`pyderasn.Integer` object
1715 :param bounds: set ``(MIN, MAX)`` value constraint.
1716 (-inf, +inf) by default
1717 :param bytes impl: override default tag with ``IMPLICIT`` one
1718 :param bytes expl: override default tag with ``EXPLICIT`` one
1719 :param default: set default value. Type same as in ``value``
1720 :param bool optional: is object ``OPTIONAL`` in sequence
1722 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1724 specs = getattr(self, "schema", {}) if _specs is None else _specs
1725 self.specs = specs if isinstance(specs, dict) else dict(specs)
1726 self._bound_min, self._bound_max = getattr(
1729 (float("-inf"), float("+inf")),
1730 ) if bounds is None else bounds
1731 if value is not None:
1732 self._value = self._value_sanitize(value)
1733 if default is not None:
1734 default = self._value_sanitize(default)
1735 self.default = self.__class__(
1741 if self._value is None:
1742 self._value = default
1744 def _value_sanitize(self, value):
1745 if issubclass(value.__class__, Integer):
1746 value = value._value
1747 elif isinstance(value, integer_types):
1749 elif isinstance(value, str):
1750 value = self.specs.get(value)
1752 raise ObjUnknown("integer value: %s" % value)
1754 raise InvalidValueType((self.__class__, int, str))
1755 if not self._bound_min <= value <= self._bound_max:
1756 raise BoundsError(self._bound_min, value, self._bound_max)
1761 return self._value is not None
1764 obj = self.__class__(_specs=self.specs)
1765 obj._value = self._value
1766 obj._bound_min = self._bound_min
1767 obj._bound_max = self._bound_max
1769 obj._expl = self._expl
1770 obj.default = self.default
1771 obj.optional = self.optional
1772 obj.offset = self.offset
1773 obj.llen = self.llen
1774 obj.vlen = self.vlen
1778 self._assert_ready()
1779 return int(self._value)
1782 self._assert_ready()
1785 bytes(self._expl or b"") +
1786 str(self._value).encode("ascii"),
1789 def __eq__(self, their):
1790 if isinstance(their, integer_types):
1791 return self._value == their
1792 if not issubclass(their.__class__, Integer):
1795 self._value == their._value and
1796 self.tag == their.tag and
1797 self._expl == their._expl
1800 def __lt__(self, their):
1801 return self._value < their._value
1805 for name, value in self.specs.items():
1806 if value == self._value:
1818 return self.__class__(
1821 (self._bound_min, self._bound_max)
1822 if bounds is None else bounds
1824 impl=self.tag if impl is None else impl,
1825 expl=self._expl if expl is None else expl,
1826 default=self.default if default is None else default,
1827 optional=self.optional if optional is None else optional,
1832 self._assert_ready()
1836 octets = bytearray([0])
1840 octets = bytearray()
1842 octets.append((value & 0xFF) ^ 0xFF)
1844 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1847 octets = bytearray()
1849 octets.append(value & 0xFF)
1851 if octets[-1] & 0x80 > 0:
1854 octets = bytes(octets)
1856 bytes_len = ceil(value.bit_length() / 8) or 1
1859 octets = value.to_bytes(
1864 except OverflowError:
1868 return b"".join((self.tag, len_encode(len(octets)), octets))
1870 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1872 t, _, lv = tag_strip(tlv)
1873 except DecodeError as err:
1874 raise err.__class__(
1876 klass=self.__class__,
1877 decode_path=decode_path,
1882 klass=self.__class__,
1883 decode_path=decode_path,
1889 l, llen, v = len_decode(lv)
1890 except DecodeError as err:
1891 raise err.__class__(
1893 klass=self.__class__,
1894 decode_path=decode_path,
1898 raise NotEnoughData(
1899 "encoded length is longer than data",
1900 klass=self.__class__,
1901 decode_path=decode_path,
1905 raise NotEnoughData(
1907 klass=self.__class__,
1908 decode_path=decode_path,
1911 v, tail = v[:l], v[l:]
1912 first_octet = byte2int(v)
1914 second_octet = byte2int(v[1:])
1916 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1917 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1920 "non normalized integer",
1921 klass=self.__class__,
1922 decode_path=decode_path,
1927 if first_octet & 0x80 > 0:
1928 octets = bytearray()
1929 for octet in bytearray(v):
1930 octets.append(octet ^ 0xFF)
1931 for octet in octets:
1932 value = (value << 8) | octet
1936 for octet in bytearray(v):
1937 value = (value << 8) | octet
1939 value = int.from_bytes(v, byteorder="big", signed=True)
1941 obj = self.__class__(
1943 bounds=(self._bound_min, self._bound_max),
1946 default=self.default,
1947 optional=self.optional,
1949 _decoded=(offset, llen, l),
1951 except BoundsError as err:
1954 klass=self.__class__,
1955 decode_path=decode_path,
1961 return pp_console_row(next(self.pps()))
1963 def pps(self, decode_path=()):
1965 asn1_type_name=self.asn1_type_name,
1966 obj_name=self.__class__.__name__,
1967 decode_path=decode_path,
1968 value=(self.named or str(self._value)) if self.ready else None,
1969 optional=self.optional,
1970 default=self == self.default,
1971 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1972 expl=None if self._expl is None else tag_decode(self._expl),
1977 expl_offset=self.expl_offset if self.expled else None,
1978 expl_tlen=self.expl_tlen if self.expled else None,
1979 expl_llen=self.expl_llen if self.expled else None,
1980 expl_vlen=self.expl_vlen if self.expled else None,
1981 expl_lenindef=self.expl_lenindef,
1983 for pp in self.pps_lenindef(decode_path):
1987 class BitString(Obj):
1988 """``BIT STRING`` bit string type
1990 >>> BitString(b"hello world")
1991 BIT STRING 88 bits 68656c6c6f20776f726c64
1994 >>> b == b"hello world"
1999 >>> BitString("'0A3B5F291CD'H")
2000 BIT STRING 44 bits 0a3b5f291cd0
2001 >>> b = BitString("'010110000000'B")
2002 BIT STRING 12 bits 5800
2005 >>> b[0], b[1], b[2], b[3]
2006 (False, True, False, True)
2010 [False, True, False, True, True, False, False, False, False, False, False, False]
2014 class KeyUsage(BitString):
2016 ("digitalSignature", 0),
2017 ("nonRepudiation", 1),
2018 ("keyEncipherment", 2),
2021 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2022 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2024 ['nonRepudiation', 'keyEncipherment']
2026 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2030 Pay attention that BIT STRING can be encoded both in primitive
2031 and constructed forms. Decoder always checks constructed form tag
2032 additionally to specified primitive one. If BER decoding is
2033 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2034 of DER restrictions.
2036 __slots__ = ("tag_constructed", "specs", "defined")
2037 tag_default = tag_encode(3)
2038 asn1_type_name = "BIT STRING"
2051 :param value: set the value. Either binary type, tuple of named
2052 values (if ``schema`` is specified in the class),
2053 string in ``'XXX...'B`` form, or
2054 :py:class:`pyderasn.BitString` object
2055 :param bytes impl: override default tag with ``IMPLICIT`` one
2056 :param bytes expl: override default tag with ``EXPLICIT`` one
2057 :param default: set default value. Type same as in ``value``
2058 :param bool optional: is object ``OPTIONAL`` in sequence
2060 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2061 specs = getattr(self, "schema", {}) if _specs is None else _specs
2062 self.specs = specs if isinstance(specs, dict) else dict(specs)
2063 self._value = None if value is None else self._value_sanitize(value)
2064 if default is not None:
2065 default = self._value_sanitize(default)
2066 self.default = self.__class__(
2072 self._value = default
2074 tag_klass, _, tag_num = tag_decode(self.tag)
2075 self.tag_constructed = tag_encode(
2077 form=TagFormConstructed,
2081 def _bits2octets(self, bits):
2082 if len(self.specs) > 0:
2083 bits = bits.rstrip("0")
2085 bits += "0" * ((8 - (bit_len % 8)) % 8)
2086 octets = bytearray(len(bits) // 8)
2087 for i in six_xrange(len(octets)):
2088 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2089 return bit_len, bytes(octets)
2091 def _value_sanitize(self, value):
2092 if issubclass(value.__class__, BitString):
2094 if isinstance(value, (string_types, binary_type)):
2096 isinstance(value, string_types) and
2097 value.startswith("'")
2099 if value.endswith("'B"):
2101 if not set(value) <= set(("0", "1")):
2102 raise ValueError("B's coding contains unacceptable chars")
2103 return self._bits2octets(value)
2104 elif value.endswith("'H"):
2108 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2110 if isinstance(value, binary_type):
2111 return (len(value) * 8, value)
2113 raise InvalidValueType((self.__class__, string_types, binary_type))
2114 if isinstance(value, tuple):
2117 isinstance(value[0], integer_types) and
2118 isinstance(value[1], binary_type)
2123 bit = self.specs.get(name)
2125 raise ObjUnknown("BitString value: %s" % name)
2128 return self._bits2octets("")
2130 return self._bits2octets("".join(
2131 ("1" if bit in bits else "0")
2132 for bit in six_xrange(max(bits) + 1)
2134 raise InvalidValueType((self.__class__, binary_type, string_types))
2138 return self._value is not None
2141 obj = self.__class__(_specs=self.specs)
2143 if value is not None:
2144 value = (value[0], value[1])
2147 obj._expl = self._expl
2148 obj.default = self.default
2149 obj.optional = self.optional
2150 obj.offset = self.offset
2151 obj.llen = self.llen
2152 obj.vlen = self.vlen
2156 self._assert_ready()
2157 for i in six_xrange(self._value[0]):
2162 self._assert_ready()
2163 return self._value[0]
2165 def __bytes__(self):
2166 self._assert_ready()
2167 return self._value[1]
2169 def __eq__(self, their):
2170 if isinstance(their, bytes):
2171 return self._value[1] == their
2172 if not issubclass(their.__class__, BitString):
2175 self._value == their._value and
2176 self.tag == their.tag and
2177 self._expl == their._expl
2182 return [name for name, bit in self.specs.items() if self[bit]]
2192 return self.__class__(
2194 impl=self.tag if impl is None else impl,
2195 expl=self._expl if expl is None else expl,
2196 default=self.default if default is None else default,
2197 optional=self.optional if optional is None else optional,
2201 def __getitem__(self, key):
2202 if isinstance(key, int):
2203 bit_len, octets = self._value
2207 byte2int(memoryview(octets)[key // 8:]) >>
2210 if isinstance(key, string_types):
2211 value = self.specs.get(key)
2213 raise ObjUnknown("BitString value: %s" % key)
2215 raise InvalidValueType((int, str))
2218 self._assert_ready()
2219 bit_len, octets = self._value
2222 len_encode(len(octets) + 1),
2223 int2byte((8 - bit_len % 8) % 8),
2227 def _decode_chunk(self, lv, offset, decode_path, ctx):
2229 l, llen, v = len_decode(lv)
2230 except DecodeError as err:
2231 raise err.__class__(
2233 klass=self.__class__,
2234 decode_path=decode_path,
2238 raise NotEnoughData(
2239 "encoded length is longer than data",
2240 klass=self.__class__,
2241 decode_path=decode_path,
2245 raise NotEnoughData(
2247 klass=self.__class__,
2248 decode_path=decode_path,
2251 pad_size = byte2int(v)
2252 if l == 1 and pad_size != 0:
2254 "invalid empty value",
2255 klass=self.__class__,
2256 decode_path=decode_path,
2262 klass=self.__class__,
2263 decode_path=decode_path,
2266 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2269 klass=self.__class__,
2270 decode_path=decode_path,
2273 v, tail = v[:l], v[l:]
2274 obj = self.__class__(
2275 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2278 default=self.default,
2279 optional=self.optional,
2281 _decoded=(offset, llen, l),
2285 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2287 t, tlen, lv = tag_strip(tlv)
2288 except DecodeError as err:
2289 raise err.__class__(
2291 klass=self.__class__,
2292 decode_path=decode_path,
2298 return self._decode_chunk(lv, offset, decode_path, ctx)
2299 if t == self.tag_constructed:
2300 if not ctx.get("bered", False):
2302 "unallowed BER constructed encoding",
2303 klass=self.__class__,
2304 decode_path=decode_path,
2311 l, llen, v = len_decode(lv)
2312 except LenIndefForm:
2313 llen, l, v = 1, 0, lv[1:]
2315 except DecodeError as err:
2316 raise err.__class__(
2318 klass=self.__class__,
2319 decode_path=decode_path,
2323 raise NotEnoughData(
2324 "encoded length is longer than data",
2325 klass=self.__class__,
2326 decode_path=decode_path,
2329 if not lenindef and l == 0:
2330 raise NotEnoughData(
2332 klass=self.__class__,
2333 decode_path=decode_path,
2337 sub_offset = offset + tlen + llen
2341 if v[:EOC_LEN].tobytes() == EOC:
2348 "chunk out of bounds",
2349 klass=self.__class__,
2350 decode_path=decode_path + (str(len(chunks) - 1),),
2351 offset=chunks[-1].offset,
2353 sub_decode_path = decode_path + (str(len(chunks)),)
2355 chunk, v_tail = BitString().decode(
2358 decode_path=sub_decode_path,
2364 "expected BitString encoded chunk",
2365 klass=self.__class__,
2366 decode_path=sub_decode_path,
2369 chunks.append(chunk)
2370 sub_offset += chunk.tlvlen
2371 vlen += chunk.tlvlen
2373 if len(chunks) == 0:
2376 klass=self.__class__,
2377 decode_path=decode_path,
2382 for chunk_i, chunk in enumerate(chunks[:-1]):
2383 if chunk.bit_len % 8 != 0:
2385 "BitString chunk is not multiple of 8 bits",
2386 klass=self.__class__,
2387 decode_path=decode_path + (str(chunk_i),),
2388 offset=chunk.offset,
2390 values.append(bytes(chunk))
2391 bit_len += chunk.bit_len
2392 chunk_last = chunks[-1]
2393 values.append(bytes(chunk_last))
2394 bit_len += chunk_last.bit_len
2395 obj = self.__class__(
2396 value=(bit_len, b"".join(values)),
2399 default=self.default,
2400 optional=self.optional,
2402 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2404 obj.lenindef = lenindef
2406 return obj, (v[EOC_LEN:] if lenindef else v)
2408 klass=self.__class__,
2409 decode_path=decode_path,
2414 return pp_console_row(next(self.pps()))
2416 def pps(self, decode_path=()):
2420 bit_len, blob = self._value
2421 value = "%d bits" % bit_len
2422 if len(self.specs) > 0:
2423 blob = tuple(self.named)
2425 asn1_type_name=self.asn1_type_name,
2426 obj_name=self.__class__.__name__,
2427 decode_path=decode_path,
2430 optional=self.optional,
2431 default=self == self.default,
2432 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2433 expl=None if self._expl is None else tag_decode(self._expl),
2438 expl_offset=self.expl_offset if self.expled else None,
2439 expl_tlen=self.expl_tlen if self.expled else None,
2440 expl_llen=self.expl_llen if self.expled else None,
2441 expl_vlen=self.expl_vlen if self.expled else None,
2442 expl_lenindef=self.expl_lenindef,
2443 lenindef=self.lenindef,
2446 defined_by, defined = self.defined or (None, None)
2447 if defined_by is not None:
2449 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2451 for pp in self.pps_lenindef(decode_path):
2455 class OctetString(Obj):
2456 """``OCTET STRING`` binary string type
2458 >>> s = OctetString(b"hello world")
2459 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2460 >>> s == OctetString(b"hello world")
2465 >>> OctetString(b"hello", bounds=(4, 4))
2466 Traceback (most recent call last):
2467 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2468 >>> OctetString(b"hell", bounds=(4, 4))
2469 OCTET STRING 4 bytes 68656c6c
2473 Pay attention that OCTET STRING can be encoded both in primitive
2474 and constructed forms. Decoder always checks constructed form tag
2475 additionally to specified primitive one. If BER decoding is
2476 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2477 of DER restrictions.
2479 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2480 tag_default = tag_encode(4)
2481 asn1_type_name = "OCTET STRING"
2494 :param value: set the value. Either binary type, or
2495 :py:class:`pyderasn.OctetString` object
2496 :param bounds: set ``(MIN, MAX)`` value size constraint.
2497 (-inf, +inf) by default
2498 :param bytes impl: override default tag with ``IMPLICIT`` one
2499 :param bytes expl: override default tag with ``EXPLICIT`` one
2500 :param default: set default value. Type same as in ``value``
2501 :param bool optional: is object ``OPTIONAL`` in sequence
2503 super(OctetString, self).__init__(
2511 self._bound_min, self._bound_max = getattr(
2515 ) if bounds is None else bounds
2516 if value is not None:
2517 self._value = self._value_sanitize(value)
2518 if default is not None:
2519 default = self._value_sanitize(default)
2520 self.default = self.__class__(
2525 if self._value is None:
2526 self._value = default
2528 tag_klass, _, tag_num = tag_decode(self.tag)
2529 self.tag_constructed = tag_encode(
2531 form=TagFormConstructed,
2535 def _value_sanitize(self, value):
2536 if issubclass(value.__class__, OctetString):
2537 value = value._value
2538 elif isinstance(value, binary_type):
2541 raise InvalidValueType((self.__class__, bytes))
2542 if not self._bound_min <= len(value) <= self._bound_max:
2543 raise BoundsError(self._bound_min, len(value), self._bound_max)
2548 return self._value is not None
2551 obj = self.__class__()
2552 obj._value = self._value
2553 obj._bound_min = self._bound_min
2554 obj._bound_max = self._bound_max
2556 obj._expl = self._expl
2557 obj.default = self.default
2558 obj.optional = self.optional
2559 obj.offset = self.offset
2560 obj.llen = self.llen
2561 obj.vlen = self.vlen
2564 def __bytes__(self):
2565 self._assert_ready()
2568 def __eq__(self, their):
2569 if isinstance(their, binary_type):
2570 return self._value == their
2571 if not issubclass(their.__class__, OctetString):
2574 self._value == their._value and
2575 self.tag == their.tag and
2576 self._expl == their._expl
2579 def __lt__(self, their):
2580 return self._value < their._value
2591 return self.__class__(
2594 (self._bound_min, self._bound_max)
2595 if bounds is None else bounds
2597 impl=self.tag if impl is None else impl,
2598 expl=self._expl if expl is None else expl,
2599 default=self.default if default is None else default,
2600 optional=self.optional if optional is None else optional,
2604 self._assert_ready()
2607 len_encode(len(self._value)),
2611 def _decode_chunk(self, lv, offset, decode_path, ctx):
2613 l, llen, v = len_decode(lv)
2614 except DecodeError as err:
2615 raise err.__class__(
2617 klass=self.__class__,
2618 decode_path=decode_path,
2622 raise NotEnoughData(
2623 "encoded length is longer than data",
2624 klass=self.__class__,
2625 decode_path=decode_path,
2628 v, tail = v[:l], v[l:]
2630 obj = self.__class__(
2632 bounds=(self._bound_min, self._bound_max),
2635 default=self.default,
2636 optional=self.optional,
2637 _decoded=(offset, llen, l),
2639 except DecodeError as err:
2642 klass=self.__class__,
2643 decode_path=decode_path,
2646 except BoundsError as err:
2649 klass=self.__class__,
2650 decode_path=decode_path,
2655 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2657 t, tlen, lv = tag_strip(tlv)
2658 except DecodeError as err:
2659 raise err.__class__(
2661 klass=self.__class__,
2662 decode_path=decode_path,
2668 return self._decode_chunk(lv, offset, decode_path, ctx)
2669 if t == self.tag_constructed:
2670 if not ctx.get("bered", False):
2672 "unallowed BER constructed encoding",
2673 klass=self.__class__,
2674 decode_path=decode_path,
2681 l, llen, v = len_decode(lv)
2682 except LenIndefForm:
2683 llen, l, v = 1, 0, lv[1:]
2685 except DecodeError as err:
2686 raise err.__class__(
2688 klass=self.__class__,
2689 decode_path=decode_path,
2693 raise NotEnoughData(
2694 "encoded length is longer than data",
2695 klass=self.__class__,
2696 decode_path=decode_path,
2700 sub_offset = offset + tlen + llen
2704 if v[:EOC_LEN].tobytes() == EOC:
2711 "chunk out of bounds",
2712 klass=self.__class__,
2713 decode_path=decode_path + (str(len(chunks) - 1),),
2714 offset=chunks[-1].offset,
2716 sub_decode_path = decode_path + (str(len(chunks)),)
2718 chunk, v_tail = OctetString().decode(
2721 decode_path=sub_decode_path,
2727 "expected OctetString encoded chunk",
2728 klass=self.__class__,
2729 decode_path=sub_decode_path,
2732 chunks.append(chunk)
2733 sub_offset += chunk.tlvlen
2734 vlen += chunk.tlvlen
2737 obj = self.__class__(
2738 value=b"".join(bytes(chunk) for chunk in chunks),
2739 bounds=(self._bound_min, self._bound_max),
2742 default=self.default,
2743 optional=self.optional,
2744 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2746 except DecodeError as err:
2749 klass=self.__class__,
2750 decode_path=decode_path,
2753 except BoundsError as err:
2756 klass=self.__class__,
2757 decode_path=decode_path,
2760 obj.lenindef = lenindef
2762 return obj, (v[EOC_LEN:] if lenindef else v)
2764 klass=self.__class__,
2765 decode_path=decode_path,
2770 return pp_console_row(next(self.pps()))
2772 def pps(self, decode_path=()):
2774 asn1_type_name=self.asn1_type_name,
2775 obj_name=self.__class__.__name__,
2776 decode_path=decode_path,
2777 value=("%d bytes" % len(self._value)) if self.ready else None,
2778 blob=self._value if self.ready else None,
2779 optional=self.optional,
2780 default=self == self.default,
2781 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2782 expl=None if self._expl is None else tag_decode(self._expl),
2787 expl_offset=self.expl_offset if self.expled else None,
2788 expl_tlen=self.expl_tlen if self.expled else None,
2789 expl_llen=self.expl_llen if self.expled else None,
2790 expl_vlen=self.expl_vlen if self.expled else None,
2791 expl_lenindef=self.expl_lenindef,
2792 lenindef=self.lenindef,
2795 defined_by, defined = self.defined or (None, None)
2796 if defined_by is not None:
2798 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2800 for pp in self.pps_lenindef(decode_path):
2805 """``NULL`` null object
2813 tag_default = tag_encode(5)
2814 asn1_type_name = "NULL"
2818 value=None, # unused, but Sequence passes it
2825 :param bytes impl: override default tag with ``IMPLICIT`` one
2826 :param bytes expl: override default tag with ``EXPLICIT`` one
2827 :param bool optional: is object ``OPTIONAL`` in sequence
2829 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2837 obj = self.__class__()
2839 obj._expl = self._expl
2840 obj.default = self.default
2841 obj.optional = self.optional
2842 obj.offset = self.offset
2843 obj.llen = self.llen
2844 obj.vlen = self.vlen
2847 def __eq__(self, their):
2848 if not issubclass(their.__class__, Null):
2851 self.tag == their.tag and
2852 self._expl == their._expl
2862 return self.__class__(
2863 impl=self.tag if impl is None else impl,
2864 expl=self._expl if expl is None else expl,
2865 optional=self.optional if optional is None else optional,
2869 return self.tag + len_encode(0)
2871 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2873 t, _, lv = tag_strip(tlv)
2874 except DecodeError as err:
2875 raise err.__class__(
2877 klass=self.__class__,
2878 decode_path=decode_path,
2883 klass=self.__class__,
2884 decode_path=decode_path,
2890 l, _, v = len_decode(lv)
2891 except DecodeError as err:
2892 raise err.__class__(
2894 klass=self.__class__,
2895 decode_path=decode_path,
2899 raise InvalidLength(
2900 "Null must have zero length",
2901 klass=self.__class__,
2902 decode_path=decode_path,
2905 obj = self.__class__(
2908 optional=self.optional,
2909 _decoded=(offset, 1, 0),
2914 return pp_console_row(next(self.pps()))
2916 def pps(self, decode_path=()):
2918 asn1_type_name=self.asn1_type_name,
2919 obj_name=self.__class__.__name__,
2920 decode_path=decode_path,
2921 optional=self.optional,
2922 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2923 expl=None if self._expl is None else tag_decode(self._expl),
2928 expl_offset=self.expl_offset if self.expled else None,
2929 expl_tlen=self.expl_tlen if self.expled else None,
2930 expl_llen=self.expl_llen if self.expled else None,
2931 expl_vlen=self.expl_vlen if self.expled else None,
2932 expl_lenindef=self.expl_lenindef,
2934 for pp in self.pps_lenindef(decode_path):
2938 class ObjectIdentifier(Obj):
2939 """``OBJECT IDENTIFIER`` OID type
2941 >>> oid = ObjectIdentifier((1, 2, 3))
2942 OBJECT IDENTIFIER 1.2.3
2943 >>> oid == ObjectIdentifier("1.2.3")
2949 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2950 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2952 >>> str(ObjectIdentifier((3, 1)))
2953 Traceback (most recent call last):
2954 pyderasn.InvalidOID: unacceptable first arc value
2956 __slots__ = ("defines",)
2957 tag_default = tag_encode(6)
2958 asn1_type_name = "OBJECT IDENTIFIER"
2971 :param value: set the value. Either tuples of integers,
2972 string of "."-concatenated integers, or
2973 :py:class:`pyderasn.ObjectIdentifier` object
2974 :param defines: sequence of tuples. Each tuple has two elements.
2975 First one is relative to current one decode
2976 path, aiming to the field defined by that OID.
2977 Read about relative path in
2978 :py:func:`pyderasn.abs_decode_path`. Second
2979 tuple element is ``{OID: pyderasn.Obj()}``
2980 dictionary, mapping between current OID value
2981 and structure applied to defined field.
2982 :ref:`Read about DEFINED BY <definedby>`
2983 :param bytes impl: override default tag with ``IMPLICIT`` one
2984 :param bytes expl: override default tag with ``EXPLICIT`` one
2985 :param default: set default value. Type same as in ``value``
2986 :param bool optional: is object ``OPTIONAL`` in sequence
2988 super(ObjectIdentifier, self).__init__(
2996 if value is not None:
2997 self._value = self._value_sanitize(value)
2998 if default is not None:
2999 default = self._value_sanitize(default)
3000 self.default = self.__class__(
3005 if self._value is None:
3006 self._value = default
3007 self.defines = defines
3009 def __add__(self, their):
3010 if isinstance(their, self.__class__):
3011 return self.__class__(self._value + their._value)
3012 if isinstance(their, tuple):
3013 return self.__class__(self._value + their)
3014 raise InvalidValueType((self.__class__, tuple))
3016 def _value_sanitize(self, value):
3017 if issubclass(value.__class__, ObjectIdentifier):
3019 if isinstance(value, string_types):
3021 value = tuple(int(arc) for arc in value.split("."))
3023 raise InvalidOID("unacceptable arcs values")
3024 if isinstance(value, tuple):
3026 raise InvalidOID("less than 2 arcs")
3027 first_arc = value[0]
3028 if first_arc in (0, 1):
3029 if not (0 <= value[1] <= 39):
3030 raise InvalidOID("second arc is too wide")
3031 elif first_arc == 2:
3034 raise InvalidOID("unacceptable first arc value")
3036 raise InvalidValueType((self.__class__, str, tuple))
3040 return self._value is not None
3043 obj = self.__class__()
3044 obj._value = self._value
3045 obj.defines = self.defines
3047 obj._expl = self._expl
3048 obj.default = self.default
3049 obj.optional = self.optional
3050 obj.offset = self.offset
3051 obj.llen = self.llen
3052 obj.vlen = self.vlen
3056 self._assert_ready()
3057 return iter(self._value)
3060 return ".".join(str(arc) for arc in self._value or ())
3063 self._assert_ready()
3066 bytes(self._expl or b"") +
3067 str(self._value).encode("ascii"),
3070 def __eq__(self, their):
3071 if isinstance(their, tuple):
3072 return self._value == their
3073 if not issubclass(their.__class__, ObjectIdentifier):
3076 self.tag == their.tag and
3077 self._expl == their._expl and
3078 self._value == their._value
3081 def __lt__(self, their):
3082 return self._value < their._value
3093 return self.__class__(
3095 defines=self.defines if defines is None else defines,
3096 impl=self.tag if impl is None else impl,
3097 expl=self._expl if expl is None else expl,
3098 default=self.default if default is None else default,
3099 optional=self.optional if optional is None else optional,
3103 self._assert_ready()
3105 first_value = value[1]
3106 first_arc = value[0]
3109 elif first_arc == 1:
3111 elif first_arc == 2:
3113 else: # pragma: no cover
3114 raise RuntimeError("invalid arc is stored")
3115 octets = [zero_ended_encode(first_value)]
3116 for arc in value[2:]:
3117 octets.append(zero_ended_encode(arc))
3118 v = b"".join(octets)
3119 return b"".join((self.tag, len_encode(len(v)), v))
3121 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3123 t, _, lv = tag_strip(tlv)
3124 except DecodeError as err:
3125 raise err.__class__(
3127 klass=self.__class__,
3128 decode_path=decode_path,
3133 klass=self.__class__,
3134 decode_path=decode_path,
3140 l, llen, v = len_decode(lv)
3141 except DecodeError as err:
3142 raise err.__class__(
3144 klass=self.__class__,
3145 decode_path=decode_path,
3149 raise NotEnoughData(
3150 "encoded length is longer than data",
3151 klass=self.__class__,
3152 decode_path=decode_path,
3156 raise NotEnoughData(
3158 klass=self.__class__,
3159 decode_path=decode_path,
3162 v, tail = v[:l], v[l:]
3168 octet = indexbytes(v, i)
3169 arc = (arc << 7) | (octet & 0x7F)
3170 if octet & 0x80 == 0:
3178 klass=self.__class__,
3179 decode_path=decode_path,
3183 second_arc = arcs[0]
3184 if 0 <= second_arc <= 39:
3186 elif 40 <= second_arc <= 79:
3192 obj = self.__class__(
3193 value=tuple([first_arc, second_arc] + arcs[1:]),
3196 default=self.default,
3197 optional=self.optional,
3198 _decoded=(offset, llen, l),
3203 return pp_console_row(next(self.pps()))
3205 def pps(self, decode_path=()):
3207 asn1_type_name=self.asn1_type_name,
3208 obj_name=self.__class__.__name__,
3209 decode_path=decode_path,
3210 value=str(self) if self.ready else None,
3211 optional=self.optional,
3212 default=self == self.default,
3213 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3214 expl=None if self._expl is None else tag_decode(self._expl),
3219 expl_offset=self.expl_offset if self.expled else None,
3220 expl_tlen=self.expl_tlen if self.expled else None,
3221 expl_llen=self.expl_llen if self.expled else None,
3222 expl_vlen=self.expl_vlen if self.expled else None,
3223 expl_lenindef=self.expl_lenindef,
3225 for pp in self.pps_lenindef(decode_path):
3229 class Enumerated(Integer):
3230 """``ENUMERATED`` integer type
3232 This type is identical to :py:class:`pyderasn.Integer`, but requires
3233 schema to be specified and does not accept values missing from it.
3236 tag_default = tag_encode(10)
3237 asn1_type_name = "ENUMERATED"
3248 bounds=None, # dummy argument, workability for Integer.decode
3250 super(Enumerated, self).__init__(
3259 if len(self.specs) == 0:
3260 raise ValueError("schema must be specified")
3262 def _value_sanitize(self, value):
3263 if isinstance(value, self.__class__):
3264 value = value._value
3265 elif isinstance(value, integer_types):
3266 if value not in list(self.specs.values()):
3268 "unknown integer value: %s" % value,
3269 klass=self.__class__,
3271 elif isinstance(value, string_types):
3272 value = self.specs.get(value)
3274 raise ObjUnknown("integer value: %s" % value)
3276 raise InvalidValueType((self.__class__, int, str))
3280 obj = self.__class__(_specs=self.specs)
3281 obj._value = self._value
3282 obj._bound_min = self._bound_min
3283 obj._bound_max = self._bound_max
3285 obj._expl = self._expl
3286 obj.default = self.default
3287 obj.optional = self.optional
3288 obj.offset = self.offset
3289 obj.llen = self.llen
3290 obj.vlen = self.vlen
3302 return self.__class__(
3304 impl=self.tag if impl is None else impl,
3305 expl=self._expl if expl is None else expl,
3306 default=self.default if default is None else default,
3307 optional=self.optional if optional is None else optional,
3312 class CommonString(OctetString):
3313 """Common class for all strings
3315 Everything resembles :py:class:`pyderasn.OctetString`, except
3316 ability to deal with unicode text strings.
3318 >>> hexenc("привет мир".encode("utf-8"))
3319 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3320 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3322 >>> s = UTF8String("привет мир")
3323 UTF8String UTF8String привет мир
3325 'привет мир'
3326 >>> hexenc(bytes(s))
3327 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3329 >>> PrintableString("привет мир")
3330 Traceback (most recent call last):
3331 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3333 >>> BMPString("ада", bounds=(2, 2))
3334 Traceback (most recent call last):
3335 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3336 >>> s = BMPString("ад", bounds=(2, 2))
3339 >>> hexenc(bytes(s))
3347 * - :py:class:`pyderasn.UTF8String`
3349 * - :py:class:`pyderasn.NumericString`
3351 * - :py:class:`pyderasn.PrintableString`
3353 * - :py:class:`pyderasn.TeletexString`
3355 * - :py:class:`pyderasn.T61String`
3357 * - :py:class:`pyderasn.VideotexString`
3359 * - :py:class:`pyderasn.IA5String`
3361 * - :py:class:`pyderasn.GraphicString`
3363 * - :py:class:`pyderasn.VisibleString`
3365 * - :py:class:`pyderasn.ISO646String`
3367 * - :py:class:`pyderasn.GeneralString`
3369 * - :py:class:`pyderasn.UniversalString`
3371 * - :py:class:`pyderasn.BMPString`
3374 __slots__ = ("encoding",)
3376 def _value_sanitize(self, value):
3378 value_decoded = None
3379 if isinstance(value, self.__class__):
3380 value_raw = value._value
3381 elif isinstance(value, text_type):
3382 value_decoded = value
3383 elif isinstance(value, binary_type):
3386 raise InvalidValueType((self.__class__, text_type, binary_type))
3389 value_decoded.encode(self.encoding)
3390 if value_raw is None else value_raw
3393 value_raw.decode(self.encoding)
3394 if value_decoded is None else value_decoded
3396 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3397 raise DecodeError(str(err))
3398 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3406 def __eq__(self, their):
3407 if isinstance(their, binary_type):
3408 return self._value == their
3409 if isinstance(their, text_type):
3410 return self._value == their.encode(self.encoding)
3411 if not isinstance(their, self.__class__):
3414 self._value == their._value and
3415 self.tag == their.tag and
3416 self._expl == their._expl
3419 def __unicode__(self):
3421 return self._value.decode(self.encoding)
3422 return text_type(self._value)
3425 return pp_console_row(next(self.pps(no_unicode=PY2)))
3427 def pps(self, decode_path=(), no_unicode=False):
3430 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3432 asn1_type_name=self.asn1_type_name,
3433 obj_name=self.__class__.__name__,
3434 decode_path=decode_path,
3436 optional=self.optional,
3437 default=self == self.default,
3438 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3439 expl=None if self._expl is None else tag_decode(self._expl),
3444 expl_offset=self.expl_offset if self.expled else None,
3445 expl_tlen=self.expl_tlen if self.expled else None,
3446 expl_llen=self.expl_llen if self.expled else None,
3447 expl_vlen=self.expl_vlen if self.expled else None,
3448 expl_lenindef=self.expl_lenindef,
3450 for pp in self.pps_lenindef(decode_path):
3454 class UTF8String(CommonString):
3456 tag_default = tag_encode(12)
3458 asn1_type_name = "UTF8String"
3461 class NumericString(CommonString):
3464 Its value is properly sanitized: only ASCII digits can be stored.
3467 tag_default = tag_encode(18)
3469 asn1_type_name = "NumericString"
3470 allowable_chars = set(digits.encode("ascii"))
3472 def _value_sanitize(self, value):
3473 value = super(NumericString, self)._value_sanitize(value)
3474 if not set(value) <= self.allowable_chars:
3475 raise DecodeError("non-numeric value")
3479 class PrintableString(CommonString):
3481 tag_default = tag_encode(19)
3483 asn1_type_name = "PrintableString"
3486 class TeletexString(CommonString):
3488 tag_default = tag_encode(20)
3490 asn1_type_name = "TeletexString"
3493 class T61String(TeletexString):
3495 asn1_type_name = "T61String"
3498 class VideotexString(CommonString):
3500 tag_default = tag_encode(21)
3501 encoding = "iso-8859-1"
3502 asn1_type_name = "VideotexString"
3505 class IA5String(CommonString):
3507 tag_default = tag_encode(22)
3509 asn1_type_name = "IA5"
3512 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3513 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3514 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3517 class UTCTime(CommonString):
3518 """``UTCTime`` datetime type
3520 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3521 UTCTime UTCTime 2017-09-30T22:07:50
3527 datetime.datetime(2017, 9, 30, 22, 7, 50)
3528 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3529 datetime.datetime(1957, 9, 30, 22, 7, 50)
3532 tag_default = tag_encode(23)
3534 asn1_type_name = "UTCTime"
3536 fmt = "%y%m%d%H%M%SZ"
3546 bounds=None, # dummy argument, workability for OctetString.decode
3549 :param value: set the value. Either datetime type, or
3550 :py:class:`pyderasn.UTCTime` object
3551 :param bytes impl: override default tag with ``IMPLICIT`` one
3552 :param bytes expl: override default tag with ``EXPLICIT`` one
3553 :param default: set default value. Type same as in ``value``
3554 :param bool optional: is object ``OPTIONAL`` in sequence
3556 super(UTCTime, self).__init__(
3564 if value is not None:
3565 self._value = self._value_sanitize(value)
3566 if default is not None:
3567 default = self._value_sanitize(default)
3568 self.default = self.__class__(
3573 if self._value is None:
3574 self._value = default
3576 def _value_sanitize(self, value):
3577 if isinstance(value, self.__class__):
3579 if isinstance(value, datetime):
3580 return value.strftime(self.fmt).encode("ascii")
3581 if isinstance(value, binary_type):
3583 value_decoded = value.decode("ascii")
3584 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3585 raise DecodeError("invalid UTCTime encoding")
3586 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3588 datetime.strptime(value_decoded, self.fmt)
3589 except (TypeError, ValueError):
3590 raise DecodeError("invalid UTCTime format")
3593 raise DecodeError("invalid UTCTime length")
3594 raise InvalidValueType((self.__class__, datetime))
3596 def __eq__(self, their):
3597 if isinstance(their, binary_type):
3598 return self._value == their
3599 if isinstance(their, datetime):
3600 return self.todatetime() == their
3601 if not isinstance(their, self.__class__):
3604 self._value == their._value and
3605 self.tag == their.tag and
3606 self._expl == their._expl
3609 def todatetime(self):
3610 """Convert to datetime
3614 Pay attention that UTCTime can not hold full year, so all years
3615 having < 50 years are treated as 20xx, 19xx otherwise, according
3616 to X.509 recomendation.
3618 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3619 year = value.year % 100
3621 year=(2000 + year) if year < 50 else (1900 + year),
3625 minute=value.minute,
3626 second=value.second,
3630 return pp_console_row(next(self.pps()))
3632 def pps(self, decode_path=()):
3634 asn1_type_name=self.asn1_type_name,
3635 obj_name=self.__class__.__name__,
3636 decode_path=decode_path,
3637 value=self.todatetime().isoformat() if self.ready else None,
3638 optional=self.optional,
3639 default=self == self.default,
3640 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3641 expl=None if self._expl is None else tag_decode(self._expl),
3646 expl_offset=self.expl_offset if self.expled else None,
3647 expl_tlen=self.expl_tlen if self.expled else None,
3648 expl_llen=self.expl_llen if self.expled else None,
3649 expl_vlen=self.expl_vlen if self.expled else None,
3650 expl_lenindef=self.expl_lenindef,
3652 for pp in self.pps_lenindef(decode_path):
3656 class GeneralizedTime(UTCTime):
3657 """``GeneralizedTime`` datetime type
3659 This type is similar to :py:class:`pyderasn.UTCTime`.
3661 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3662 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3664 '20170930220750.000123Z'
3665 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3666 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3669 tag_default = tag_encode(24)
3670 asn1_type_name = "GeneralizedTime"
3672 fmt = "%Y%m%d%H%M%SZ"
3673 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3675 def _value_sanitize(self, value):
3676 if isinstance(value, self.__class__):
3678 if isinstance(value, datetime):
3679 return value.strftime(
3680 self.fmt_ms if value.microsecond > 0 else self.fmt
3682 if isinstance(value, binary_type):
3684 value_decoded = value.decode("ascii")
3685 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3686 raise DecodeError("invalid GeneralizedTime encoding")
3687 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3689 datetime.strptime(value_decoded, self.fmt)
3690 except (TypeError, ValueError):
3692 "invalid GeneralizedTime (without ms) format",
3695 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3697 datetime.strptime(value_decoded, self.fmt_ms)
3698 except (TypeError, ValueError):
3700 "invalid GeneralizedTime (with ms) format",
3705 "invalid GeneralizedTime length",
3706 klass=self.__class__,
3708 raise InvalidValueType((self.__class__, datetime))
3710 def todatetime(self):
3711 value = self._value.decode("ascii")
3712 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3713 return datetime.strptime(value, self.fmt)
3714 return datetime.strptime(value, self.fmt_ms)
3717 class GraphicString(CommonString):
3719 tag_default = tag_encode(25)
3720 encoding = "iso-8859-1"
3721 asn1_type_name = "GraphicString"
3724 class VisibleString(CommonString):
3726 tag_default = tag_encode(26)
3728 asn1_type_name = "VisibleString"
3731 class ISO646String(VisibleString):
3733 asn1_type_name = "ISO646String"
3736 class GeneralString(CommonString):
3738 tag_default = tag_encode(27)
3739 encoding = "iso-8859-1"
3740 asn1_type_name = "GeneralString"
3743 class UniversalString(CommonString):
3745 tag_default = tag_encode(28)
3746 encoding = "utf-32-be"
3747 asn1_type_name = "UniversalString"
3750 class BMPString(CommonString):
3752 tag_default = tag_encode(30)
3753 encoding = "utf-16-be"
3754 asn1_type_name = "BMPString"
3758 """``CHOICE`` special type
3762 class GeneralName(Choice):
3764 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3765 ("dNSName", IA5String(impl=tag_ctxp(2))),
3768 >>> gn = GeneralName()
3770 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3771 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3772 >>> gn["dNSName"] = IA5String("bar.baz")
3773 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3774 >>> gn["rfc822Name"]
3777 [2] IA5String IA5 bar.baz
3780 >>> gn.value == gn["dNSName"]
3783 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3785 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3786 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3788 __slots__ = ("specs",)
3790 asn1_type_name = "CHOICE"
3803 :param value: set the value. Either ``(choice, value)`` tuple, or
3804 :py:class:`pyderasn.Choice` object
3805 :param bytes impl: can not be set, do **not** use it
3806 :param bytes expl: override default tag with ``EXPLICIT`` one
3807 :param default: set default value. Type same as in ``value``
3808 :param bool optional: is object ``OPTIONAL`` in sequence
3810 if impl is not None:
3811 raise ValueError("no implicit tag allowed for CHOICE")
3812 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3814 schema = getattr(self, "schema", ())
3815 if len(schema) == 0:
3816 raise ValueError("schema must be specified")
3818 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3821 if value is not None:
3822 self._value = self._value_sanitize(value)
3823 if default is not None:
3824 default_value = self._value_sanitize(default)
3825 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3826 default_obj.specs = self.specs
3827 default_obj._value = default_value
3828 self.default = default_obj
3830 self._value = default_obj.copy()._value
3832 def _value_sanitize(self, value):
3833 if isinstance(value, self.__class__):
3835 if isinstance(value, tuple) and len(value) == 2:
3837 spec = self.specs.get(choice)
3839 raise ObjUnknown(choice)
3840 if not isinstance(obj, spec.__class__):
3841 raise InvalidValueType((spec,))
3842 return (choice, spec(obj))
3843 raise InvalidValueType((self.__class__, tuple))
3847 return self._value is not None and self._value[1].ready
3850 obj = self.__class__(schema=self.specs)
3851 obj._expl = self._expl
3852 obj.default = self.default
3853 obj.optional = self.optional
3854 obj.offset = self.offset
3855 obj.llen = self.llen
3856 obj.vlen = self.vlen
3858 if value is not None:
3859 obj._value = (value[0], value[1].copy())
3862 def __eq__(self, their):
3863 if isinstance(their, tuple) and len(their) == 2:
3864 return self._value == their
3865 if not isinstance(their, self.__class__):
3868 self.specs == their.specs and
3869 self._value == their._value
3879 return self.__class__(
3882 expl=self._expl if expl is None else expl,
3883 default=self.default if default is None else default,
3884 optional=self.optional if optional is None else optional,
3889 self._assert_ready()
3890 return self._value[0]
3894 self._assert_ready()
3895 return self._value[1]
3897 def __getitem__(self, key):
3898 if key not in self.specs:
3899 raise ObjUnknown(key)
3900 if self._value is None:
3902 choice, value = self._value
3907 def __setitem__(self, key, value):
3908 spec = self.specs.get(key)
3910 raise ObjUnknown(key)
3911 if not isinstance(value, spec.__class__):
3912 raise InvalidValueType((spec.__class__,))
3913 self._value = (key, spec(value))
3921 return self._value[1].decoded if self.ready else False
3924 self._assert_ready()
3925 return self._value[1].encode()
3927 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3928 for choice, spec in self.specs.items():
3929 sub_decode_path = decode_path + (choice,)
3935 decode_path=sub_decode_path,
3944 klass=self.__class__,
3945 decode_path=decode_path,
3950 value, tail = spec.decode(
3954 decode_path=sub_decode_path,
3957 obj = self.__class__(
3960 default=self.default,
3961 optional=self.optional,
3962 _decoded=(offset, 0, value.fulllen),
3964 obj._value = (choice, value)
3968 value = pp_console_row(next(self.pps()))
3970 value = "%s[%r]" % (value, self.value)
3973 def pps(self, decode_path=()):
3975 asn1_type_name=self.asn1_type_name,
3976 obj_name=self.__class__.__name__,
3977 decode_path=decode_path,
3978 value=self.choice if self.ready else None,
3979 optional=self.optional,
3980 default=self == self.default,
3981 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3982 expl=None if self._expl is None else tag_decode(self._expl),
3987 expl_lenindef=self.expl_lenindef,
3990 yield self.value.pps(decode_path=decode_path + (self.choice,))
3991 for pp in self.pps_lenindef(decode_path):
3995 class PrimitiveTypes(Choice):
3996 """Predefined ``CHOICE`` for all generic primitive types
3998 It could be useful for general decoding of some unspecified values:
4000 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4001 OCTET STRING 3 bytes 666f6f
4002 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4006 schema = tuple((klass.__name__, klass()) for klass in (
4031 """``ANY`` special type
4033 >>> Any(Integer(-123))
4035 >>> a = Any(OctetString(b"hello world").encode())
4036 ANY 040b68656c6c6f20776f726c64
4037 >>> hexenc(bytes(a))
4038 b'0x040x0bhello world'
4040 __slots__ = ("defined",)
4041 tag_default = tag_encode(0)
4042 asn1_type_name = "ANY"
4052 :param value: set the value. Either any kind of pyderasn's
4053 **ready** object, or bytes. Pay attention that
4054 **no** validation is performed is raw binary value
4056 :param bytes expl: override default tag with ``EXPLICIT`` one
4057 :param bool optional: is object ``OPTIONAL`` in sequence
4059 super(Any, self).__init__(None, expl, None, optional, _decoded)
4060 self._value = None if value is None else self._value_sanitize(value)
4063 def _value_sanitize(self, value):
4064 if isinstance(value, self.__class__):
4066 if isinstance(value, Obj):
4067 return value.encode()
4068 if isinstance(value, binary_type):
4070 raise InvalidValueType((self.__class__, Obj, binary_type))
4074 return self._value is not None
4077 obj = self.__class__()
4078 obj._value = self._value
4080 obj._expl = self._expl
4081 obj.optional = self.optional
4082 obj.offset = self.offset
4083 obj.llen = self.llen
4084 obj.vlen = self.vlen
4087 def __eq__(self, their):
4088 if isinstance(their, binary_type):
4089 return self._value == their
4090 if issubclass(their.__class__, Any):
4091 return self._value == their._value
4100 return self.__class__(
4102 expl=self._expl if expl is None else expl,
4103 optional=self.optional if optional is None else optional,
4106 def __bytes__(self):
4107 self._assert_ready()
4115 self._assert_ready()
4118 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4120 t, tlen, lv = tag_strip(tlv)
4121 except DecodeError as err:
4122 raise err.__class__(
4124 klass=self.__class__,
4125 decode_path=decode_path,
4129 l, llen, v = len_decode(lv)
4130 except LenIndefForm as err:
4131 if not ctx.get("bered", False):
4132 raise err.__class__(
4134 klass=self.__class__,
4135 decode_path=decode_path,
4138 llen, vlen, v = 1, 0, lv[1:]
4139 sub_offset = offset + tlen + llen
4141 while v[:EOC_LEN].tobytes() != EOC:
4142 chunk, v = Any().decode(
4145 decode_path=decode_path + (str(chunk_i),),
4149 vlen += chunk.tlvlen
4150 sub_offset += chunk.tlvlen
4152 tlvlen = tlen + llen + vlen + EOC_LEN
4153 obj = self.__class__(
4154 value=tlv[:tlvlen].tobytes(),
4156 optional=self.optional,
4157 _decoded=(offset, 0, tlvlen),
4161 return obj, v[EOC_LEN:]
4162 except DecodeError as err:
4163 raise err.__class__(
4165 klass=self.__class__,
4166 decode_path=decode_path,
4170 raise NotEnoughData(
4171 "encoded length is longer than data",
4172 klass=self.__class__,
4173 decode_path=decode_path,
4176 tlvlen = tlen + llen + l
4177 v, tail = tlv[:tlvlen], v[l:]
4178 obj = self.__class__(
4181 optional=self.optional,
4182 _decoded=(offset, 0, tlvlen),
4188 return pp_console_row(next(self.pps()))
4190 def pps(self, decode_path=()):
4192 asn1_type_name=self.asn1_type_name,
4193 obj_name=self.__class__.__name__,
4194 decode_path=decode_path,
4195 blob=self._value if self.ready else None,
4196 optional=self.optional,
4197 default=self == self.default,
4198 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4199 expl=None if self._expl is None else tag_decode(self._expl),
4204 expl_offset=self.expl_offset if self.expled else None,
4205 expl_tlen=self.expl_tlen if self.expled else None,
4206 expl_llen=self.expl_llen if self.expled else None,
4207 expl_vlen=self.expl_vlen if self.expled else None,
4208 expl_lenindef=self.expl_lenindef,
4209 lenindef=self.lenindef,
4211 defined_by, defined = self.defined or (None, None)
4212 if defined_by is not None:
4214 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4216 for pp in self.pps_lenindef(decode_path):
4220 ########################################################################
4221 # ASN.1 constructed types
4222 ########################################################################
4224 def get_def_by_path(defines_by_path, sub_decode_path):
4225 """Get define by decode path
4227 for path, define in defines_by_path:
4228 if len(path) != len(sub_decode_path):
4230 for p1, p2 in zip(path, sub_decode_path):
4231 if (p1 != any) and (p1 != p2):
4237 def abs_decode_path(decode_path, rel_path):
4238 """Create an absolute decode path from current and relative ones
4240 :param decode_path: current decode path, starting point.
4242 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4243 If first tuple's element is "/", then treat it as
4244 an absolute path, ignoring ``decode_path`` as
4245 starting point. Also this tuple can contain ".."
4246 elements, stripping the leading element from
4249 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4250 ("foo", "bar", "baz", "whatever")
4251 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4253 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4256 if rel_path[0] == "/":
4258 if rel_path[0] == "..":
4259 return abs_decode_path(decode_path[:-1], rel_path[1:])
4260 return decode_path + rel_path
4263 class Sequence(Obj):
4264 """``SEQUENCE`` structure type
4266 You have to make specification of sequence::
4268 class Extension(Sequence):
4270 ("extnID", ObjectIdentifier()),
4271 ("critical", Boolean(default=False)),
4272 ("extnValue", OctetString()),
4275 Then, you can work with it as with dictionary.
4277 >>> ext = Extension()
4278 >>> Extension().specs
4280 ('extnID', OBJECT IDENTIFIER),
4281 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4282 ('extnValue', OCTET STRING),
4284 >>> ext["extnID"] = "1.2.3"
4285 Traceback (most recent call last):
4286 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4287 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4289 You can determine if sequence is ready to be encoded:
4294 Traceback (most recent call last):
4295 pyderasn.ObjNotReady: object is not ready: extnValue
4296 >>> ext["extnValue"] = OctetString(b"foobar")
4300 Value you want to assign, must have the same **type** as in
4301 corresponding specification, but it can have different tags,
4302 optional/default attributes -- they will be taken from specification
4305 class TBSCertificate(Sequence):
4307 ("version", Version(expl=tag_ctxc(0), default="v1")),
4310 >>> tbs = TBSCertificate()
4311 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4313 Assign ``None`` to remove value from sequence.
4315 You can set values in Sequence during its initialization:
4317 >>> AlgorithmIdentifier((
4318 ("algorithm", ObjectIdentifier("1.2.3")),
4319 ("parameters", Any(Null()))
4321 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4323 You can determine if value exists/set in the sequence and take its value:
4325 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4328 OBJECT IDENTIFIER 1.2.3
4330 But pay attention that if value has default, then it won't be (not
4331 in) in the sequence (because ``DEFAULT`` must not be encoded in
4332 DER), but you can read its value:
4334 >>> "critical" in ext, ext["critical"]
4335 (False, BOOLEAN False)
4336 >>> ext["critical"] = Boolean(True)
4337 >>> "critical" in ext, ext["critical"]
4338 (True, BOOLEAN True)
4340 All defaulted values are always optional.
4342 .. _allow_default_values_ctx:
4344 DER prohibits default value encoding and will raise an error if
4345 default value is unexpectedly met during decode.
4346 If :ref:`bered <bered_ctx>` context option is set, then no error
4347 will be raised, but ``bered`` attribute set. You can disable strict
4348 defaulted values existence validation by setting
4349 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4351 Two sequences are equal if they have equal specification (schema),
4352 implicit/explicit tagging and the same values.
4354 __slots__ = ("specs",)
4355 tag_default = tag_encode(form=TagFormConstructed, num=16)
4356 asn1_type_name = "SEQUENCE"
4368 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4370 schema = getattr(self, "schema", ())
4372 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4375 if value is not None:
4376 if issubclass(value.__class__, Sequence):
4377 self._value = value._value
4378 elif hasattr(value, "__iter__"):
4379 for seq_key, seq_value in value:
4380 self[seq_key] = seq_value
4382 raise InvalidValueType((Sequence,))
4383 if default is not None:
4384 if not issubclass(default.__class__, Sequence):
4385 raise InvalidValueType((Sequence,))
4386 default_value = default._value
4387 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4388 default_obj.specs = self.specs
4389 default_obj._value = default_value
4390 self.default = default_obj
4392 self._value = default_obj.copy()._value
4396 for name, spec in self.specs.items():
4397 value = self._value.get(name)
4408 obj = self.__class__(schema=self.specs)
4410 obj._expl = self._expl
4411 obj.default = self.default
4412 obj.optional = self.optional
4413 obj.offset = self.offset
4414 obj.llen = self.llen
4415 obj.vlen = self.vlen
4416 obj._value = {k: v.copy() for k, v in self._value.items()}
4419 def __eq__(self, their):
4420 if not isinstance(their, self.__class__):
4423 self.specs == their.specs and
4424 self.tag == their.tag and
4425 self._expl == their._expl and
4426 self._value == their._value
4437 return self.__class__(
4440 impl=self.tag if impl is None else impl,
4441 expl=self._expl if expl is None else expl,
4442 default=self.default if default is None else default,
4443 optional=self.optional if optional is None else optional,
4446 def __contains__(self, key):
4447 return key in self._value
4449 def __setitem__(self, key, value):
4450 spec = self.specs.get(key)
4452 raise ObjUnknown(key)
4454 self._value.pop(key, None)
4456 if not isinstance(value, spec.__class__):
4457 raise InvalidValueType((spec.__class__,))
4458 value = spec(value=value)
4459 if spec.default is not None and value == spec.default:
4460 self._value.pop(key, None)
4462 self._value[key] = value
4464 def __getitem__(self, key):
4465 value = self._value.get(key)
4466 if value is not None:
4468 spec = self.specs.get(key)
4470 raise ObjUnknown(key)
4471 if spec.default is not None:
4475 def _encoded_values(self):
4477 for name, spec in self.specs.items():
4478 value = self._value.get(name)
4482 raise ObjNotReady(name)
4483 raws.append(value.encode())
4487 v = b"".join(self._encoded_values())
4488 return b"".join((self.tag, len_encode(len(v)), v))
4490 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4492 t, tlen, lv = tag_strip(tlv)
4493 except DecodeError as err:
4494 raise err.__class__(
4496 klass=self.__class__,
4497 decode_path=decode_path,
4502 klass=self.__class__,
4503 decode_path=decode_path,
4509 ctx_bered = ctx.get("bered", False)
4511 l, llen, v = len_decode(lv)
4512 except LenIndefForm as err:
4514 raise err.__class__(
4516 klass=self.__class__,
4517 decode_path=decode_path,
4520 l, llen, v = 0, 1, lv[1:]
4522 except DecodeError as err:
4523 raise err.__class__(
4525 klass=self.__class__,
4526 decode_path=decode_path,
4530 raise NotEnoughData(
4531 "encoded length is longer than data",
4532 klass=self.__class__,
4533 decode_path=decode_path,
4537 v, tail = v[:l], v[l:]
4539 sub_offset = offset + tlen + llen
4542 ctx_allow_default_values = ctx.get("allow_default_values", False)
4543 for name, spec in self.specs.items():
4544 if spec.optional and (
4545 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4549 sub_decode_path = decode_path + (name,)
4551 value, v_tail = spec.decode(
4555 decode_path=sub_decode_path,
4563 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4564 if defined is not None:
4565 defined_by, defined_spec = defined
4566 if issubclass(value.__class__, SequenceOf):
4567 for i, _value in enumerate(value):
4568 sub_sub_decode_path = sub_decode_path + (
4570 DecodePathDefBy(defined_by),
4572 defined_value, defined_tail = defined_spec.decode(
4573 memoryview(bytes(_value)),
4575 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4576 if value.expled else (value.tlen + value.llen)
4579 decode_path=sub_sub_decode_path,
4582 if len(defined_tail) > 0:
4585 klass=self.__class__,
4586 decode_path=sub_sub_decode_path,
4589 _value.defined = (defined_by, defined_value)
4591 defined_value, defined_tail = defined_spec.decode(
4592 memoryview(bytes(value)),
4594 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4595 if value.expled else (value.tlen + value.llen)
4598 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4601 if len(defined_tail) > 0:
4604 klass=self.__class__,
4605 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4608 value.defined = (defined_by, defined_value)
4610 value_len = value.fulllen
4612 sub_offset += value_len
4614 if spec.default is not None and value == spec.default:
4615 if ctx_bered or ctx_allow_default_values:
4619 "DEFAULT value met",
4620 klass=self.__class__,
4621 decode_path=sub_decode_path,
4624 values[name] = value
4626 spec_defines = getattr(spec, "defines", ())
4627 if len(spec_defines) == 0:
4628 defines_by_path = ctx.get("defines_by_path", ())
4629 if len(defines_by_path) > 0:
4630 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4631 if spec_defines is not None and len(spec_defines) > 0:
4632 for rel_path, schema in spec_defines:
4633 defined = schema.get(value, None)
4634 if defined is not None:
4635 ctx.setdefault("_defines", []).append((
4636 abs_decode_path(sub_decode_path[:-1], rel_path),
4640 if v[:EOC_LEN].tobytes() != EOC:
4643 klass=self.__class__,
4644 decode_path=decode_path,
4652 klass=self.__class__,
4653 decode_path=decode_path,
4656 obj = self.__class__(
4660 default=self.default,
4661 optional=self.optional,
4662 _decoded=(offset, llen, vlen),
4665 obj.lenindef = lenindef
4670 value = pp_console_row(next(self.pps()))
4672 for name in self.specs:
4673 _value = self._value.get(name)
4676 cols.append("%s: %s" % (name, repr(_value)))
4677 return "%s[%s]" % (value, "; ".join(cols))
4679 def pps(self, decode_path=()):
4681 asn1_type_name=self.asn1_type_name,
4682 obj_name=self.__class__.__name__,
4683 decode_path=decode_path,
4684 optional=self.optional,
4685 default=self == self.default,
4686 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4687 expl=None if self._expl is None else tag_decode(self._expl),
4692 expl_offset=self.expl_offset if self.expled else None,
4693 expl_tlen=self.expl_tlen if self.expled else None,
4694 expl_llen=self.expl_llen if self.expled else None,
4695 expl_vlen=self.expl_vlen if self.expled else None,
4696 expl_lenindef=self.expl_lenindef,
4697 lenindef=self.lenindef,
4699 for name in self.specs:
4700 value = self._value.get(name)
4703 yield value.pps(decode_path=decode_path + (name,))
4704 for pp in self.pps_lenindef(decode_path):
4708 class Set(Sequence):
4709 """``SET`` structure type
4711 Its usage is identical to :py:class:`pyderasn.Sequence`.
4713 .. _allow_unordered_set_ctx:
4715 DER prohibits unordered values encoding and will raise an error
4716 during decode. If If :ref:`bered <bered_ctx>` context option is set,
4717 then no error will occure. Also you can disable strict values
4718 ordering check by setting ``"allow_unordered_set": True``
4719 :ref:`context <ctx>` option.
4722 tag_default = tag_encode(form=TagFormConstructed, num=17)
4723 asn1_type_name = "SET"
4726 raws = self._encoded_values()
4729 return b"".join((self.tag, len_encode(len(v)), v))
4731 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4733 t, tlen, lv = tag_strip(tlv)
4734 except DecodeError as err:
4735 raise err.__class__(
4737 klass=self.__class__,
4738 decode_path=decode_path,
4743 klass=self.__class__,
4744 decode_path=decode_path,
4750 ctx_bered = ctx.get("bered", False)
4752 l, llen, v = len_decode(lv)
4753 except LenIndefForm as err:
4755 raise err.__class__(
4757 klass=self.__class__,
4758 decode_path=decode_path,
4761 l, llen, v = 0, 1, lv[1:]
4763 except DecodeError as err:
4764 raise err.__class__(
4766 klass=self.__class__,
4767 decode_path=decode_path,
4771 raise NotEnoughData(
4772 "encoded length is longer than data",
4773 klass=self.__class__,
4777 v, tail = v[:l], v[l:]
4779 sub_offset = offset + tlen + llen
4782 ctx_allow_default_values = ctx.get("allow_default_values", False)
4783 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4784 value_prev = memoryview(v[:0])
4785 specs_items = self.specs.items
4787 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4789 for name, spec in specs_items():
4790 sub_decode_path = decode_path + (name,)
4796 decode_path=sub_decode_path,
4805 klass=self.__class__,
4806 decode_path=decode_path,
4809 value, v_tail = spec.decode(
4813 decode_path=sub_decode_path,
4816 value_len = value.fulllen
4817 if value_prev.tobytes() > v[:value_len].tobytes():
4818 if ctx_bered or ctx_allow_unordered_set:
4822 "unordered " + self.asn1_type_name,
4823 klass=self.__class__,
4824 decode_path=sub_decode_path,
4827 if spec.default is None or value != spec.default:
4829 elif ctx_bered or ctx_allow_default_values:
4833 "DEFAULT value met",
4834 klass=self.__class__,
4835 decode_path=sub_decode_path,
4838 values[name] = value
4839 value_prev = v[:value_len]
4840 sub_offset += value_len
4843 obj = self.__class__(
4847 default=self.default,
4848 optional=self.optional,
4849 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4852 if v[:EOC_LEN].tobytes() != EOC:
4855 klass=self.__class__,
4856 decode_path=decode_path,
4864 "not all values are ready",
4865 klass=self.__class__,
4866 decode_path=decode_path,
4873 class SequenceOf(Obj):
4874 """``SEQUENCE OF`` sequence type
4876 For that kind of type you must specify the object it will carry on
4877 (bounds are for example here, not required)::
4879 class Ints(SequenceOf):
4884 >>> ints.append(Integer(123))
4885 >>> ints.append(Integer(234))
4887 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4888 >>> [int(i) for i in ints]
4890 >>> ints.append(Integer(345))
4891 Traceback (most recent call last):
4892 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4895 >>> ints[1] = Integer(345)
4897 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4899 Also you can initialize sequence with preinitialized values:
4901 >>> ints = Ints([Integer(123), Integer(234)])
4903 __slots__ = ("spec", "_bound_min", "_bound_max")
4904 tag_default = tag_encode(form=TagFormConstructed, num=16)
4905 asn1_type_name = "SEQUENCE OF"
4918 super(SequenceOf, self).__init__(
4926 schema = getattr(self, "schema", None)
4928 raise ValueError("schema must be specified")
4930 self._bound_min, self._bound_max = getattr(
4934 ) if bounds is None else bounds
4936 if value is not None:
4937 self._value = self._value_sanitize(value)
4938 if default is not None:
4939 default_value = self._value_sanitize(default)
4940 default_obj = self.__class__(
4945 default_obj._value = default_value
4946 self.default = default_obj
4948 self._value = default_obj.copy()._value
4950 def _value_sanitize(self, value):
4951 if issubclass(value.__class__, SequenceOf):
4952 value = value._value
4953 elif hasattr(value, "__iter__"):
4956 raise InvalidValueType((self.__class__, iter))
4957 if not self._bound_min <= len(value) <= self._bound_max:
4958 raise BoundsError(self._bound_min, len(value), self._bound_max)
4960 if not isinstance(v, self.spec.__class__):
4961 raise InvalidValueType((self.spec.__class__,))
4966 return all(v.ready for v in self._value)
4969 obj = self.__class__(schema=self.spec)
4970 obj._bound_min = self._bound_min
4971 obj._bound_max = self._bound_max
4973 obj._expl = self._expl
4974 obj.default = self.default
4975 obj.optional = self.optional
4976 obj.offset = self.offset
4977 obj.llen = self.llen
4978 obj.vlen = self.vlen
4979 obj._value = [v.copy() for v in self._value]
4982 def __eq__(self, their):
4983 if isinstance(their, self.__class__):
4985 self.spec == their.spec and
4986 self.tag == their.tag and
4987 self._expl == their._expl and
4988 self._value == their._value
4990 if hasattr(their, "__iter__"):
4991 return self._value == list(their)
5003 return self.__class__(
5007 (self._bound_min, self._bound_max)
5008 if bounds is None else bounds
5010 impl=self.tag if impl is None else impl,
5011 expl=self._expl if expl is None else expl,
5012 default=self.default if default is None else default,
5013 optional=self.optional if optional is None else optional,
5016 def __contains__(self, key):
5017 return key in self._value
5019 def append(self, value):
5020 if not isinstance(value, self.spec.__class__):
5021 raise InvalidValueType((self.spec.__class__,))
5022 if len(self._value) + 1 > self._bound_max:
5025 len(self._value) + 1,
5028 self._value.append(value)
5031 self._assert_ready()
5032 return iter(self._value)
5035 self._assert_ready()
5036 return len(self._value)
5038 def __setitem__(self, key, value):
5039 if not isinstance(value, self.spec.__class__):
5040 raise InvalidValueType((self.spec.__class__,))
5041 self._value[key] = self.spec(value=value)
5043 def __getitem__(self, key):
5044 return self._value[key]
5046 def _encoded_values(self):
5047 return [v.encode() for v in self._value]
5050 v = b"".join(self._encoded_values())
5051 return b"".join((self.tag, len_encode(len(v)), v))
5053 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5055 t, tlen, lv = tag_strip(tlv)
5056 except DecodeError as err:
5057 raise err.__class__(
5059 klass=self.__class__,
5060 decode_path=decode_path,
5065 klass=self.__class__,
5066 decode_path=decode_path,
5073 l, llen, v = len_decode(lv)
5074 except LenIndefForm as err:
5075 if not ctx.get("bered", False):
5076 raise err.__class__(
5078 klass=self.__class__,
5079 decode_path=decode_path,
5082 l, llen, v = 0, 1, lv[1:]
5084 except DecodeError as err:
5085 raise err.__class__(
5087 klass=self.__class__,
5088 decode_path=decode_path,
5092 raise NotEnoughData(
5093 "encoded length is longer than data",
5094 klass=self.__class__,
5095 decode_path=decode_path,
5099 v, tail = v[:l], v[l:]
5101 sub_offset = offset + tlen + llen
5105 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5107 value, v_tail = spec.decode(
5111 decode_path=decode_path + (str(len(_value)),),
5114 value_len = value.fulllen
5115 sub_offset += value_len
5118 _value.append(value)
5120 obj = self.__class__(
5123 bounds=(self._bound_min, self._bound_max),
5126 default=self.default,
5127 optional=self.optional,
5128 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5130 except BoundsError as err:
5133 klass=self.__class__,
5134 decode_path=decode_path,
5138 if v[:EOC_LEN].tobytes() != EOC:
5141 klass=self.__class__,
5142 decode_path=decode_path,
5151 pp_console_row(next(self.pps())),
5152 ", ".join(repr(v) for v in self._value),
5155 def pps(self, decode_path=()):
5157 asn1_type_name=self.asn1_type_name,
5158 obj_name=self.__class__.__name__,
5159 decode_path=decode_path,
5160 optional=self.optional,
5161 default=self == self.default,
5162 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5163 expl=None if self._expl is None else tag_decode(self._expl),
5168 expl_offset=self.expl_offset if self.expled else None,
5169 expl_tlen=self.expl_tlen if self.expled else None,
5170 expl_llen=self.expl_llen if self.expled else None,
5171 expl_vlen=self.expl_vlen if self.expled else None,
5172 expl_lenindef=self.expl_lenindef,
5173 lenindef=self.lenindef,
5175 for i, value in enumerate(self._value):
5176 yield value.pps(decode_path=decode_path + (str(i),))
5177 for pp in self.pps_lenindef(decode_path):
5181 class SetOf(SequenceOf):
5182 """``SET OF`` sequence type
5184 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5187 tag_default = tag_encode(form=TagFormConstructed, num=17)
5188 asn1_type_name = "SET OF"
5191 raws = self._encoded_values()
5194 return b"".join((self.tag, len_encode(len(v)), v))
5197 def obj_by_path(pypath): # pragma: no cover
5198 """Import object specified as string Python path
5200 Modules must be separated from classes/functions with ``:``.
5202 >>> obj_by_path("foo.bar:Baz")
5203 <class 'foo.bar.Baz'>
5204 >>> obj_by_path("foo.bar:Baz.boo")
5205 <classmethod 'foo.bar.Baz.boo'>
5207 mod, objs = pypath.rsplit(":", 1)
5208 from importlib import import_module
5209 obj = import_module(mod)
5210 for obj_name in objs.split("."):
5211 obj = getattr(obj, obj_name)
5215 def generic_decoder(): # pragma: no cover
5216 # All of this below is a big hack with self references
5217 choice = PrimitiveTypes()
5218 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5219 choice.specs["SetOf"] = SetOf(schema=choice)
5221 choice.specs["SequenceOf%d" % i] = SequenceOf(
5225 choice.specs["Any"] = Any()
5227 # Class name equals to type name, to omit it from output
5228 class SEQUENCEOF(SequenceOf):
5236 with_decode_path=False,
5237 decode_path_only=(),
5239 def _pprint_pps(pps):
5241 if hasattr(pp, "_fields"):
5243 decode_path_only != () and
5244 pp.decode_path[:len(decode_path_only)] != decode_path_only
5247 if pp.asn1_type_name == Choice.asn1_type_name:
5249 pp_kwargs = pp._asdict()
5250 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5251 pp = _pp(**pp_kwargs)
5252 yield pp_console_row(
5257 with_colours=with_colours,
5258 with_decode_path=with_decode_path,
5259 decode_path_len_decrease=len(decode_path_only),
5261 for row in pp_console_blob(
5263 decode_path_len_decrease=len(decode_path_only),
5267 for row in _pprint_pps(pp):
5269 return "\n".join(_pprint_pps(obj.pps()))
5270 return SEQUENCEOF(), pprint_any
5273 def main(): # pragma: no cover
5275 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5276 parser.add_argument(
5280 help="Skip that number of bytes from the beginning",
5282 parser.add_argument(
5284 help="Python path to dictionary with OIDs",
5286 parser.add_argument(
5288 help="Python path to schema definition to use",
5290 parser.add_argument(
5291 "--defines-by-path",
5292 help="Python path to decoder's defines_by_path",
5294 parser.add_argument(
5296 action="store_true",
5297 help="Disallow BER encoding",
5299 parser.add_argument(
5300 "--print-decode-path",
5301 action="store_true",
5302 help="Print decode paths",
5304 parser.add_argument(
5305 "--decode-path-only",
5306 help="Print only specified decode path",
5308 parser.add_argument(
5310 action="store_true",
5311 help="Allow explicit tag out-of-bound",
5313 parser.add_argument(
5315 type=argparse.FileType("rb"),
5316 help="Path to DER file you want to decode",
5318 args = parser.parse_args()
5319 args.DERFile.seek(args.skip)
5320 der = memoryview(args.DERFile.read())
5321 args.DERFile.close()
5322 oids = obj_by_path(args.oids) if args.oids else {}
5324 schema = obj_by_path(args.schema)
5325 from functools import partial
5326 pprinter = partial(pprint, big_blobs=True)
5328 schema, pprinter = generic_decoder()
5330 "bered": not args.nobered,
5331 "allow_expl_oob": args.allow_expl_oob,
5333 if args.defines_by_path is not None:
5334 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5335 obj, tail = schema().decode(der, ctx=ctx)
5339 with_colours=True if environ.get("NO_COLOR") is None else False,
5340 with_decode_path=args.print_decode_path,
5342 () if args.decode_path_only is None else
5343 tuple(args.decode_path_only.split(":"))
5347 print("\nTrailing data: %s" % hexenc(tail))
5350 if __name__ == "__main__":