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``, ``SET OF`` can contain it.
388 * If object has an indefinite length encoding, then its ``lenindef``
389 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
390 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
392 * If object has an indefinite length encoded explicit tag, then
393 ``expl_lenindef`` is set to True.
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)
3965 obj.lenindef = value.lenindef
3966 obj.bered = value.bered
3970 value = pp_console_row(next(self.pps()))
3972 value = "%s[%r]" % (value, self.value)
3975 def pps(self, decode_path=()):
3977 asn1_type_name=self.asn1_type_name,
3978 obj_name=self.__class__.__name__,
3979 decode_path=decode_path,
3980 value=self.choice if self.ready else None,
3981 optional=self.optional,
3982 default=self == self.default,
3983 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3984 expl=None if self._expl is None else tag_decode(self._expl),
3989 expl_lenindef=self.expl_lenindef,
3990 lenindef=self.lenindef,
3994 yield self.value.pps(decode_path=decode_path + (self.choice,))
3995 for pp in self.pps_lenindef(decode_path):
3999 class PrimitiveTypes(Choice):
4000 """Predefined ``CHOICE`` for all generic primitive types
4002 It could be useful for general decoding of some unspecified values:
4004 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4005 OCTET STRING 3 bytes 666f6f
4006 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4010 schema = tuple((klass.__name__, klass()) for klass in (
4035 """``ANY`` special type
4037 >>> Any(Integer(-123))
4039 >>> a = Any(OctetString(b"hello world").encode())
4040 ANY 040b68656c6c6f20776f726c64
4041 >>> hexenc(bytes(a))
4042 b'0x040x0bhello world'
4044 __slots__ = ("defined",)
4045 tag_default = tag_encode(0)
4046 asn1_type_name = "ANY"
4056 :param value: set the value. Either any kind of pyderasn's
4057 **ready** object, or bytes. Pay attention that
4058 **no** validation is performed is raw binary value
4060 :param bytes expl: override default tag with ``EXPLICIT`` one
4061 :param bool optional: is object ``OPTIONAL`` in sequence
4063 super(Any, self).__init__(None, expl, None, optional, _decoded)
4064 self._value = None if value is None else self._value_sanitize(value)
4067 def _value_sanitize(self, value):
4068 if isinstance(value, self.__class__):
4070 if isinstance(value, Obj):
4071 return value.encode()
4072 if isinstance(value, binary_type):
4074 raise InvalidValueType((self.__class__, Obj, binary_type))
4078 return self._value is not None
4081 obj = self.__class__()
4082 obj._value = self._value
4084 obj._expl = self._expl
4085 obj.optional = self.optional
4086 obj.offset = self.offset
4087 obj.llen = self.llen
4088 obj.vlen = self.vlen
4091 def __eq__(self, their):
4092 if isinstance(their, binary_type):
4093 return self._value == their
4094 if issubclass(their.__class__, Any):
4095 return self._value == their._value
4104 return self.__class__(
4106 expl=self._expl if expl is None else expl,
4107 optional=self.optional if optional is None else optional,
4110 def __bytes__(self):
4111 self._assert_ready()
4119 self._assert_ready()
4122 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4124 t, tlen, lv = tag_strip(tlv)
4125 except DecodeError as err:
4126 raise err.__class__(
4128 klass=self.__class__,
4129 decode_path=decode_path,
4133 l, llen, v = len_decode(lv)
4134 except LenIndefForm as err:
4135 if not ctx.get("bered", False):
4136 raise err.__class__(
4138 klass=self.__class__,
4139 decode_path=decode_path,
4142 llen, vlen, v = 1, 0, lv[1:]
4143 sub_offset = offset + tlen + llen
4145 while v[:EOC_LEN].tobytes() != EOC:
4146 chunk, v = Any().decode(
4149 decode_path=decode_path + (str(chunk_i),),
4153 vlen += chunk.tlvlen
4154 sub_offset += chunk.tlvlen
4156 tlvlen = tlen + llen + vlen + EOC_LEN
4157 obj = self.__class__(
4158 value=tlv[:tlvlen].tobytes(),
4160 optional=self.optional,
4161 _decoded=(offset, 0, tlvlen),
4165 return obj, v[EOC_LEN:]
4166 except DecodeError as err:
4167 raise err.__class__(
4169 klass=self.__class__,
4170 decode_path=decode_path,
4174 raise NotEnoughData(
4175 "encoded length is longer than data",
4176 klass=self.__class__,
4177 decode_path=decode_path,
4180 tlvlen = tlen + llen + l
4181 v, tail = tlv[:tlvlen], v[l:]
4182 obj = self.__class__(
4185 optional=self.optional,
4186 _decoded=(offset, 0, tlvlen),
4192 return pp_console_row(next(self.pps()))
4194 def pps(self, decode_path=()):
4196 asn1_type_name=self.asn1_type_name,
4197 obj_name=self.__class__.__name__,
4198 decode_path=decode_path,
4199 blob=self._value if self.ready else None,
4200 optional=self.optional,
4201 default=self == self.default,
4202 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4203 expl=None if self._expl is None else tag_decode(self._expl),
4208 expl_offset=self.expl_offset if self.expled else None,
4209 expl_tlen=self.expl_tlen if self.expled else None,
4210 expl_llen=self.expl_llen if self.expled else None,
4211 expl_vlen=self.expl_vlen if self.expled else None,
4212 expl_lenindef=self.expl_lenindef,
4213 lenindef=self.lenindef,
4215 defined_by, defined = self.defined or (None, None)
4216 if defined_by is not None:
4218 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4220 for pp in self.pps_lenindef(decode_path):
4224 ########################################################################
4225 # ASN.1 constructed types
4226 ########################################################################
4228 def get_def_by_path(defines_by_path, sub_decode_path):
4229 """Get define by decode path
4231 for path, define in defines_by_path:
4232 if len(path) != len(sub_decode_path):
4234 for p1, p2 in zip(path, sub_decode_path):
4235 if (p1 != any) and (p1 != p2):
4241 def abs_decode_path(decode_path, rel_path):
4242 """Create an absolute decode path from current and relative ones
4244 :param decode_path: current decode path, starting point.
4246 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4247 If first tuple's element is "/", then treat it as
4248 an absolute path, ignoring ``decode_path`` as
4249 starting point. Also this tuple can contain ".."
4250 elements, stripping the leading element from
4253 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4254 ("foo", "bar", "baz", "whatever")
4255 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4257 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4260 if rel_path[0] == "/":
4262 if rel_path[0] == "..":
4263 return abs_decode_path(decode_path[:-1], rel_path[1:])
4264 return decode_path + rel_path
4267 class Sequence(Obj):
4268 """``SEQUENCE`` structure type
4270 You have to make specification of sequence::
4272 class Extension(Sequence):
4274 ("extnID", ObjectIdentifier()),
4275 ("critical", Boolean(default=False)),
4276 ("extnValue", OctetString()),
4279 Then, you can work with it as with dictionary.
4281 >>> ext = Extension()
4282 >>> Extension().specs
4284 ('extnID', OBJECT IDENTIFIER),
4285 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4286 ('extnValue', OCTET STRING),
4288 >>> ext["extnID"] = "1.2.3"
4289 Traceback (most recent call last):
4290 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4291 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4293 You can determine if sequence is ready to be encoded:
4298 Traceback (most recent call last):
4299 pyderasn.ObjNotReady: object is not ready: extnValue
4300 >>> ext["extnValue"] = OctetString(b"foobar")
4304 Value you want to assign, must have the same **type** as in
4305 corresponding specification, but it can have different tags,
4306 optional/default attributes -- they will be taken from specification
4309 class TBSCertificate(Sequence):
4311 ("version", Version(expl=tag_ctxc(0), default="v1")),
4314 >>> tbs = TBSCertificate()
4315 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4317 Assign ``None`` to remove value from sequence.
4319 You can set values in Sequence during its initialization:
4321 >>> AlgorithmIdentifier((
4322 ("algorithm", ObjectIdentifier("1.2.3")),
4323 ("parameters", Any(Null()))
4325 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4327 You can determine if value exists/set in the sequence and take its value:
4329 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4332 OBJECT IDENTIFIER 1.2.3
4334 But pay attention that if value has default, then it won't be (not
4335 in) in the sequence (because ``DEFAULT`` must not be encoded in
4336 DER), but you can read its value:
4338 >>> "critical" in ext, ext["critical"]
4339 (False, BOOLEAN False)
4340 >>> ext["critical"] = Boolean(True)
4341 >>> "critical" in ext, ext["critical"]
4342 (True, BOOLEAN True)
4344 All defaulted values are always optional.
4346 .. _allow_default_values_ctx:
4348 DER prohibits default value encoding and will raise an error if
4349 default value is unexpectedly met during decode.
4350 If :ref:`bered <bered_ctx>` context option is set, then no error
4351 will be raised, but ``bered`` attribute set. You can disable strict
4352 defaulted values existence validation by setting
4353 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4355 Two sequences are equal if they have equal specification (schema),
4356 implicit/explicit tagging and the same values.
4358 __slots__ = ("specs",)
4359 tag_default = tag_encode(form=TagFormConstructed, num=16)
4360 asn1_type_name = "SEQUENCE"
4372 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4374 schema = getattr(self, "schema", ())
4376 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4379 if value is not None:
4380 if issubclass(value.__class__, Sequence):
4381 self._value = value._value
4382 elif hasattr(value, "__iter__"):
4383 for seq_key, seq_value in value:
4384 self[seq_key] = seq_value
4386 raise InvalidValueType((Sequence,))
4387 if default is not None:
4388 if not issubclass(default.__class__, Sequence):
4389 raise InvalidValueType((Sequence,))
4390 default_value = default._value
4391 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4392 default_obj.specs = self.specs
4393 default_obj._value = default_value
4394 self.default = default_obj
4396 self._value = default_obj.copy()._value
4400 for name, spec in self.specs.items():
4401 value = self._value.get(name)
4412 obj = self.__class__(schema=self.specs)
4414 obj._expl = self._expl
4415 obj.default = self.default
4416 obj.optional = self.optional
4417 obj.offset = self.offset
4418 obj.llen = self.llen
4419 obj.vlen = self.vlen
4420 obj._value = {k: v.copy() for k, v in self._value.items()}
4423 def __eq__(self, their):
4424 if not isinstance(their, self.__class__):
4427 self.specs == their.specs and
4428 self.tag == their.tag and
4429 self._expl == their._expl and
4430 self._value == their._value
4441 return self.__class__(
4444 impl=self.tag if impl is None else impl,
4445 expl=self._expl if expl is None else expl,
4446 default=self.default if default is None else default,
4447 optional=self.optional if optional is None else optional,
4450 def __contains__(self, key):
4451 return key in self._value
4453 def __setitem__(self, key, value):
4454 spec = self.specs.get(key)
4456 raise ObjUnknown(key)
4458 self._value.pop(key, None)
4460 if not isinstance(value, spec.__class__):
4461 raise InvalidValueType((spec.__class__,))
4462 value = spec(value=value)
4463 if spec.default is not None and value == spec.default:
4464 self._value.pop(key, None)
4466 self._value[key] = value
4468 def __getitem__(self, key):
4469 value = self._value.get(key)
4470 if value is not None:
4472 spec = self.specs.get(key)
4474 raise ObjUnknown(key)
4475 if spec.default is not None:
4479 def _encoded_values(self):
4481 for name, spec in self.specs.items():
4482 value = self._value.get(name)
4486 raise ObjNotReady(name)
4487 raws.append(value.encode())
4491 v = b"".join(self._encoded_values())
4492 return b"".join((self.tag, len_encode(len(v)), v))
4494 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4496 t, tlen, lv = tag_strip(tlv)
4497 except DecodeError as err:
4498 raise err.__class__(
4500 klass=self.__class__,
4501 decode_path=decode_path,
4506 klass=self.__class__,
4507 decode_path=decode_path,
4513 ctx_bered = ctx.get("bered", False)
4515 l, llen, v = len_decode(lv)
4516 except LenIndefForm as err:
4518 raise err.__class__(
4520 klass=self.__class__,
4521 decode_path=decode_path,
4524 l, llen, v = 0, 1, lv[1:]
4526 except DecodeError as err:
4527 raise err.__class__(
4529 klass=self.__class__,
4530 decode_path=decode_path,
4534 raise NotEnoughData(
4535 "encoded length is longer than data",
4536 klass=self.__class__,
4537 decode_path=decode_path,
4541 v, tail = v[:l], v[l:]
4543 sub_offset = offset + tlen + llen
4546 ctx_allow_default_values = ctx.get("allow_default_values", False)
4547 for name, spec in self.specs.items():
4548 if spec.optional and (
4549 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4553 sub_decode_path = decode_path + (name,)
4555 value, v_tail = spec.decode(
4559 decode_path=sub_decode_path,
4567 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4568 if defined is not None:
4569 defined_by, defined_spec = defined
4570 if issubclass(value.__class__, SequenceOf):
4571 for i, _value in enumerate(value):
4572 sub_sub_decode_path = sub_decode_path + (
4574 DecodePathDefBy(defined_by),
4576 defined_value, defined_tail = defined_spec.decode(
4577 memoryview(bytes(_value)),
4579 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4580 if value.expled else (value.tlen + value.llen)
4583 decode_path=sub_sub_decode_path,
4586 if len(defined_tail) > 0:
4589 klass=self.__class__,
4590 decode_path=sub_sub_decode_path,
4593 _value.defined = (defined_by, defined_value)
4595 defined_value, defined_tail = defined_spec.decode(
4596 memoryview(bytes(value)),
4598 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4599 if value.expled else (value.tlen + value.llen)
4602 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4605 if len(defined_tail) > 0:
4608 klass=self.__class__,
4609 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4612 value.defined = (defined_by, defined_value)
4614 value_len = value.fulllen
4616 sub_offset += value_len
4618 if spec.default is not None and value == spec.default:
4619 if ctx_bered or ctx_allow_default_values:
4623 "DEFAULT value met",
4624 klass=self.__class__,
4625 decode_path=sub_decode_path,
4628 values[name] = value
4630 spec_defines = getattr(spec, "defines", ())
4631 if len(spec_defines) == 0:
4632 defines_by_path = ctx.get("defines_by_path", ())
4633 if len(defines_by_path) > 0:
4634 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4635 if spec_defines is not None and len(spec_defines) > 0:
4636 for rel_path, schema in spec_defines:
4637 defined = schema.get(value, None)
4638 if defined is not None:
4639 ctx.setdefault("_defines", []).append((
4640 abs_decode_path(sub_decode_path[:-1], rel_path),
4644 if v[:EOC_LEN].tobytes() != EOC:
4647 klass=self.__class__,
4648 decode_path=decode_path,
4656 klass=self.__class__,
4657 decode_path=decode_path,
4660 obj = self.__class__(
4664 default=self.default,
4665 optional=self.optional,
4666 _decoded=(offset, llen, vlen),
4669 obj.lenindef = lenindef
4674 value = pp_console_row(next(self.pps()))
4676 for name in self.specs:
4677 _value = self._value.get(name)
4680 cols.append("%s: %s" % (name, repr(_value)))
4681 return "%s[%s]" % (value, "; ".join(cols))
4683 def pps(self, decode_path=()):
4685 asn1_type_name=self.asn1_type_name,
4686 obj_name=self.__class__.__name__,
4687 decode_path=decode_path,
4688 optional=self.optional,
4689 default=self == self.default,
4690 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4691 expl=None if self._expl is None else tag_decode(self._expl),
4696 expl_offset=self.expl_offset if self.expled else None,
4697 expl_tlen=self.expl_tlen if self.expled else None,
4698 expl_llen=self.expl_llen if self.expled else None,
4699 expl_vlen=self.expl_vlen if self.expled else None,
4700 expl_lenindef=self.expl_lenindef,
4701 lenindef=self.lenindef,
4703 for name in self.specs:
4704 value = self._value.get(name)
4707 yield value.pps(decode_path=decode_path + (name,))
4708 for pp in self.pps_lenindef(decode_path):
4712 class Set(Sequence):
4713 """``SET`` structure type
4715 Its usage is identical to :py:class:`pyderasn.Sequence`.
4717 .. _allow_unordered_set_ctx:
4719 DER prohibits unordered values encoding and will raise an error
4720 during decode. If If :ref:`bered <bered_ctx>` context option is set,
4721 then no error will occure. Also you can disable strict values
4722 ordering check by setting ``"allow_unordered_set": True``
4723 :ref:`context <ctx>` option.
4726 tag_default = tag_encode(form=TagFormConstructed, num=17)
4727 asn1_type_name = "SET"
4730 raws = self._encoded_values()
4733 return b"".join((self.tag, len_encode(len(v)), v))
4735 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4737 t, tlen, lv = tag_strip(tlv)
4738 except DecodeError as err:
4739 raise err.__class__(
4741 klass=self.__class__,
4742 decode_path=decode_path,
4747 klass=self.__class__,
4748 decode_path=decode_path,
4754 ctx_bered = ctx.get("bered", False)
4756 l, llen, v = len_decode(lv)
4757 except LenIndefForm as err:
4759 raise err.__class__(
4761 klass=self.__class__,
4762 decode_path=decode_path,
4765 l, llen, v = 0, 1, lv[1:]
4767 except DecodeError as err:
4768 raise err.__class__(
4770 klass=self.__class__,
4771 decode_path=decode_path,
4775 raise NotEnoughData(
4776 "encoded length is longer than data",
4777 klass=self.__class__,
4781 v, tail = v[:l], v[l:]
4783 sub_offset = offset + tlen + llen
4786 ctx_allow_default_values = ctx.get("allow_default_values", False)
4787 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4788 value_prev = memoryview(v[:0])
4789 specs_items = self.specs.items
4791 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4793 for name, spec in specs_items():
4794 sub_decode_path = decode_path + (name,)
4800 decode_path=sub_decode_path,
4809 klass=self.__class__,
4810 decode_path=decode_path,
4813 value, v_tail = spec.decode(
4817 decode_path=sub_decode_path,
4820 value_len = value.fulllen
4821 if value_prev.tobytes() > v[:value_len].tobytes():
4822 if ctx_bered or ctx_allow_unordered_set:
4826 "unordered " + self.asn1_type_name,
4827 klass=self.__class__,
4828 decode_path=sub_decode_path,
4831 if spec.default is None or value != spec.default:
4833 elif ctx_bered or ctx_allow_default_values:
4837 "DEFAULT value met",
4838 klass=self.__class__,
4839 decode_path=sub_decode_path,
4842 values[name] = value
4843 value_prev = v[:value_len]
4844 sub_offset += value_len
4847 obj = self.__class__(
4851 default=self.default,
4852 optional=self.optional,
4853 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4856 if v[:EOC_LEN].tobytes() != EOC:
4859 klass=self.__class__,
4860 decode_path=decode_path,
4868 "not all values are ready",
4869 klass=self.__class__,
4870 decode_path=decode_path,
4877 class SequenceOf(Obj):
4878 """``SEQUENCE OF`` sequence type
4880 For that kind of type you must specify the object it will carry on
4881 (bounds are for example here, not required)::
4883 class Ints(SequenceOf):
4888 >>> ints.append(Integer(123))
4889 >>> ints.append(Integer(234))
4891 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4892 >>> [int(i) for i in ints]
4894 >>> ints.append(Integer(345))
4895 Traceback (most recent call last):
4896 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4899 >>> ints[1] = Integer(345)
4901 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4903 Also you can initialize sequence with preinitialized values:
4905 >>> ints = Ints([Integer(123), Integer(234)])
4907 __slots__ = ("spec", "_bound_min", "_bound_max")
4908 tag_default = tag_encode(form=TagFormConstructed, num=16)
4909 asn1_type_name = "SEQUENCE OF"
4922 super(SequenceOf, self).__init__(
4930 schema = getattr(self, "schema", None)
4932 raise ValueError("schema must be specified")
4934 self._bound_min, self._bound_max = getattr(
4938 ) if bounds is None else bounds
4940 if value is not None:
4941 self._value = self._value_sanitize(value)
4942 if default is not None:
4943 default_value = self._value_sanitize(default)
4944 default_obj = self.__class__(
4949 default_obj._value = default_value
4950 self.default = default_obj
4952 self._value = default_obj.copy()._value
4954 def _value_sanitize(self, value):
4955 if issubclass(value.__class__, SequenceOf):
4956 value = value._value
4957 elif hasattr(value, "__iter__"):
4960 raise InvalidValueType((self.__class__, iter))
4961 if not self._bound_min <= len(value) <= self._bound_max:
4962 raise BoundsError(self._bound_min, len(value), self._bound_max)
4964 if not isinstance(v, self.spec.__class__):
4965 raise InvalidValueType((self.spec.__class__,))
4970 return all(v.ready for v in self._value)
4973 obj = self.__class__(schema=self.spec)
4974 obj._bound_min = self._bound_min
4975 obj._bound_max = self._bound_max
4977 obj._expl = self._expl
4978 obj.default = self.default
4979 obj.optional = self.optional
4980 obj.offset = self.offset
4981 obj.llen = self.llen
4982 obj.vlen = self.vlen
4983 obj._value = [v.copy() for v in self._value]
4986 def __eq__(self, their):
4987 if isinstance(their, self.__class__):
4989 self.spec == their.spec and
4990 self.tag == their.tag and
4991 self._expl == their._expl and
4992 self._value == their._value
4994 if hasattr(their, "__iter__"):
4995 return self._value == list(their)
5007 return self.__class__(
5011 (self._bound_min, self._bound_max)
5012 if bounds is None else bounds
5014 impl=self.tag if impl is None else impl,
5015 expl=self._expl if expl is None else expl,
5016 default=self.default if default is None else default,
5017 optional=self.optional if optional is None else optional,
5020 def __contains__(self, key):
5021 return key in self._value
5023 def append(self, value):
5024 if not isinstance(value, self.spec.__class__):
5025 raise InvalidValueType((self.spec.__class__,))
5026 if len(self._value) + 1 > self._bound_max:
5029 len(self._value) + 1,
5032 self._value.append(value)
5035 self._assert_ready()
5036 return iter(self._value)
5039 self._assert_ready()
5040 return len(self._value)
5042 def __setitem__(self, key, value):
5043 if not isinstance(value, self.spec.__class__):
5044 raise InvalidValueType((self.spec.__class__,))
5045 self._value[key] = self.spec(value=value)
5047 def __getitem__(self, key):
5048 return self._value[key]
5050 def _encoded_values(self):
5051 return [v.encode() for v in self._value]
5054 v = b"".join(self._encoded_values())
5055 return b"".join((self.tag, len_encode(len(v)), v))
5057 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5059 t, tlen, lv = tag_strip(tlv)
5060 except DecodeError as err:
5061 raise err.__class__(
5063 klass=self.__class__,
5064 decode_path=decode_path,
5069 klass=self.__class__,
5070 decode_path=decode_path,
5076 ctx_bered = ctx.get("bered", False)
5078 l, llen, v = len_decode(lv)
5079 except LenIndefForm as err:
5081 raise err.__class__(
5083 klass=self.__class__,
5084 decode_path=decode_path,
5087 l, llen, v = 0, 1, lv[1:]
5089 except DecodeError as err:
5090 raise err.__class__(
5092 klass=self.__class__,
5093 decode_path=decode_path,
5097 raise NotEnoughData(
5098 "encoded length is longer than data",
5099 klass=self.__class__,
5100 decode_path=decode_path,
5104 v, tail = v[:l], v[l:]
5106 sub_offset = offset + tlen + llen
5108 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5109 value_prev = memoryview(v[:0])
5113 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5115 sub_decode_path = decode_path + (str(len(_value)),)
5116 value, v_tail = spec.decode(
5120 decode_path=sub_decode_path,
5123 value_len = value.fulllen
5125 if value_prev.tobytes() > v[:value_len].tobytes():
5126 if ctx_bered or ctx_allow_unordered_set:
5130 "unordered " + self.asn1_type_name,
5131 klass=self.__class__,
5132 decode_path=sub_decode_path,
5135 value_prev = v[:value_len]
5136 _value.append(value)
5137 sub_offset += value_len
5141 obj = self.__class__(
5144 bounds=(self._bound_min, self._bound_max),
5147 default=self.default,
5148 optional=self.optional,
5149 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5151 except BoundsError as err:
5154 klass=self.__class__,
5155 decode_path=decode_path,
5159 if v[:EOC_LEN].tobytes() != EOC:
5162 klass=self.__class__,
5163 decode_path=decode_path,
5173 pp_console_row(next(self.pps())),
5174 ", ".join(repr(v) for v in self._value),
5177 def pps(self, decode_path=()):
5179 asn1_type_name=self.asn1_type_name,
5180 obj_name=self.__class__.__name__,
5181 decode_path=decode_path,
5182 optional=self.optional,
5183 default=self == self.default,
5184 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5185 expl=None if self._expl is None else tag_decode(self._expl),
5190 expl_offset=self.expl_offset if self.expled else None,
5191 expl_tlen=self.expl_tlen if self.expled else None,
5192 expl_llen=self.expl_llen if self.expled else None,
5193 expl_vlen=self.expl_vlen if self.expled else None,
5194 expl_lenindef=self.expl_lenindef,
5195 lenindef=self.lenindef,
5197 for i, value in enumerate(self._value):
5198 yield value.pps(decode_path=decode_path + (str(i),))
5199 for pp in self.pps_lenindef(decode_path):
5203 class SetOf(SequenceOf):
5204 """``SET OF`` sequence type
5206 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5209 tag_default = tag_encode(form=TagFormConstructed, num=17)
5210 asn1_type_name = "SET OF"
5213 raws = self._encoded_values()
5216 return b"".join((self.tag, len_encode(len(v)), v))
5218 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5219 return super(SetOf, self)._decode(
5225 ordering_check=True,
5229 def obj_by_path(pypath): # pragma: no cover
5230 """Import object specified as string Python path
5232 Modules must be separated from classes/functions with ``:``.
5234 >>> obj_by_path("foo.bar:Baz")
5235 <class 'foo.bar.Baz'>
5236 >>> obj_by_path("foo.bar:Baz.boo")
5237 <classmethod 'foo.bar.Baz.boo'>
5239 mod, objs = pypath.rsplit(":", 1)
5240 from importlib import import_module
5241 obj = import_module(mod)
5242 for obj_name in objs.split("."):
5243 obj = getattr(obj, obj_name)
5247 def generic_decoder(): # pragma: no cover
5248 # All of this below is a big hack with self references
5249 choice = PrimitiveTypes()
5250 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5251 choice.specs["SetOf"] = SetOf(schema=choice)
5253 choice.specs["SequenceOf%d" % i] = SequenceOf(
5257 choice.specs["Any"] = Any()
5259 # Class name equals to type name, to omit it from output
5260 class SEQUENCEOF(SequenceOf):
5268 with_decode_path=False,
5269 decode_path_only=(),
5271 def _pprint_pps(pps):
5273 if hasattr(pp, "_fields"):
5275 decode_path_only != () and
5276 pp.decode_path[:len(decode_path_only)] != decode_path_only
5279 if pp.asn1_type_name == Choice.asn1_type_name:
5281 pp_kwargs = pp._asdict()
5282 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5283 pp = _pp(**pp_kwargs)
5284 yield pp_console_row(
5289 with_colours=with_colours,
5290 with_decode_path=with_decode_path,
5291 decode_path_len_decrease=len(decode_path_only),
5293 for row in pp_console_blob(
5295 decode_path_len_decrease=len(decode_path_only),
5299 for row in _pprint_pps(pp):
5301 return "\n".join(_pprint_pps(obj.pps()))
5302 return SEQUENCEOF(), pprint_any
5305 def main(): # pragma: no cover
5307 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5308 parser.add_argument(
5312 help="Skip that number of bytes from the beginning",
5314 parser.add_argument(
5316 help="Python path to dictionary with OIDs",
5318 parser.add_argument(
5320 help="Python path to schema definition to use",
5322 parser.add_argument(
5323 "--defines-by-path",
5324 help="Python path to decoder's defines_by_path",
5326 parser.add_argument(
5328 action="store_true",
5329 help="Disallow BER encoding",
5331 parser.add_argument(
5332 "--print-decode-path",
5333 action="store_true",
5334 help="Print decode paths",
5336 parser.add_argument(
5337 "--decode-path-only",
5338 help="Print only specified decode path",
5340 parser.add_argument(
5342 action="store_true",
5343 help="Allow explicit tag out-of-bound",
5345 parser.add_argument(
5347 type=argparse.FileType("rb"),
5348 help="Path to DER file you want to decode",
5350 args = parser.parse_args()
5351 args.DERFile.seek(args.skip)
5352 der = memoryview(args.DERFile.read())
5353 args.DERFile.close()
5354 oids = obj_by_path(args.oids) if args.oids else {}
5356 schema = obj_by_path(args.schema)
5357 from functools import partial
5358 pprinter = partial(pprint, big_blobs=True)
5360 schema, pprinter = generic_decoder()
5362 "bered": not args.nobered,
5363 "allow_expl_oob": args.allow_expl_oob,
5365 if args.defines_by_path is not None:
5366 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5367 obj, tail = schema().decode(der, ctx=ctx)
5371 with_colours=True if environ.get("NO_COLOR") is None else False,
5372 with_decode_path=args.print_decode_path,
5374 () if args.decode_path_only is None else
5375 tuple(args.decode_path_only.split(":"))
5379 print("\nTrailing data: %s" % hexenc(tail))
5382 if __name__ == "__main__":