3 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
4 # Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 """Python ASN.1 DER/BER codec with abstract structures
21 This library allows you to marshal various structures in ASN.1 DER
22 format, unmarshal them in BER/CER/DER ones.
26 >>> Integer().decode(raw) == i
29 There are primitive types, holding single values
30 (:py:class:`pyderasn.BitString`,
31 :py:class:`pyderasn.Boolean`,
32 :py:class:`pyderasn.Enumerated`,
33 :py:class:`pyderasn.GeneralizedTime`,
34 :py:class:`pyderasn.Integer`,
35 :py:class:`pyderasn.Null`,
36 :py:class:`pyderasn.ObjectIdentifier`,
37 :py:class:`pyderasn.OctetString`,
38 :py:class:`pyderasn.UTCTime`,
39 :py:class:`various strings <pyderasn.CommonString>`
40 (:py:class:`pyderasn.BMPString`,
41 :py:class:`pyderasn.GeneralString`,
42 :py:class:`pyderasn.GraphicString`,
43 :py:class:`pyderasn.IA5String`,
44 :py:class:`pyderasn.ISO646String`,
45 :py:class:`pyderasn.NumericString`,
46 :py:class:`pyderasn.PrintableString`,
47 :py:class:`pyderasn.T61String`,
48 :py:class:`pyderasn.TeletexString`,
49 :py:class:`pyderasn.UniversalString`,
50 :py:class:`pyderasn.UTF8String`,
51 :py:class:`pyderasn.VideotexString`,
52 :py:class:`pyderasn.VisibleString`)),
53 constructed types, holding multiple primitive types
54 (:py:class:`pyderasn.Sequence`,
55 :py:class:`pyderasn.SequenceOf`,
56 :py:class:`pyderasn.Set`,
57 :py:class:`pyderasn.SetOf`),
58 and special types like
59 :py:class:`pyderasn.Any` and
60 :py:class:`pyderasn.Choice`.
68 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
69 the default tag used during coding process. You can override it with
70 either ``IMPLICIT`` (using ``impl`` keyword argument), or
71 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments take
72 raw binary string, containing that tag. You can **not** set implicit and
73 explicit tags simultaneously.
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
78 number. Pay attention that explicit tags always have *constructed* tag
79 (``tag_ctxc``), but implicit tags for primitive types are primitive
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two objects of the same type, but with different implicit/explicit tags
94 You can get object's effective tag (either default or implicited) through
95 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
112 Many objects in sequences could be ``OPTIONAL`` and could have
113 ``DEFAULT`` value. You can specify that object's property using
114 corresponding keyword arguments.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
119 Those specifications do not play any role in primitive value encoding,
120 but are taken into account when dealing with sequences holding them. For
121 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
143 Some objects give ability to set value size constraints. This is either
144 possible integer value, or allowed length of various strings and
145 sequences. Constraints are set in the following way::
150 And values satisfaction is checked as: ``MIN <= X <= MAX``.
152 For simplicity you can also set bounds the following way::
154 bounded_x = X(bounds=(MIN, MAX))
156 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
162 All objects have ``ready`` boolean property, that tells if object is
163 ready to be encoded. If that kind of action is performed on unready
164 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
166 All objects have ``copy()`` method, that returns their copy, that can be
174 Decoding is performed using ``decode()`` method. ``offset`` optional
175 argument could be used to set initial object's offset in the binary
176 data, for convenience. It returns decoded object and remaining
177 unmarshalled data (tail). Internally all work is done on
178 ``memoryview(data)``, and you can leave returning tail as a memoryview,
179 by specifying ``leavemm=True`` argument.
181 When object is decoded, ``decoded`` property is true and you can safely
182 use following properties:
184 * ``offset`` -- position including initial offset where object's tag starts
185 * ``tlen`` -- length of object's tag
186 * ``llen`` -- length of object's length value
187 * ``vlen`` -- length of object's value
188 * ``tlvlen`` -- length of the whole object
190 Pay attention that those values do **not** include anything related to
191 explicit tag. If you want to know information about it, then use:
193 * ``expled`` -- to know if explicit tag is set
194 * ``expl_offset`` (it is lesser than ``offset``)
197 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
198 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
200 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
203 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
210 You can specify so called context keyword argument during ``decode()``
211 invocation. It is dictionary containing various options governing
214 Currently available context options:
216 * :ref:`allow_default_values <allow_default_values_ctx>`
217 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
218 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
219 * :ref:`bered <bered_ctx>`
220 * :ref:`defines_by_path <defines_by_path_ctx>`
227 All objects have ``pps()`` method, that is a generator of
228 :py:class:`pyderasn.PP` namedtuple, holding various raw information
229 about the object. If ``pps`` is called on sequences, then all underlying
230 ``PP`` will be yielded.
232 You can use :py:func:`pyderasn.pp_console_row` function, converting
233 those ``PP`` to human readable string. Actually exactly it is used for
234 all object ``repr``. But it is easy to write custom formatters.
236 >>> from pyderasn import pprint
237 >>> encoded = Integer(-12345).encode()
238 >>> obj, tail = Integer().decode(encoded)
239 >>> print(pprint(obj))
240 0 [1,1, 2] INTEGER -12345
247 ASN.1 structures often have ANY and OCTET STRING fields, that are
248 DEFINED BY some previously met ObjectIdentifier. This library provides
249 ability to specify mapping between some OID and field that must be
250 decoded with specific specification.
255 :py:class:`pyderasn.ObjectIdentifier` field inside
256 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
257 necessary for decoding structures. For example, CMS (:rfc:`5652`)
260 class ContentInfo(Sequence):
262 ("contentType", ContentType(defines=((("content",), {
263 id_digestedData: DigestedData(),
264 id_signedData: SignedData(),
266 ("content", Any(expl=tag_ctxc(0))),
269 ``contentType`` field tells that it defines that ``content`` must be
270 decoded with ``SignedData`` specification, if ``contentType`` equals to
271 ``id-signedData``. The same applies to ``DigestedData``. If
272 ``contentType`` contains unknown OID, then no automatic decoding is
275 You can specify multiple fields, that will be autodecoded -- that is why
276 ``defines`` kwarg is a sequence. You can specify defined field
277 relatively or absolutely to current decode path. For example ``defines``
278 for AlgorithmIdentifier of X.509's
279 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
283 id_ecPublicKey: ECParameters(),
284 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
286 (("..", "subjectPublicKey"), {
287 id_rsaEncryption: RSAPublicKey(),
288 id_GostR3410_2001: OctetString(),
292 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
293 autodecode its parameters inside SPKI's algorithm and its public key
296 Following types can be automatically decoded (DEFINED BY):
298 * :py:class:`pyderasn.Any`
299 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
300 * :py:class:`pyderasn.OctetString`
301 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
302 ``Any``/``BitString``/``OctetString``-s
304 When any of those fields is automatically decoded, then ``.defined``
305 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
306 was defined, ``value`` contains corresponding decoded value. For example
307 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
309 .. _defines_by_path_ctx:
311 defines_by_path context option
312 ______________________________
314 Sometimes you either can not or do not want to explicitly set *defines*
315 in the scheme. You can dynamically apply those definitions when calling
316 ``.decode()`` method.
318 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
319 value must be sequence of following tuples::
321 (decode_path, defines)
323 where ``decode_path`` is a tuple holding so-called decode path to the
324 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
325 ``defines``, holding exactly the same value as accepted in its keyword
328 For example, again for CMS, you want to automatically decode
329 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
330 structures it may hold. Also, automatically decode ``controlSequence``
333 content_info, tail = ContentInfo().decode(data, defines_by_path=(
336 ((("content",), {id_signedData: SignedData()}),),
341 DecodePathDefBy(id_signedData),
346 id_cct_PKIData: PKIData(),
347 id_cct_PKIResponse: PKIResponse(),
353 DecodePathDefBy(id_signedData),
356 DecodePathDefBy(id_cct_PKIResponse),
362 id_cmc_recipientNonce: RecipientNonce(),
363 id_cmc_senderNonce: SenderNonce(),
364 id_cmc_statusInfoV2: CMCStatusInfoV2(),
365 id_cmc_transactionId: TransactionId(),
370 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
371 First function is useful for path construction when some automatic
372 decoding is already done. ``any`` means literally any value it meet --
373 useful for SEQUENCE/SET OF-s.
380 By default PyDERASN accepts only DER encoded data. It always encodes to
381 DER. But you can optionally enable BER decoding with setting ``bered``
382 :ref:`context <ctx>` argument to True. Indefinite lengths and
383 constructed primitive types should be parsed successfully.
385 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
386 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
387 STRING``, ``SEQUENCE``, ``SET``, ``SET OF`` can contain it.
388 * If object has an indefinite length encoding, then its ``lenindef``
389 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
390 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
392 * If object has an indefinite length encoded explicit tag, then
393 ``expl_lenindef`` is set to True.
394 * If object has either any of BER-related encoding (explicit tag
395 indefinite length, object's indefinite length, BER-encoding) or any
396 underlying component has that kind of encoding, then ``bered``
397 attribute is set to True. For example SignedData CMS can have
398 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
399 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
401 EOC (end-of-contents) token's length is taken in advance in object's
404 .. _allow_expl_oob_ctx:
406 Allow explicit tag out-of-bound
407 -------------------------------
409 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
410 one value, more than one object. If you set ``allow_expl_oob`` context
411 option to True, then no error will be raised and that invalid encoding
412 will be silently further processed. But pay attention that offsets and
413 lengths will be invalid in that case.
417 This option should be used only for skipping some decode errors, just
418 to see the decoded structure somehow.
425 .. autoclass:: pyderasn.Boolean
430 .. autoclass:: pyderasn.Integer
435 .. autoclass:: pyderasn.BitString
440 .. autoclass:: pyderasn.OctetString
445 .. autoclass:: pyderasn.Null
450 .. autoclass:: pyderasn.ObjectIdentifier
455 .. autoclass:: pyderasn.Enumerated
459 .. autoclass:: pyderasn.CommonString
463 .. autoclass:: pyderasn.NumericString
467 .. autoclass:: pyderasn.UTCTime
468 :members: __init__, todatetime
472 .. autoclass:: pyderasn.GeneralizedTime
479 .. autoclass:: pyderasn.Choice
484 .. autoclass:: PrimitiveTypes
488 .. autoclass:: pyderasn.Any
496 .. autoclass:: pyderasn.Sequence
501 .. autoclass:: pyderasn.Set
506 .. autoclass:: pyderasn.SequenceOf
511 .. autoclass:: pyderasn.SetOf
517 .. autofunction:: pyderasn.abs_decode_path
518 .. autofunction:: pyderasn.hexenc
519 .. autofunction:: pyderasn.hexdec
520 .. autofunction:: pyderasn.tag_encode
521 .. autofunction:: pyderasn.tag_decode
522 .. autofunction:: pyderasn.tag_ctxp
523 .. autofunction:: pyderasn.tag_ctxc
524 .. autoclass:: pyderasn.Obj
525 .. autoclass:: pyderasn.DecodeError
527 .. autoclass:: pyderasn.NotEnoughData
528 .. autoclass:: pyderasn.LenIndefForm
529 .. autoclass:: pyderasn.TagMismatch
530 .. autoclass:: pyderasn.InvalidLength
531 .. autoclass:: pyderasn.InvalidOID
532 .. autoclass:: pyderasn.ObjUnknown
533 .. autoclass:: pyderasn.ObjNotReady
534 .. autoclass:: pyderasn.InvalidValueType
535 .. autoclass:: pyderasn.BoundsError
538 from codecs import getdecoder
539 from codecs import getencoder
540 from collections import namedtuple
541 from collections import OrderedDict
542 from datetime import datetime
543 from math import ceil
544 from os import environ
545 from string import digits
547 from six import add_metaclass
548 from six import binary_type
549 from six import byte2int
550 from six import indexbytes
551 from six import int2byte
552 from six import integer_types
553 from six import iterbytes
555 from six import string_types
556 from six import text_type
557 from six.moves import xrange as six_xrange
561 from termcolor import colored
562 except ImportError: # pragma: no cover
563 def colored(what, *args):
607 "TagClassApplication",
611 "TagFormConstructed",
622 TagClassUniversal = 0
623 TagClassApplication = 1 << 6
624 TagClassContext = 1 << 7
625 TagClassPrivate = 1 << 6 | 1 << 7
627 TagFormConstructed = 1 << 5
630 TagClassApplication: "APPLICATION ",
631 TagClassPrivate: "PRIVATE ",
632 TagClassUniversal: "UNIV ",
636 LENINDEF = b"\x80" # length indefinite mark
637 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
640 ########################################################################
642 ########################################################################
644 class DecodeError(Exception):
645 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
647 :param str msg: reason of decode failing
648 :param klass: optional exact DecodeError inherited class (like
649 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
650 :py:exc:`InvalidLength`)
651 :param decode_path: tuple of strings. It contains human
652 readable names of the fields through which
653 decoding process has passed
654 :param int offset: binary offset where failure happened
656 super(DecodeError, self).__init__()
659 self.decode_path = decode_path
665 "" if self.klass is None else self.klass.__name__,
667 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
668 if len(self.decode_path) > 0 else ""
670 ("(at %d)" % self.offset) if self.offset > 0 else "",
676 return "%s(%s)" % (self.__class__.__name__, self)
679 class NotEnoughData(DecodeError):
683 class LenIndefForm(DecodeError):
687 class TagMismatch(DecodeError):
691 class InvalidLength(DecodeError):
695 class InvalidOID(DecodeError):
699 class ObjUnknown(ValueError):
700 def __init__(self, name):
701 super(ObjUnknown, self).__init__()
705 return "object is unknown: %s" % self.name
708 return "%s(%s)" % (self.__class__.__name__, self)
711 class ObjNotReady(ValueError):
712 def __init__(self, name):
713 super(ObjNotReady, self).__init__()
717 return "object is not ready: %s" % self.name
720 return "%s(%s)" % (self.__class__.__name__, self)
723 class InvalidValueType(ValueError):
724 def __init__(self, expected_types):
725 super(InvalidValueType, self).__init__()
726 self.expected_types = expected_types
729 return "invalid value type, expected: %s" % ", ".join(
730 [repr(t) for t in self.expected_types]
734 return "%s(%s)" % (self.__class__.__name__, self)
737 class BoundsError(ValueError):
738 def __init__(self, bound_min, value, bound_max):
739 super(BoundsError, self).__init__()
740 self.bound_min = bound_min
742 self.bound_max = bound_max
745 return "unsatisfied bounds: %s <= %s <= %s" % (
752 return "%s(%s)" % (self.__class__.__name__, self)
755 ########################################################################
757 ########################################################################
759 _hexdecoder = getdecoder("hex")
760 _hexencoder = getencoder("hex")
764 """Binary data to hexadecimal string convert
766 return _hexdecoder(data)[0]
770 """Hexadecimal string to binary data convert
772 return _hexencoder(data)[0].decode("ascii")
775 def int_bytes_len(num, byte_len=8):
778 return int(ceil(float(num.bit_length()) / byte_len))
781 def zero_ended_encode(num):
782 octets = bytearray(int_bytes_len(num, 7))
784 octets[i] = num & 0x7F
788 octets[i] = 0x80 | (num & 0x7F)
794 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
795 """Encode tag to binary form
797 :param int num: tag's number
798 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
799 :py:data:`pyderasn.TagClassContext`,
800 :py:data:`pyderasn.TagClassApplication`,
801 :py:data:`pyderasn.TagClassPrivate`)
802 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
803 :py:data:`pyderasn.TagFormConstructed`)
807 return int2byte(klass | form | num)
808 # [XX|X|11111][1.......][1.......] ... [0.......]
809 return int2byte(klass | form | 31) + zero_ended_encode(num)
813 """Decode tag from binary form
817 No validation is performed, assuming that it has already passed.
819 It returns tuple with three integers, as
820 :py:func:`pyderasn.tag_encode` accepts.
822 first_octet = byte2int(tag)
823 klass = first_octet & 0xC0
824 form = first_octet & 0x20
825 if first_octet & 0x1F < 0x1F:
826 return (klass, form, first_octet & 0x1F)
828 for octet in iterbytes(tag[1:]):
831 return (klass, form, num)
835 """Create CONTEXT PRIMITIVE tag
837 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
841 """Create CONTEXT CONSTRUCTED tag
843 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
847 """Take off tag from the data
849 :returns: (encoded tag, tag length, remaining data)
852 raise NotEnoughData("no data at all")
853 if byte2int(data) & 0x1F < 31:
854 return data[:1], 1, data[1:]
859 raise DecodeError("unfinished tag")
860 if indexbytes(data, i) & 0x80 == 0:
863 return data[:i], i, data[i:]
869 octets = bytearray(int_bytes_len(l) + 1)
870 octets[0] = 0x80 | (len(octets) - 1)
871 for i in six_xrange(len(octets) - 1, 0, -1):
877 def len_decode(data):
880 :returns: (decoded length, length's length, remaining data)
881 :raises LenIndefForm: if indefinite form encoding is met
884 raise NotEnoughData("no data at all")
885 first_octet = byte2int(data)
886 if first_octet & 0x80 == 0:
887 return first_octet, 1, data[1:]
888 octets_num = first_octet & 0x7F
889 if octets_num + 1 > len(data):
890 raise NotEnoughData("encoded length is longer than data")
893 if byte2int(data[1:]) == 0:
894 raise DecodeError("leading zeros")
896 for v in iterbytes(data[1:1 + octets_num]):
899 raise DecodeError("long form instead of short one")
900 return l, 1 + octets_num, data[1 + octets_num:]
903 ########################################################################
905 ########################################################################
907 class AutoAddSlots(type):
908 def __new__(mcs, name, bases, _dict):
909 _dict["__slots__"] = _dict.get("__slots__", ())
910 return type.__new__(mcs, name, bases, _dict)
913 @add_metaclass(AutoAddSlots)
915 """Common ASN.1 object class
917 All ASN.1 types are inherited from it. It has metaclass that
918 automatically adds ``__slots__`` to all inherited classes.
942 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
943 self._expl = getattr(self, "expl", None) if expl is None else expl
944 if self.tag != self.tag_default and self._expl is not None:
945 raise ValueError("implicit and explicit tags can not be set simultaneously")
946 if default is not None:
948 self.optional = optional
949 self.offset, self.llen, self.vlen = _decoded
951 self.expl_lenindef = False
952 self.lenindef = False
953 self.ber_encoded = False
956 def ready(self): # pragma: no cover
957 """Is object ready to be encoded?
959 raise NotImplementedError()
961 def _assert_ready(self):
963 raise ObjNotReady(self.__class__.__name__)
967 """Is either object or any elements inside is BER encoded?
969 return self.expl_lenindef or self.lenindef or self.ber_encoded
973 """Is object decoded?
975 return (self.llen + self.vlen) > 0
977 def copy(self): # pragma: no cover
978 """Make a copy of object, safe to be mutated
980 raise NotImplementedError()
988 return self.tlen + self.llen + self.vlen
990 def __str__(self): # pragma: no cover
991 return self.__bytes__() if PY2 else self.__unicode__()
993 def __ne__(self, their):
994 return not(self == their)
996 def __gt__(self, their): # pragma: no cover
997 return not(self < their)
999 def __le__(self, their): # pragma: no cover
1000 return (self == their) or (self < their)
1002 def __ge__(self, their): # pragma: no cover
1003 return (self == their) or (self > their)
1005 def _encode(self): # pragma: no cover
1006 raise NotImplementedError()
1008 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1009 raise NotImplementedError()
1012 raw = self._encode()
1013 if self._expl is None:
1015 return b"".join((self._expl, len_encode(len(raw)), raw))
1028 :param data: either binary or memoryview
1029 :param int offset: initial data's offset
1030 :param bool leavemm: do we need to leave memoryview of remaining
1031 data as is, or convert it to bytes otherwise
1032 :param ctx: optional :ref:`context <ctx>` governing decoding process.
1033 :param tag_only: decode only the tag, without length and contents
1034 (used only in Choice and Set structures, trying to
1035 determine if tag satisfies the scheme)
1036 :returns: (Obj, remaining data)
1040 tlv = memoryview(data)
1041 if self._expl is None:
1042 result = self._decode(
1045 decode_path=decode_path,
1054 t, tlen, lv = tag_strip(tlv)
1055 except DecodeError as err:
1056 raise err.__class__(
1058 klass=self.__class__,
1059 decode_path=decode_path,
1064 klass=self.__class__,
1065 decode_path=decode_path,
1069 l, llen, v = len_decode(lv)
1070 except LenIndefForm as err:
1071 if not ctx.get("bered", False):
1072 raise err.__class__(
1074 klass=self.__class__,
1075 decode_path=decode_path,
1079 offset += tlen + llen
1080 result = self._decode(
1083 decode_path=decode_path,
1087 if tag_only: # pragma: no cover
1090 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1091 if eoc_expected.tobytes() != EOC:
1094 klass=self.__class__,
1095 decode_path=decode_path,
1099 obj.expl_lenindef = True
1100 except DecodeError as err:
1101 raise err.__class__(
1103 klass=self.__class__,
1104 decode_path=decode_path,
1109 raise NotEnoughData(
1110 "encoded length is longer than data",
1111 klass=self.__class__,
1112 decode_path=decode_path,
1115 result = self._decode(
1117 offset=offset + tlen + llen,
1118 decode_path=decode_path,
1122 if tag_only: # pragma: no cover
1125 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1127 "explicit tag out-of-bound, longer than data",
1128 klass=self.__class__,
1129 decode_path=decode_path,
1132 return obj, (tail if leavemm else tail.tobytes())
1136 return self._expl is not None
1143 def expl_tlen(self):
1144 return len(self._expl)
1147 def expl_llen(self):
1148 if self.expl_lenindef:
1150 return len(len_encode(self.tlvlen))
1153 def expl_offset(self):
1154 return self.offset - self.expl_tlen - self.expl_llen
1157 def expl_vlen(self):
1161 def expl_tlvlen(self):
1162 return self.expl_tlen + self.expl_llen + self.expl_vlen
1165 def fulloffset(self):
1166 return self.expl_offset if self.expled else self.offset
1170 return self.expl_tlvlen if self.expled else self.tlvlen
1172 def pps_lenindef(self, decode_path):
1173 if self.lenindef and not (
1174 getattr(self, "defined", None) is not None and
1175 self.defined[1].lenindef
1178 asn1_type_name="EOC",
1180 decode_path=decode_path,
1182 self.offset + self.tlvlen -
1183 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1191 if self.expl_lenindef:
1193 asn1_type_name="EOC",
1194 obj_name="EXPLICIT",
1195 decode_path=decode_path,
1196 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1205 class DecodePathDefBy(object):
1206 """DEFINED BY representation inside decode path
1208 __slots__ = ("defined_by",)
1210 def __init__(self, defined_by):
1211 self.defined_by = defined_by
1213 def __ne__(self, their):
1214 return not(self == their)
1216 def __eq__(self, their):
1217 if not isinstance(their, self.__class__):
1219 return self.defined_by == their.defined_by
1222 return "DEFINED BY " + str(self.defined_by)
1225 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1228 ########################################################################
1230 ########################################################################
1232 PP = namedtuple("PP", (
1258 asn1_type_name="unknown",
1275 expl_lenindef=False,
1305 def _colourize(what, colour, with_colours, attrs=("bold",)):
1306 return colored(what, colour, attrs=attrs) if with_colours else what
1315 with_decode_path=False,
1316 decode_path_len_decrease=0,
1323 " " if pp.expl_offset is None else
1324 ("-%d" % (pp.offset - pp.expl_offset))
1326 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1328 col = _colourize(col, "red", with_colours, ())
1329 col += _colourize("B", "red", with_colours) if pp.bered else " "
1331 col = "[%d,%d,%4d]%s" % (
1335 LENINDEF_PP_CHAR if pp.lenindef else " "
1337 col = _colourize(col, "green", with_colours, ())
1339 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1340 if decode_path_len > 0:
1341 cols.append(" ." * decode_path_len)
1342 ent = pp.decode_path[-1]
1343 if isinstance(ent, DecodePathDefBy):
1344 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1345 value = str(ent.defined_by)
1347 oids is not None and
1348 ent.defined_by.asn1_type_name ==
1349 ObjectIdentifier.asn1_type_name and
1352 cols.append(_colourize("%s:" % oids[value], "green", with_colours))
1354 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1356 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1357 if pp.expl is not None:
1358 klass, _, num = pp.expl
1359 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1360 cols.append(_colourize(col, "blue", with_colours))
1361 if pp.impl is not None:
1362 klass, _, num = pp.impl
1363 col = "[%s%d]" % (TagClassReprs[klass], num)
1364 cols.append(_colourize(col, "blue", with_colours))
1365 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1366 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1368 cols.append(_colourize("BER", "red", with_colours))
1369 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1370 if pp.value is not None:
1372 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1374 oids is not None and
1375 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1378 cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
1380 if isinstance(pp.blob, binary_type):
1381 cols.append(hexenc(pp.blob))
1382 elif isinstance(pp.blob, tuple):
1383 cols.append(", ".join(pp.blob))
1385 cols.append(_colourize("OPTIONAL", "red", with_colours))
1387 cols.append(_colourize("DEFAULT", "red", with_colours))
1388 if with_decode_path:
1389 cols.append(_colourize(
1390 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1394 return " ".join(cols)
1397 def pp_console_blob(pp, decode_path_len_decrease=0):
1398 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1399 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1400 if decode_path_len > 0:
1401 cols.append(" ." * (decode_path_len + 1))
1402 if isinstance(pp.blob, binary_type):
1403 blob = hexenc(pp.blob).upper()
1404 for i in range(0, len(blob), 32):
1405 chunk = blob[i:i + 32]
1406 yield " ".join(cols + [":".join(
1407 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1409 elif isinstance(pp.blob, tuple):
1410 yield " ".join(cols + [", ".join(pp.blob)])
1418 with_decode_path=False,
1419 decode_path_only=(),
1421 """Pretty print object
1423 :param Obj obj: object you want to pretty print
1424 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1425 from it is met, then its humand readable form is printed
1426 :param big_blobs: if large binary objects are met (like OctetString
1427 values), do we need to print them too, on separate
1429 :param with_colours: colourize output, if ``termcolor`` library
1431 :param with_decode_path: print decode path
1432 :param decode_path_only: print only that specified decode path
1434 def _pprint_pps(pps):
1436 if hasattr(pp, "_fields"):
1438 decode_path_only != () and
1440 str(p) for p in pp.decode_path[:len(decode_path_only)]
1441 ) != decode_path_only
1445 yield pp_console_row(
1450 with_colours=with_colours,
1451 with_decode_path=with_decode_path,
1452 decode_path_len_decrease=len(decode_path_only),
1454 for row in pp_console_blob(
1456 decode_path_len_decrease=len(decode_path_only),
1460 yield pp_console_row(
1465 with_colours=with_colours,
1466 with_decode_path=with_decode_path,
1467 decode_path_len_decrease=len(decode_path_only),
1470 for row in _pprint_pps(pp):
1472 return "\n".join(_pprint_pps(obj.pps()))
1475 ########################################################################
1476 # ASN.1 primitive types
1477 ########################################################################
1480 """``BOOLEAN`` boolean type
1482 >>> b = Boolean(True)
1484 >>> b == Boolean(True)
1490 tag_default = tag_encode(1)
1491 asn1_type_name = "BOOLEAN"
1503 :param value: set the value. Either boolean type, or
1504 :py:class:`pyderasn.Boolean` object
1505 :param bytes impl: override default tag with ``IMPLICIT`` one
1506 :param bytes expl: override default tag with ``EXPLICIT`` one
1507 :param default: set default value. Type same as in ``value``
1508 :param bool optional: is object ``OPTIONAL`` in sequence
1510 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1511 self._value = None if value is None else self._value_sanitize(value)
1512 if default is not None:
1513 default = self._value_sanitize(default)
1514 self.default = self.__class__(
1520 self._value = default
1522 def _value_sanitize(self, value):
1523 if issubclass(value.__class__, Boolean):
1525 if isinstance(value, bool):
1527 raise InvalidValueType((self.__class__, bool))
1531 return self._value is not None
1534 obj = self.__class__()
1535 obj._value = self._value
1537 obj._expl = self._expl
1538 obj.default = self.default
1539 obj.optional = self.optional
1540 obj.offset = self.offset
1541 obj.llen = self.llen
1542 obj.vlen = self.vlen
1545 def __nonzero__(self):
1546 self._assert_ready()
1550 self._assert_ready()
1553 def __eq__(self, their):
1554 if isinstance(their, bool):
1555 return self._value == their
1556 if not issubclass(their.__class__, Boolean):
1559 self._value == their._value and
1560 self.tag == their.tag and
1561 self._expl == their._expl
1572 return self.__class__(
1574 impl=self.tag if impl is None else impl,
1575 expl=self._expl if expl is None else expl,
1576 default=self.default if default is None else default,
1577 optional=self.optional if optional is None else optional,
1581 self._assert_ready()
1585 (b"\xFF" if self._value else b"\x00"),
1588 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1590 t, _, lv = tag_strip(tlv)
1591 except DecodeError as err:
1592 raise err.__class__(
1594 klass=self.__class__,
1595 decode_path=decode_path,
1600 klass=self.__class__,
1601 decode_path=decode_path,
1607 l, _, v = len_decode(lv)
1608 except DecodeError as err:
1609 raise err.__class__(
1611 klass=self.__class__,
1612 decode_path=decode_path,
1616 raise InvalidLength(
1617 "Boolean's length must be equal to 1",
1618 klass=self.__class__,
1619 decode_path=decode_path,
1623 raise NotEnoughData(
1624 "encoded length is longer than data",
1625 klass=self.__class__,
1626 decode_path=decode_path,
1629 first_octet = byte2int(v)
1631 if first_octet == 0:
1633 elif first_octet == 0xFF:
1635 elif ctx.get("bered", False):
1640 "unacceptable Boolean value",
1641 klass=self.__class__,
1642 decode_path=decode_path,
1645 obj = self.__class__(
1649 default=self.default,
1650 optional=self.optional,
1651 _decoded=(offset, 1, 1),
1653 obj.ber_encoded = ber_encoded
1657 return pp_console_row(next(self.pps()))
1659 def pps(self, decode_path=()):
1661 asn1_type_name=self.asn1_type_name,
1662 obj_name=self.__class__.__name__,
1663 decode_path=decode_path,
1664 value=str(self._value) if self.ready else None,
1665 optional=self.optional,
1666 default=self == self.default,
1667 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1668 expl=None if self._expl is None else tag_decode(self._expl),
1673 expl_offset=self.expl_offset if self.expled else None,
1674 expl_tlen=self.expl_tlen if self.expled else None,
1675 expl_llen=self.expl_llen if self.expled else None,
1676 expl_vlen=self.expl_vlen if self.expled else None,
1677 expl_lenindef=self.expl_lenindef,
1678 ber_encoded=self.ber_encoded,
1681 for pp in self.pps_lenindef(decode_path):
1686 """``INTEGER`` integer type
1688 >>> b = Integer(-123)
1690 >>> b == Integer(-123)
1695 >>> Integer(2, bounds=(1, 3))
1697 >>> Integer(5, bounds=(1, 3))
1698 Traceback (most recent call last):
1699 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1703 class Version(Integer):
1710 >>> v = Version("v1")
1717 {'v3': 2, 'v1': 0, 'v2': 1}
1719 __slots__ = ("specs", "_bound_min", "_bound_max")
1720 tag_default = tag_encode(2)
1721 asn1_type_name = "INTEGER"
1735 :param value: set the value. Either integer type, named value
1736 (if ``schema`` is specified in the class), or
1737 :py:class:`pyderasn.Integer` object
1738 :param bounds: set ``(MIN, MAX)`` value constraint.
1739 (-inf, +inf) by default
1740 :param bytes impl: override default tag with ``IMPLICIT`` one
1741 :param bytes expl: override default tag with ``EXPLICIT`` one
1742 :param default: set default value. Type same as in ``value``
1743 :param bool optional: is object ``OPTIONAL`` in sequence
1745 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1747 specs = getattr(self, "schema", {}) if _specs is None else _specs
1748 self.specs = specs if isinstance(specs, dict) else dict(specs)
1749 self._bound_min, self._bound_max = getattr(
1752 (float("-inf"), float("+inf")),
1753 ) if bounds is None else bounds
1754 if value is not None:
1755 self._value = self._value_sanitize(value)
1756 if default is not None:
1757 default = self._value_sanitize(default)
1758 self.default = self.__class__(
1764 if self._value is None:
1765 self._value = default
1767 def _value_sanitize(self, value):
1768 if issubclass(value.__class__, Integer):
1769 value = value._value
1770 elif isinstance(value, integer_types):
1772 elif isinstance(value, str):
1773 value = self.specs.get(value)
1775 raise ObjUnknown("integer value: %s" % value)
1777 raise InvalidValueType((self.__class__, int, str))
1778 if not self._bound_min <= value <= self._bound_max:
1779 raise BoundsError(self._bound_min, value, self._bound_max)
1784 return self._value is not None
1787 obj = self.__class__(_specs=self.specs)
1788 obj._value = self._value
1789 obj._bound_min = self._bound_min
1790 obj._bound_max = self._bound_max
1792 obj._expl = self._expl
1793 obj.default = self.default
1794 obj.optional = self.optional
1795 obj.offset = self.offset
1796 obj.llen = self.llen
1797 obj.vlen = self.vlen
1801 self._assert_ready()
1802 return int(self._value)
1805 self._assert_ready()
1808 bytes(self._expl or b"") +
1809 str(self._value).encode("ascii"),
1812 def __eq__(self, their):
1813 if isinstance(their, integer_types):
1814 return self._value == their
1815 if not issubclass(their.__class__, Integer):
1818 self._value == their._value and
1819 self.tag == their.tag and
1820 self._expl == their._expl
1823 def __lt__(self, their):
1824 return self._value < their._value
1828 for name, value in self.specs.items():
1829 if value == self._value:
1841 return self.__class__(
1844 (self._bound_min, self._bound_max)
1845 if bounds is None else bounds
1847 impl=self.tag if impl is None else impl,
1848 expl=self._expl if expl is None else expl,
1849 default=self.default if default is None else default,
1850 optional=self.optional if optional is None else optional,
1855 self._assert_ready()
1859 octets = bytearray([0])
1863 octets = bytearray()
1865 octets.append((value & 0xFF) ^ 0xFF)
1867 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1870 octets = bytearray()
1872 octets.append(value & 0xFF)
1874 if octets[-1] & 0x80 > 0:
1877 octets = bytes(octets)
1879 bytes_len = ceil(value.bit_length() / 8) or 1
1882 octets = value.to_bytes(
1887 except OverflowError:
1891 return b"".join((self.tag, len_encode(len(octets)), octets))
1893 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1895 t, _, lv = tag_strip(tlv)
1896 except DecodeError as err:
1897 raise err.__class__(
1899 klass=self.__class__,
1900 decode_path=decode_path,
1905 klass=self.__class__,
1906 decode_path=decode_path,
1912 l, llen, v = len_decode(lv)
1913 except DecodeError as err:
1914 raise err.__class__(
1916 klass=self.__class__,
1917 decode_path=decode_path,
1921 raise NotEnoughData(
1922 "encoded length is longer than data",
1923 klass=self.__class__,
1924 decode_path=decode_path,
1928 raise NotEnoughData(
1930 klass=self.__class__,
1931 decode_path=decode_path,
1934 v, tail = v[:l], v[l:]
1935 first_octet = byte2int(v)
1937 second_octet = byte2int(v[1:])
1939 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1940 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1943 "non normalized integer",
1944 klass=self.__class__,
1945 decode_path=decode_path,
1950 if first_octet & 0x80 > 0:
1951 octets = bytearray()
1952 for octet in bytearray(v):
1953 octets.append(octet ^ 0xFF)
1954 for octet in octets:
1955 value = (value << 8) | octet
1959 for octet in bytearray(v):
1960 value = (value << 8) | octet
1962 value = int.from_bytes(v, byteorder="big", signed=True)
1964 obj = self.__class__(
1966 bounds=(self._bound_min, self._bound_max),
1969 default=self.default,
1970 optional=self.optional,
1972 _decoded=(offset, llen, l),
1974 except BoundsError as err:
1977 klass=self.__class__,
1978 decode_path=decode_path,
1984 return pp_console_row(next(self.pps()))
1986 def pps(self, decode_path=()):
1988 asn1_type_name=self.asn1_type_name,
1989 obj_name=self.__class__.__name__,
1990 decode_path=decode_path,
1991 value=(self.named or str(self._value)) if self.ready else None,
1992 optional=self.optional,
1993 default=self == self.default,
1994 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1995 expl=None if self._expl is None else tag_decode(self._expl),
2000 expl_offset=self.expl_offset if self.expled else None,
2001 expl_tlen=self.expl_tlen if self.expled else None,
2002 expl_llen=self.expl_llen if self.expled else None,
2003 expl_vlen=self.expl_vlen if self.expled else None,
2004 expl_lenindef=self.expl_lenindef,
2007 for pp in self.pps_lenindef(decode_path):
2011 class BitString(Obj):
2012 """``BIT STRING`` bit string type
2014 >>> BitString(b"hello world")
2015 BIT STRING 88 bits 68656c6c6f20776f726c64
2018 >>> b == b"hello world"
2023 >>> BitString("'0A3B5F291CD'H")
2024 BIT STRING 44 bits 0a3b5f291cd0
2025 >>> b = BitString("'010110000000'B")
2026 BIT STRING 12 bits 5800
2029 >>> b[0], b[1], b[2], b[3]
2030 (False, True, False, True)
2034 [False, True, False, True, True, False, False, False, False, False, False, False]
2038 class KeyUsage(BitString):
2040 ("digitalSignature", 0),
2041 ("nonRepudiation", 1),
2042 ("keyEncipherment", 2),
2045 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2046 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2048 ['nonRepudiation', 'keyEncipherment']
2050 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2054 Pay attention that BIT STRING can be encoded both in primitive
2055 and constructed forms. Decoder always checks constructed form tag
2056 additionally to specified primitive one. If BER decoding is
2057 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2058 of DER restrictions.
2060 __slots__ = ("tag_constructed", "specs", "defined")
2061 tag_default = tag_encode(3)
2062 asn1_type_name = "BIT STRING"
2075 :param value: set the value. Either binary type, tuple of named
2076 values (if ``schema`` is specified in the class),
2077 string in ``'XXX...'B`` form, or
2078 :py:class:`pyderasn.BitString` object
2079 :param bytes impl: override default tag with ``IMPLICIT`` one
2080 :param bytes expl: override default tag with ``EXPLICIT`` one
2081 :param default: set default value. Type same as in ``value``
2082 :param bool optional: is object ``OPTIONAL`` in sequence
2084 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2085 specs = getattr(self, "schema", {}) if _specs is None else _specs
2086 self.specs = specs if isinstance(specs, dict) else dict(specs)
2087 self._value = None if value is None else self._value_sanitize(value)
2088 if default is not None:
2089 default = self._value_sanitize(default)
2090 self.default = self.__class__(
2096 self._value = default
2098 tag_klass, _, tag_num = tag_decode(self.tag)
2099 self.tag_constructed = tag_encode(
2101 form=TagFormConstructed,
2105 def _bits2octets(self, bits):
2106 if len(self.specs) > 0:
2107 bits = bits.rstrip("0")
2109 bits += "0" * ((8 - (bit_len % 8)) % 8)
2110 octets = bytearray(len(bits) // 8)
2111 for i in six_xrange(len(octets)):
2112 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2113 return bit_len, bytes(octets)
2115 def _value_sanitize(self, value):
2116 if issubclass(value.__class__, BitString):
2118 if isinstance(value, (string_types, binary_type)):
2120 isinstance(value, string_types) and
2121 value.startswith("'")
2123 if value.endswith("'B"):
2125 if not set(value) <= set(("0", "1")):
2126 raise ValueError("B's coding contains unacceptable chars")
2127 return self._bits2octets(value)
2128 elif value.endswith("'H"):
2132 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2134 if isinstance(value, binary_type):
2135 return (len(value) * 8, value)
2137 raise InvalidValueType((self.__class__, string_types, binary_type))
2138 if isinstance(value, tuple):
2141 isinstance(value[0], integer_types) and
2142 isinstance(value[1], binary_type)
2147 bit = self.specs.get(name)
2149 raise ObjUnknown("BitString value: %s" % name)
2152 return self._bits2octets("")
2154 return self._bits2octets("".join(
2155 ("1" if bit in bits else "0")
2156 for bit in six_xrange(max(bits) + 1)
2158 raise InvalidValueType((self.__class__, binary_type, string_types))
2162 return self._value is not None
2165 obj = self.__class__(_specs=self.specs)
2167 if value is not None:
2168 value = (value[0], value[1])
2171 obj._expl = self._expl
2172 obj.default = self.default
2173 obj.optional = self.optional
2174 obj.offset = self.offset
2175 obj.llen = self.llen
2176 obj.vlen = self.vlen
2180 self._assert_ready()
2181 for i in six_xrange(self._value[0]):
2186 self._assert_ready()
2187 return self._value[0]
2189 def __bytes__(self):
2190 self._assert_ready()
2191 return self._value[1]
2193 def __eq__(self, their):
2194 if isinstance(their, bytes):
2195 return self._value[1] == their
2196 if not issubclass(their.__class__, BitString):
2199 self._value == their._value and
2200 self.tag == their.tag and
2201 self._expl == their._expl
2206 return [name for name, bit in self.specs.items() if self[bit]]
2216 return self.__class__(
2218 impl=self.tag if impl is None else impl,
2219 expl=self._expl if expl is None else expl,
2220 default=self.default if default is None else default,
2221 optional=self.optional if optional is None else optional,
2225 def __getitem__(self, key):
2226 if isinstance(key, int):
2227 bit_len, octets = self._value
2231 byte2int(memoryview(octets)[key // 8:]) >>
2234 if isinstance(key, string_types):
2235 value = self.specs.get(key)
2237 raise ObjUnknown("BitString value: %s" % key)
2239 raise InvalidValueType((int, str))
2242 self._assert_ready()
2243 bit_len, octets = self._value
2246 len_encode(len(octets) + 1),
2247 int2byte((8 - bit_len % 8) % 8),
2251 def _decode_chunk(self, lv, offset, decode_path, ctx):
2253 l, llen, v = len_decode(lv)
2254 except DecodeError as err:
2255 raise err.__class__(
2257 klass=self.__class__,
2258 decode_path=decode_path,
2262 raise NotEnoughData(
2263 "encoded length is longer than data",
2264 klass=self.__class__,
2265 decode_path=decode_path,
2269 raise NotEnoughData(
2271 klass=self.__class__,
2272 decode_path=decode_path,
2275 pad_size = byte2int(v)
2276 if l == 1 and pad_size != 0:
2278 "invalid empty value",
2279 klass=self.__class__,
2280 decode_path=decode_path,
2286 klass=self.__class__,
2287 decode_path=decode_path,
2290 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2293 klass=self.__class__,
2294 decode_path=decode_path,
2297 v, tail = v[:l], v[l:]
2298 obj = self.__class__(
2299 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2302 default=self.default,
2303 optional=self.optional,
2305 _decoded=(offset, llen, l),
2309 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2311 t, tlen, lv = tag_strip(tlv)
2312 except DecodeError as err:
2313 raise err.__class__(
2315 klass=self.__class__,
2316 decode_path=decode_path,
2320 if tag_only: # pragma: no cover
2322 return self._decode_chunk(lv, offset, decode_path, ctx)
2323 if t == self.tag_constructed:
2324 if not ctx.get("bered", False):
2326 "unallowed BER constructed encoding",
2327 klass=self.__class__,
2328 decode_path=decode_path,
2331 if tag_only: # pragma: no cover
2335 l, llen, v = len_decode(lv)
2336 except LenIndefForm:
2337 llen, l, v = 1, 0, lv[1:]
2339 except DecodeError as err:
2340 raise err.__class__(
2342 klass=self.__class__,
2343 decode_path=decode_path,
2347 raise NotEnoughData(
2348 "encoded length is longer than data",
2349 klass=self.__class__,
2350 decode_path=decode_path,
2353 if not lenindef and l == 0:
2354 raise NotEnoughData(
2356 klass=self.__class__,
2357 decode_path=decode_path,
2361 sub_offset = offset + tlen + llen
2365 if v[:EOC_LEN].tobytes() == EOC:
2372 "chunk out of bounds",
2373 klass=self.__class__,
2374 decode_path=decode_path + (str(len(chunks) - 1),),
2375 offset=chunks[-1].offset,
2377 sub_decode_path = decode_path + (str(len(chunks)),)
2379 chunk, v_tail = BitString().decode(
2382 decode_path=sub_decode_path,
2388 "expected BitString encoded chunk",
2389 klass=self.__class__,
2390 decode_path=sub_decode_path,
2393 chunks.append(chunk)
2394 sub_offset += chunk.tlvlen
2395 vlen += chunk.tlvlen
2397 if len(chunks) == 0:
2400 klass=self.__class__,
2401 decode_path=decode_path,
2406 for chunk_i, chunk in enumerate(chunks[:-1]):
2407 if chunk.bit_len % 8 != 0:
2409 "BitString chunk is not multiple of 8 bits",
2410 klass=self.__class__,
2411 decode_path=decode_path + (str(chunk_i),),
2412 offset=chunk.offset,
2414 values.append(bytes(chunk))
2415 bit_len += chunk.bit_len
2416 chunk_last = chunks[-1]
2417 values.append(bytes(chunk_last))
2418 bit_len += chunk_last.bit_len
2419 obj = self.__class__(
2420 value=(bit_len, b"".join(values)),
2423 default=self.default,
2424 optional=self.optional,
2426 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2428 obj.lenindef = lenindef
2429 obj.ber_encoded = True
2430 return obj, (v[EOC_LEN:] if lenindef else v)
2432 klass=self.__class__,
2433 decode_path=decode_path,
2438 return pp_console_row(next(self.pps()))
2440 def pps(self, decode_path=()):
2444 bit_len, blob = self._value
2445 value = "%d bits" % bit_len
2446 if len(self.specs) > 0:
2447 blob = tuple(self.named)
2449 asn1_type_name=self.asn1_type_name,
2450 obj_name=self.__class__.__name__,
2451 decode_path=decode_path,
2454 optional=self.optional,
2455 default=self == self.default,
2456 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2457 expl=None if self._expl is None else tag_decode(self._expl),
2462 expl_offset=self.expl_offset if self.expled else None,
2463 expl_tlen=self.expl_tlen if self.expled else None,
2464 expl_llen=self.expl_llen if self.expled else None,
2465 expl_vlen=self.expl_vlen if self.expled else None,
2466 expl_lenindef=self.expl_lenindef,
2467 lenindef=self.lenindef,
2468 ber_encoded=self.ber_encoded,
2471 defined_by, defined = self.defined or (None, None)
2472 if defined_by is not None:
2474 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2476 for pp in self.pps_lenindef(decode_path):
2480 class OctetString(Obj):
2481 """``OCTET STRING`` binary string type
2483 >>> s = OctetString(b"hello world")
2484 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2485 >>> s == OctetString(b"hello world")
2490 >>> OctetString(b"hello", bounds=(4, 4))
2491 Traceback (most recent call last):
2492 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2493 >>> OctetString(b"hell", bounds=(4, 4))
2494 OCTET STRING 4 bytes 68656c6c
2498 Pay attention that OCTET STRING can be encoded both in primitive
2499 and constructed forms. Decoder always checks constructed form tag
2500 additionally to specified primitive one. If BER decoding is
2501 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2502 of DER restrictions.
2504 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2505 tag_default = tag_encode(4)
2506 asn1_type_name = "OCTET STRING"
2519 :param value: set the value. Either binary type, or
2520 :py:class:`pyderasn.OctetString` object
2521 :param bounds: set ``(MIN, MAX)`` value size constraint.
2522 (-inf, +inf) by default
2523 :param bytes impl: override default tag with ``IMPLICIT`` one
2524 :param bytes expl: override default tag with ``EXPLICIT`` one
2525 :param default: set default value. Type same as in ``value``
2526 :param bool optional: is object ``OPTIONAL`` in sequence
2528 super(OctetString, self).__init__(
2536 self._bound_min, self._bound_max = getattr(
2540 ) if bounds is None else bounds
2541 if value is not None:
2542 self._value = self._value_sanitize(value)
2543 if default is not None:
2544 default = self._value_sanitize(default)
2545 self.default = self.__class__(
2550 if self._value is None:
2551 self._value = default
2553 tag_klass, _, tag_num = tag_decode(self.tag)
2554 self.tag_constructed = tag_encode(
2556 form=TagFormConstructed,
2560 def _value_sanitize(self, value):
2561 if issubclass(value.__class__, OctetString):
2562 value = value._value
2563 elif isinstance(value, binary_type):
2566 raise InvalidValueType((self.__class__, bytes))
2567 if not self._bound_min <= len(value) <= self._bound_max:
2568 raise BoundsError(self._bound_min, len(value), self._bound_max)
2573 return self._value is not None
2576 obj = self.__class__()
2577 obj._value = self._value
2578 obj._bound_min = self._bound_min
2579 obj._bound_max = self._bound_max
2581 obj._expl = self._expl
2582 obj.default = self.default
2583 obj.optional = self.optional
2584 obj.offset = self.offset
2585 obj.llen = self.llen
2586 obj.vlen = self.vlen
2589 def __bytes__(self):
2590 self._assert_ready()
2593 def __eq__(self, their):
2594 if isinstance(their, binary_type):
2595 return self._value == their
2596 if not issubclass(their.__class__, OctetString):
2599 self._value == their._value and
2600 self.tag == their.tag and
2601 self._expl == their._expl
2604 def __lt__(self, their):
2605 return self._value < their._value
2616 return self.__class__(
2619 (self._bound_min, self._bound_max)
2620 if bounds is None else bounds
2622 impl=self.tag if impl is None else impl,
2623 expl=self._expl if expl is None else expl,
2624 default=self.default if default is None else default,
2625 optional=self.optional if optional is None else optional,
2629 self._assert_ready()
2632 len_encode(len(self._value)),
2636 def _decode_chunk(self, lv, offset, decode_path, ctx):
2638 l, llen, v = len_decode(lv)
2639 except DecodeError as err:
2640 raise err.__class__(
2642 klass=self.__class__,
2643 decode_path=decode_path,
2647 raise NotEnoughData(
2648 "encoded length is longer than data",
2649 klass=self.__class__,
2650 decode_path=decode_path,
2653 v, tail = v[:l], v[l:]
2655 obj = self.__class__(
2657 bounds=(self._bound_min, self._bound_max),
2660 default=self.default,
2661 optional=self.optional,
2662 _decoded=(offset, llen, l),
2664 except DecodeError as err:
2667 klass=self.__class__,
2668 decode_path=decode_path,
2671 except BoundsError as err:
2674 klass=self.__class__,
2675 decode_path=decode_path,
2680 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2682 t, tlen, lv = tag_strip(tlv)
2683 except DecodeError as err:
2684 raise err.__class__(
2686 klass=self.__class__,
2687 decode_path=decode_path,
2693 return self._decode_chunk(lv, offset, decode_path, ctx)
2694 if t == self.tag_constructed:
2695 if not ctx.get("bered", False):
2697 "unallowed BER constructed encoding",
2698 klass=self.__class__,
2699 decode_path=decode_path,
2706 l, llen, v = len_decode(lv)
2707 except LenIndefForm:
2708 llen, l, v = 1, 0, lv[1:]
2710 except DecodeError as err:
2711 raise err.__class__(
2713 klass=self.__class__,
2714 decode_path=decode_path,
2718 raise NotEnoughData(
2719 "encoded length is longer than data",
2720 klass=self.__class__,
2721 decode_path=decode_path,
2725 sub_offset = offset + tlen + llen
2729 if v[:EOC_LEN].tobytes() == EOC:
2736 "chunk out of bounds",
2737 klass=self.__class__,
2738 decode_path=decode_path + (str(len(chunks) - 1),),
2739 offset=chunks[-1].offset,
2741 sub_decode_path = decode_path + (str(len(chunks)),)
2743 chunk, v_tail = OctetString().decode(
2746 decode_path=sub_decode_path,
2752 "expected OctetString encoded chunk",
2753 klass=self.__class__,
2754 decode_path=sub_decode_path,
2757 chunks.append(chunk)
2758 sub_offset += chunk.tlvlen
2759 vlen += chunk.tlvlen
2762 obj = self.__class__(
2763 value=b"".join(bytes(chunk) for chunk in chunks),
2764 bounds=(self._bound_min, self._bound_max),
2767 default=self.default,
2768 optional=self.optional,
2769 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2771 except DecodeError as err:
2774 klass=self.__class__,
2775 decode_path=decode_path,
2778 except BoundsError as err:
2781 klass=self.__class__,
2782 decode_path=decode_path,
2785 obj.lenindef = lenindef
2786 obj.ber_encoded = True
2787 return obj, (v[EOC_LEN:] if lenindef else v)
2789 klass=self.__class__,
2790 decode_path=decode_path,
2795 return pp_console_row(next(self.pps()))
2797 def pps(self, decode_path=()):
2799 asn1_type_name=self.asn1_type_name,
2800 obj_name=self.__class__.__name__,
2801 decode_path=decode_path,
2802 value=("%d bytes" % len(self._value)) if self.ready else None,
2803 blob=self._value if self.ready else None,
2804 optional=self.optional,
2805 default=self == self.default,
2806 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2807 expl=None if self._expl is None else tag_decode(self._expl),
2812 expl_offset=self.expl_offset if self.expled else None,
2813 expl_tlen=self.expl_tlen if self.expled else None,
2814 expl_llen=self.expl_llen if self.expled else None,
2815 expl_vlen=self.expl_vlen if self.expled else None,
2816 expl_lenindef=self.expl_lenindef,
2817 lenindef=self.lenindef,
2818 ber_encoded=self.ber_encoded,
2821 defined_by, defined = self.defined or (None, None)
2822 if defined_by is not None:
2824 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2826 for pp in self.pps_lenindef(decode_path):
2831 """``NULL`` null object
2839 tag_default = tag_encode(5)
2840 asn1_type_name = "NULL"
2844 value=None, # unused, but Sequence passes it
2851 :param bytes impl: override default tag with ``IMPLICIT`` one
2852 :param bytes expl: override default tag with ``EXPLICIT`` one
2853 :param bool optional: is object ``OPTIONAL`` in sequence
2855 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2863 obj = self.__class__()
2865 obj._expl = self._expl
2866 obj.default = self.default
2867 obj.optional = self.optional
2868 obj.offset = self.offset
2869 obj.llen = self.llen
2870 obj.vlen = self.vlen
2873 def __eq__(self, their):
2874 if not issubclass(their.__class__, Null):
2877 self.tag == their.tag and
2878 self._expl == their._expl
2888 return self.__class__(
2889 impl=self.tag if impl is None else impl,
2890 expl=self._expl if expl is None else expl,
2891 optional=self.optional if optional is None else optional,
2895 return self.tag + len_encode(0)
2897 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2899 t, _, lv = tag_strip(tlv)
2900 except DecodeError as err:
2901 raise err.__class__(
2903 klass=self.__class__,
2904 decode_path=decode_path,
2909 klass=self.__class__,
2910 decode_path=decode_path,
2913 if tag_only: # pragma: no cover
2916 l, _, v = len_decode(lv)
2917 except DecodeError as err:
2918 raise err.__class__(
2920 klass=self.__class__,
2921 decode_path=decode_path,
2925 raise InvalidLength(
2926 "Null must have zero length",
2927 klass=self.__class__,
2928 decode_path=decode_path,
2931 obj = self.__class__(
2934 optional=self.optional,
2935 _decoded=(offset, 1, 0),
2940 return pp_console_row(next(self.pps()))
2942 def pps(self, decode_path=()):
2944 asn1_type_name=self.asn1_type_name,
2945 obj_name=self.__class__.__name__,
2946 decode_path=decode_path,
2947 optional=self.optional,
2948 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2949 expl=None if self._expl is None else tag_decode(self._expl),
2954 expl_offset=self.expl_offset if self.expled else None,
2955 expl_tlen=self.expl_tlen if self.expled else None,
2956 expl_llen=self.expl_llen if self.expled else None,
2957 expl_vlen=self.expl_vlen if self.expled else None,
2958 expl_lenindef=self.expl_lenindef,
2961 for pp in self.pps_lenindef(decode_path):
2965 class ObjectIdentifier(Obj):
2966 """``OBJECT IDENTIFIER`` OID type
2968 >>> oid = ObjectIdentifier((1, 2, 3))
2969 OBJECT IDENTIFIER 1.2.3
2970 >>> oid == ObjectIdentifier("1.2.3")
2976 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2977 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2979 >>> str(ObjectIdentifier((3, 1)))
2980 Traceback (most recent call last):
2981 pyderasn.InvalidOID: unacceptable first arc value
2983 __slots__ = ("defines",)
2984 tag_default = tag_encode(6)
2985 asn1_type_name = "OBJECT IDENTIFIER"
2998 :param value: set the value. Either tuples of integers,
2999 string of "."-concatenated integers, or
3000 :py:class:`pyderasn.ObjectIdentifier` object
3001 :param defines: sequence of tuples. Each tuple has two elements.
3002 First one is relative to current one decode
3003 path, aiming to the field defined by that OID.
3004 Read about relative path in
3005 :py:func:`pyderasn.abs_decode_path`. Second
3006 tuple element is ``{OID: pyderasn.Obj()}``
3007 dictionary, mapping between current OID value
3008 and structure applied to defined field.
3009 :ref:`Read about DEFINED BY <definedby>`
3010 :param bytes impl: override default tag with ``IMPLICIT`` one
3011 :param bytes expl: override default tag with ``EXPLICIT`` one
3012 :param default: set default value. Type same as in ``value``
3013 :param bool optional: is object ``OPTIONAL`` in sequence
3015 super(ObjectIdentifier, self).__init__(
3023 if value is not None:
3024 self._value = self._value_sanitize(value)
3025 if default is not None:
3026 default = self._value_sanitize(default)
3027 self.default = self.__class__(
3032 if self._value is None:
3033 self._value = default
3034 self.defines = defines
3036 def __add__(self, their):
3037 if isinstance(their, self.__class__):
3038 return self.__class__(self._value + their._value)
3039 if isinstance(their, tuple):
3040 return self.__class__(self._value + their)
3041 raise InvalidValueType((self.__class__, tuple))
3043 def _value_sanitize(self, value):
3044 if issubclass(value.__class__, ObjectIdentifier):
3046 if isinstance(value, string_types):
3048 value = tuple(int(arc) for arc in value.split("."))
3050 raise InvalidOID("unacceptable arcs values")
3051 if isinstance(value, tuple):
3053 raise InvalidOID("less than 2 arcs")
3054 first_arc = value[0]
3055 if first_arc in (0, 1):
3056 if not (0 <= value[1] <= 39):
3057 raise InvalidOID("second arc is too wide")
3058 elif first_arc == 2:
3061 raise InvalidOID("unacceptable first arc value")
3063 raise InvalidValueType((self.__class__, str, tuple))
3067 return self._value is not None
3070 obj = self.__class__()
3071 obj._value = self._value
3072 obj.defines = self.defines
3074 obj._expl = self._expl
3075 obj.default = self.default
3076 obj.optional = self.optional
3077 obj.offset = self.offset
3078 obj.llen = self.llen
3079 obj.vlen = self.vlen
3083 self._assert_ready()
3084 return iter(self._value)
3087 return ".".join(str(arc) for arc in self._value or ())
3090 self._assert_ready()
3093 bytes(self._expl or b"") +
3094 str(self._value).encode("ascii"),
3097 def __eq__(self, their):
3098 if isinstance(their, tuple):
3099 return self._value == their
3100 if not issubclass(their.__class__, ObjectIdentifier):
3103 self.tag == their.tag and
3104 self._expl == their._expl and
3105 self._value == their._value
3108 def __lt__(self, their):
3109 return self._value < their._value
3120 return self.__class__(
3122 defines=self.defines if defines is None else defines,
3123 impl=self.tag if impl is None else impl,
3124 expl=self._expl if expl is None else expl,
3125 default=self.default if default is None else default,
3126 optional=self.optional if optional is None else optional,
3130 self._assert_ready()
3132 first_value = value[1]
3133 first_arc = value[0]
3136 elif first_arc == 1:
3138 elif first_arc == 2:
3140 else: # pragma: no cover
3141 raise RuntimeError("invalid arc is stored")
3142 octets = [zero_ended_encode(first_value)]
3143 for arc in value[2:]:
3144 octets.append(zero_ended_encode(arc))
3145 v = b"".join(octets)
3146 return b"".join((self.tag, len_encode(len(v)), v))
3148 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3150 t, _, lv = tag_strip(tlv)
3151 except DecodeError as err:
3152 raise err.__class__(
3154 klass=self.__class__,
3155 decode_path=decode_path,
3160 klass=self.__class__,
3161 decode_path=decode_path,
3164 if tag_only: # pragma: no cover
3167 l, llen, v = len_decode(lv)
3168 except DecodeError as err:
3169 raise err.__class__(
3171 klass=self.__class__,
3172 decode_path=decode_path,
3176 raise NotEnoughData(
3177 "encoded length is longer than data",
3178 klass=self.__class__,
3179 decode_path=decode_path,
3183 raise NotEnoughData(
3185 klass=self.__class__,
3186 decode_path=decode_path,
3189 v, tail = v[:l], v[l:]
3195 octet = indexbytes(v, i)
3196 arc = (arc << 7) | (octet & 0x7F)
3197 if octet & 0x80 == 0:
3205 klass=self.__class__,
3206 decode_path=decode_path,
3210 second_arc = arcs[0]
3211 if 0 <= second_arc <= 39:
3213 elif 40 <= second_arc <= 79:
3219 obj = self.__class__(
3220 value=tuple([first_arc, second_arc] + arcs[1:]),
3223 default=self.default,
3224 optional=self.optional,
3225 _decoded=(offset, llen, l),
3230 return pp_console_row(next(self.pps()))
3232 def pps(self, decode_path=()):
3234 asn1_type_name=self.asn1_type_name,
3235 obj_name=self.__class__.__name__,
3236 decode_path=decode_path,
3237 value=str(self) if self.ready else None,
3238 optional=self.optional,
3239 default=self == self.default,
3240 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3241 expl=None if self._expl is None else tag_decode(self._expl),
3246 expl_offset=self.expl_offset if self.expled else None,
3247 expl_tlen=self.expl_tlen if self.expled else None,
3248 expl_llen=self.expl_llen if self.expled else None,
3249 expl_vlen=self.expl_vlen if self.expled else None,
3250 expl_lenindef=self.expl_lenindef,
3253 for pp in self.pps_lenindef(decode_path):
3257 class Enumerated(Integer):
3258 """``ENUMERATED`` integer type
3260 This type is identical to :py:class:`pyderasn.Integer`, but requires
3261 schema to be specified and does not accept values missing from it.
3264 tag_default = tag_encode(10)
3265 asn1_type_name = "ENUMERATED"
3276 bounds=None, # dummy argument, workability for Integer.decode
3278 super(Enumerated, self).__init__(
3287 if len(self.specs) == 0:
3288 raise ValueError("schema must be specified")
3290 def _value_sanitize(self, value):
3291 if isinstance(value, self.__class__):
3292 value = value._value
3293 elif isinstance(value, integer_types):
3294 if value not in list(self.specs.values()):
3296 "unknown integer value: %s" % value,
3297 klass=self.__class__,
3299 elif isinstance(value, string_types):
3300 value = self.specs.get(value)
3302 raise ObjUnknown("integer value: %s" % value)
3304 raise InvalidValueType((self.__class__, int, str))
3308 obj = self.__class__(_specs=self.specs)
3309 obj._value = self._value
3310 obj._bound_min = self._bound_min
3311 obj._bound_max = self._bound_max
3313 obj._expl = self._expl
3314 obj.default = self.default
3315 obj.optional = self.optional
3316 obj.offset = self.offset
3317 obj.llen = self.llen
3318 obj.vlen = self.vlen
3330 return self.__class__(
3332 impl=self.tag if impl is None else impl,
3333 expl=self._expl if expl is None else expl,
3334 default=self.default if default is None else default,
3335 optional=self.optional if optional is None else optional,
3340 class CommonString(OctetString):
3341 """Common class for all strings
3343 Everything resembles :py:class:`pyderasn.OctetString`, except
3344 ability to deal with unicode text strings.
3346 >>> hexenc("привет мир".encode("utf-8"))
3347 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3348 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3350 >>> s = UTF8String("привет мир")
3351 UTF8String UTF8String привет мир
3353 'привет мир'
3354 >>> hexenc(bytes(s))
3355 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3357 >>> PrintableString("привет мир")
3358 Traceback (most recent call last):
3359 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3361 >>> BMPString("ада", bounds=(2, 2))
3362 Traceback (most recent call last):
3363 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3364 >>> s = BMPString("ад", bounds=(2, 2))
3367 >>> hexenc(bytes(s))
3375 * - :py:class:`pyderasn.UTF8String`
3377 * - :py:class:`pyderasn.NumericString`
3379 * - :py:class:`pyderasn.PrintableString`
3381 * - :py:class:`pyderasn.TeletexString`
3383 * - :py:class:`pyderasn.T61String`
3385 * - :py:class:`pyderasn.VideotexString`
3387 * - :py:class:`pyderasn.IA5String`
3389 * - :py:class:`pyderasn.GraphicString`
3391 * - :py:class:`pyderasn.VisibleString`
3393 * - :py:class:`pyderasn.ISO646String`
3395 * - :py:class:`pyderasn.GeneralString`
3397 * - :py:class:`pyderasn.UniversalString`
3399 * - :py:class:`pyderasn.BMPString`
3402 __slots__ = ("encoding",)
3404 def _value_sanitize(self, value):
3406 value_decoded = None
3407 if isinstance(value, self.__class__):
3408 value_raw = value._value
3409 elif isinstance(value, text_type):
3410 value_decoded = value
3411 elif isinstance(value, binary_type):
3414 raise InvalidValueType((self.__class__, text_type, binary_type))
3417 value_decoded.encode(self.encoding)
3418 if value_raw is None else value_raw
3421 value_raw.decode(self.encoding)
3422 if value_decoded is None else value_decoded
3424 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3425 raise DecodeError(str(err))
3426 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3434 def __eq__(self, their):
3435 if isinstance(their, binary_type):
3436 return self._value == their
3437 if isinstance(their, text_type):
3438 return self._value == their.encode(self.encoding)
3439 if not isinstance(their, self.__class__):
3442 self._value == their._value and
3443 self.tag == their.tag and
3444 self._expl == their._expl
3447 def __unicode__(self):
3449 return self._value.decode(self.encoding)
3450 return text_type(self._value)
3453 return pp_console_row(next(self.pps(no_unicode=PY2)))
3455 def pps(self, decode_path=(), no_unicode=False):
3458 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3460 asn1_type_name=self.asn1_type_name,
3461 obj_name=self.__class__.__name__,
3462 decode_path=decode_path,
3464 optional=self.optional,
3465 default=self == self.default,
3466 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3467 expl=None if self._expl is None else tag_decode(self._expl),
3472 expl_offset=self.expl_offset if self.expled else None,
3473 expl_tlen=self.expl_tlen if self.expled else None,
3474 expl_llen=self.expl_llen if self.expled else None,
3475 expl_vlen=self.expl_vlen if self.expled else None,
3476 expl_lenindef=self.expl_lenindef,
3477 ber_encoded=self.ber_encoded,
3480 for pp in self.pps_lenindef(decode_path):
3484 class UTF8String(CommonString):
3486 tag_default = tag_encode(12)
3488 asn1_type_name = "UTF8String"
3491 class NumericString(CommonString):
3494 Its value is properly sanitized: only ASCII digits can be stored.
3497 tag_default = tag_encode(18)
3499 asn1_type_name = "NumericString"
3500 allowable_chars = set(digits.encode("ascii") + b" ")
3502 def _value_sanitize(self, value):
3503 value = super(NumericString, self)._value_sanitize(value)
3504 if not set(value) <= self.allowable_chars:
3505 raise DecodeError("non-numeric value")
3509 class PrintableString(CommonString):
3511 tag_default = tag_encode(19)
3513 asn1_type_name = "PrintableString"
3516 class TeletexString(CommonString):
3518 tag_default = tag_encode(20)
3520 asn1_type_name = "TeletexString"
3523 class T61String(TeletexString):
3525 asn1_type_name = "T61String"
3528 class VideotexString(CommonString):
3530 tag_default = tag_encode(21)
3531 encoding = "iso-8859-1"
3532 asn1_type_name = "VideotexString"
3535 class IA5String(CommonString):
3537 tag_default = tag_encode(22)
3539 asn1_type_name = "IA5"
3542 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3543 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3544 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3547 class UTCTime(CommonString):
3548 """``UTCTime`` datetime type
3550 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3551 UTCTime UTCTime 2017-09-30T22:07:50
3557 datetime.datetime(2017, 9, 30, 22, 7, 50)
3558 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3559 datetime.datetime(1957, 9, 30, 22, 7, 50)
3562 tag_default = tag_encode(23)
3564 asn1_type_name = "UTCTime"
3566 fmt = "%y%m%d%H%M%SZ"
3576 bounds=None, # dummy argument, workability for OctetString.decode
3579 :param value: set the value. Either datetime type, or
3580 :py:class:`pyderasn.UTCTime` object
3581 :param bytes impl: override default tag with ``IMPLICIT`` one
3582 :param bytes expl: override default tag with ``EXPLICIT`` one
3583 :param default: set default value. Type same as in ``value``
3584 :param bool optional: is object ``OPTIONAL`` in sequence
3586 super(UTCTime, self).__init__(
3594 if value is not None:
3595 self._value = self._value_sanitize(value)
3596 if default is not None:
3597 default = self._value_sanitize(default)
3598 self.default = self.__class__(
3603 if self._value is None:
3604 self._value = default
3606 def _value_sanitize(self, value):
3607 if isinstance(value, self.__class__):
3609 if isinstance(value, datetime):
3610 return value.strftime(self.fmt).encode("ascii")
3611 if isinstance(value, binary_type):
3613 value_decoded = value.decode("ascii")
3614 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3615 raise DecodeError("invalid UTCTime encoding")
3616 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3618 datetime.strptime(value_decoded, self.fmt)
3619 except (TypeError, ValueError):
3620 raise DecodeError("invalid UTCTime format")
3623 raise DecodeError("invalid UTCTime length")
3624 raise InvalidValueType((self.__class__, datetime))
3626 def __eq__(self, their):
3627 if isinstance(their, binary_type):
3628 return self._value == their
3629 if isinstance(their, datetime):
3630 return self.todatetime() == their
3631 if not isinstance(their, self.__class__):
3634 self._value == their._value and
3635 self.tag == their.tag and
3636 self._expl == their._expl
3639 def todatetime(self):
3640 """Convert to datetime
3644 Pay attention that UTCTime can not hold full year, so all years
3645 having < 50 years are treated as 20xx, 19xx otherwise, according
3646 to X.509 recomendation.
3648 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3649 year = value.year % 100
3651 year=(2000 + year) if year < 50 else (1900 + year),
3655 minute=value.minute,
3656 second=value.second,
3660 return pp_console_row(next(self.pps()))
3662 def pps(self, decode_path=()):
3664 asn1_type_name=self.asn1_type_name,
3665 obj_name=self.__class__.__name__,
3666 decode_path=decode_path,
3667 value=self.todatetime().isoformat() if self.ready else None,
3668 optional=self.optional,
3669 default=self == self.default,
3670 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3671 expl=None if self._expl is None else tag_decode(self._expl),
3676 expl_offset=self.expl_offset if self.expled else None,
3677 expl_tlen=self.expl_tlen if self.expled else None,
3678 expl_llen=self.expl_llen if self.expled else None,
3679 expl_vlen=self.expl_vlen if self.expled else None,
3680 expl_lenindef=self.expl_lenindef,
3681 ber_encoded=self.ber_encoded,
3684 for pp in self.pps_lenindef(decode_path):
3688 class GeneralizedTime(UTCTime):
3689 """``GeneralizedTime`` datetime type
3691 This type is similar to :py:class:`pyderasn.UTCTime`.
3693 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3694 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3696 '20170930220750.000123Z'
3697 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3698 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3701 tag_default = tag_encode(24)
3702 asn1_type_name = "GeneralizedTime"
3704 fmt = "%Y%m%d%H%M%SZ"
3705 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3707 def _value_sanitize(self, value):
3708 if isinstance(value, self.__class__):
3710 if isinstance(value, datetime):
3711 return value.strftime(
3712 self.fmt_ms if value.microsecond > 0 else self.fmt
3714 if isinstance(value, binary_type):
3716 value_decoded = value.decode("ascii")
3717 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3718 raise DecodeError("invalid GeneralizedTime encoding")
3719 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3721 datetime.strptime(value_decoded, self.fmt)
3722 except (TypeError, ValueError):
3724 "invalid GeneralizedTime (without ms) format",
3727 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3729 datetime.strptime(value_decoded, self.fmt_ms)
3730 except (TypeError, ValueError):
3732 "invalid GeneralizedTime (with ms) format",
3737 "invalid GeneralizedTime length",
3738 klass=self.__class__,
3740 raise InvalidValueType((self.__class__, datetime))
3742 def todatetime(self):
3743 value = self._value.decode("ascii")
3744 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3745 return datetime.strptime(value, self.fmt)
3746 return datetime.strptime(value, self.fmt_ms)
3749 class GraphicString(CommonString):
3751 tag_default = tag_encode(25)
3752 encoding = "iso-8859-1"
3753 asn1_type_name = "GraphicString"
3756 class VisibleString(CommonString):
3758 tag_default = tag_encode(26)
3760 asn1_type_name = "VisibleString"
3763 class ISO646String(VisibleString):
3765 asn1_type_name = "ISO646String"
3768 class GeneralString(CommonString):
3770 tag_default = tag_encode(27)
3771 encoding = "iso-8859-1"
3772 asn1_type_name = "GeneralString"
3775 class UniversalString(CommonString):
3777 tag_default = tag_encode(28)
3778 encoding = "utf-32-be"
3779 asn1_type_name = "UniversalString"
3782 class BMPString(CommonString):
3784 tag_default = tag_encode(30)
3785 encoding = "utf-16-be"
3786 asn1_type_name = "BMPString"
3790 """``CHOICE`` special type
3794 class GeneralName(Choice):
3796 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3797 ("dNSName", IA5String(impl=tag_ctxp(2))),
3800 >>> gn = GeneralName()
3802 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3803 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3804 >>> gn["dNSName"] = IA5String("bar.baz")
3805 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3806 >>> gn["rfc822Name"]
3809 [2] IA5String IA5 bar.baz
3812 >>> gn.value == gn["dNSName"]
3815 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3817 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3818 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3820 __slots__ = ("specs",)
3822 asn1_type_name = "CHOICE"
3835 :param value: set the value. Either ``(choice, value)`` tuple, or
3836 :py:class:`pyderasn.Choice` object
3837 :param bytes impl: can not be set, do **not** use it
3838 :param bytes expl: override default tag with ``EXPLICIT`` one
3839 :param default: set default value. Type same as in ``value``
3840 :param bool optional: is object ``OPTIONAL`` in sequence
3842 if impl is not None:
3843 raise ValueError("no implicit tag allowed for CHOICE")
3844 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3846 schema = getattr(self, "schema", ())
3847 if len(schema) == 0:
3848 raise ValueError("schema must be specified")
3850 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3853 if value is not None:
3854 self._value = self._value_sanitize(value)
3855 if default is not None:
3856 default_value = self._value_sanitize(default)
3857 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3858 default_obj.specs = self.specs
3859 default_obj._value = default_value
3860 self.default = default_obj
3862 self._value = default_obj.copy()._value
3864 def _value_sanitize(self, value):
3865 if isinstance(value, self.__class__):
3867 if isinstance(value, tuple) and len(value) == 2:
3869 spec = self.specs.get(choice)
3871 raise ObjUnknown(choice)
3872 if not isinstance(obj, spec.__class__):
3873 raise InvalidValueType((spec,))
3874 return (choice, spec(obj))
3875 raise InvalidValueType((self.__class__, tuple))
3879 return self._value is not None and self._value[1].ready
3883 return self.expl_lenindef or (
3884 (self._value is not None) and
3885 self._value[1].bered
3889 obj = self.__class__(schema=self.specs)
3890 obj._expl = self._expl
3891 obj.default = self.default
3892 obj.optional = self.optional
3893 obj.offset = self.offset
3894 obj.llen = self.llen
3895 obj.vlen = self.vlen
3897 if value is not None:
3898 obj._value = (value[0], value[1].copy())
3901 def __eq__(self, their):
3902 if isinstance(their, tuple) and len(their) == 2:
3903 return self._value == their
3904 if not isinstance(their, self.__class__):
3907 self.specs == their.specs and
3908 self._value == their._value
3918 return self.__class__(
3921 expl=self._expl if expl is None else expl,
3922 default=self.default if default is None else default,
3923 optional=self.optional if optional is None else optional,
3928 self._assert_ready()
3929 return self._value[0]
3933 self._assert_ready()
3934 return self._value[1]
3936 def __getitem__(self, key):
3937 if key not in self.specs:
3938 raise ObjUnknown(key)
3939 if self._value is None:
3941 choice, value = self._value
3946 def __setitem__(self, key, value):
3947 spec = self.specs.get(key)
3949 raise ObjUnknown(key)
3950 if not isinstance(value, spec.__class__):
3951 raise InvalidValueType((spec.__class__,))
3952 self._value = (key, spec(value))
3960 return self._value[1].decoded if self.ready else False
3963 self._assert_ready()
3964 return self._value[1].encode()
3966 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3967 for choice, spec in self.specs.items():
3968 sub_decode_path = decode_path + (choice,)
3974 decode_path=sub_decode_path,
3983 klass=self.__class__,
3984 decode_path=decode_path,
3987 if tag_only: # pragma: no cover
3989 value, tail = spec.decode(
3993 decode_path=sub_decode_path,
3996 obj = self.__class__(
3999 default=self.default,
4000 optional=self.optional,
4001 _decoded=(offset, 0, value.fulllen),
4003 obj._value = (choice, value)
4007 value = pp_console_row(next(self.pps()))
4009 value = "%s[%r]" % (value, self.value)
4012 def pps(self, decode_path=()):
4014 asn1_type_name=self.asn1_type_name,
4015 obj_name=self.__class__.__name__,
4016 decode_path=decode_path,
4017 value=self.choice if self.ready else None,
4018 optional=self.optional,
4019 default=self == self.default,
4020 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4021 expl=None if self._expl is None else tag_decode(self._expl),
4026 expl_lenindef=self.expl_lenindef,
4030 yield self.value.pps(decode_path=decode_path + (self.choice,))
4031 for pp in self.pps_lenindef(decode_path):
4035 class PrimitiveTypes(Choice):
4036 """Predefined ``CHOICE`` for all generic primitive types
4038 It could be useful for general decoding of some unspecified values:
4040 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4041 OCTET STRING 3 bytes 666f6f
4042 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4046 schema = tuple((klass.__name__, klass()) for klass in (
4071 """``ANY`` special type
4073 >>> Any(Integer(-123))
4075 >>> a = Any(OctetString(b"hello world").encode())
4076 ANY 040b68656c6c6f20776f726c64
4077 >>> hexenc(bytes(a))
4078 b'0x040x0bhello world'
4080 __slots__ = ("defined",)
4081 tag_default = tag_encode(0)
4082 asn1_type_name = "ANY"
4092 :param value: set the value. Either any kind of pyderasn's
4093 **ready** object, or bytes. Pay attention that
4094 **no** validation is performed is raw binary value
4096 :param bytes expl: override default tag with ``EXPLICIT`` one
4097 :param bool optional: is object ``OPTIONAL`` in sequence
4099 super(Any, self).__init__(None, expl, None, optional, _decoded)
4100 self._value = None if value is None else self._value_sanitize(value)
4103 def _value_sanitize(self, value):
4104 if isinstance(value, self.__class__):
4106 if isinstance(value, Obj):
4107 return value.encode()
4108 if isinstance(value, binary_type):
4110 raise InvalidValueType((self.__class__, Obj, binary_type))
4114 return self._value is not None
4118 if self.expl_lenindef or self.lenindef:
4120 if self.defined is None:
4122 return self.defined[1].bered
4125 obj = self.__class__()
4126 obj._value = self._value
4128 obj._expl = self._expl
4129 obj.optional = self.optional
4130 obj.offset = self.offset
4131 obj.llen = self.llen
4132 obj.vlen = self.vlen
4135 def __eq__(self, their):
4136 if isinstance(their, binary_type):
4137 return self._value == their
4138 if issubclass(their.__class__, Any):
4139 return self._value == their._value
4148 return self.__class__(
4150 expl=self._expl if expl is None else expl,
4151 optional=self.optional if optional is None else optional,
4154 def __bytes__(self):
4155 self._assert_ready()
4163 self._assert_ready()
4166 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4168 t, tlen, lv = tag_strip(tlv)
4169 except DecodeError as err:
4170 raise err.__class__(
4172 klass=self.__class__,
4173 decode_path=decode_path,
4177 l, llen, v = len_decode(lv)
4178 except LenIndefForm as err:
4179 if not ctx.get("bered", False):
4180 raise err.__class__(
4182 klass=self.__class__,
4183 decode_path=decode_path,
4186 llen, vlen, v = 1, 0, lv[1:]
4187 sub_offset = offset + tlen + llen
4189 while v[:EOC_LEN].tobytes() != EOC:
4190 chunk, v = Any().decode(
4193 decode_path=decode_path + (str(chunk_i),),
4197 vlen += chunk.tlvlen
4198 sub_offset += chunk.tlvlen
4200 tlvlen = tlen + llen + vlen + EOC_LEN
4201 obj = self.__class__(
4202 value=tlv[:tlvlen].tobytes(),
4204 optional=self.optional,
4205 _decoded=(offset, 0, tlvlen),
4209 return obj, v[EOC_LEN:]
4210 except DecodeError as err:
4211 raise err.__class__(
4213 klass=self.__class__,
4214 decode_path=decode_path,
4218 raise NotEnoughData(
4219 "encoded length is longer than data",
4220 klass=self.__class__,
4221 decode_path=decode_path,
4224 tlvlen = tlen + llen + l
4225 v, tail = tlv[:tlvlen], v[l:]
4226 obj = self.__class__(
4229 optional=self.optional,
4230 _decoded=(offset, 0, tlvlen),
4236 return pp_console_row(next(self.pps()))
4238 def pps(self, decode_path=()):
4240 asn1_type_name=self.asn1_type_name,
4241 obj_name=self.__class__.__name__,
4242 decode_path=decode_path,
4243 blob=self._value if self.ready else None,
4244 optional=self.optional,
4245 default=self == self.default,
4246 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4247 expl=None if self._expl is None else tag_decode(self._expl),
4252 expl_offset=self.expl_offset if self.expled else None,
4253 expl_tlen=self.expl_tlen if self.expled else None,
4254 expl_llen=self.expl_llen if self.expled else None,
4255 expl_vlen=self.expl_vlen if self.expled else None,
4256 expl_lenindef=self.expl_lenindef,
4257 lenindef=self.lenindef,
4260 defined_by, defined = self.defined or (None, None)
4261 if defined_by is not None:
4263 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4265 for pp in self.pps_lenindef(decode_path):
4269 ########################################################################
4270 # ASN.1 constructed types
4271 ########################################################################
4273 def get_def_by_path(defines_by_path, sub_decode_path):
4274 """Get define by decode path
4276 for path, define in defines_by_path:
4277 if len(path) != len(sub_decode_path):
4279 for p1, p2 in zip(path, sub_decode_path):
4280 if (p1 != any) and (p1 != p2):
4286 def abs_decode_path(decode_path, rel_path):
4287 """Create an absolute decode path from current and relative ones
4289 :param decode_path: current decode path, starting point.
4291 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4292 If first tuple's element is "/", then treat it as
4293 an absolute path, ignoring ``decode_path`` as
4294 starting point. Also this tuple can contain ".."
4295 elements, stripping the leading element from
4298 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4299 ("foo", "bar", "baz", "whatever")
4300 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4302 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4305 if rel_path[0] == "/":
4307 if rel_path[0] == "..":
4308 return abs_decode_path(decode_path[:-1], rel_path[1:])
4309 return decode_path + rel_path
4312 class Sequence(Obj):
4313 """``SEQUENCE`` structure type
4315 You have to make specification of sequence::
4317 class Extension(Sequence):
4319 ("extnID", ObjectIdentifier()),
4320 ("critical", Boolean(default=False)),
4321 ("extnValue", OctetString()),
4324 Then, you can work with it as with dictionary.
4326 >>> ext = Extension()
4327 >>> Extension().specs
4329 ('extnID', OBJECT IDENTIFIER),
4330 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4331 ('extnValue', OCTET STRING),
4333 >>> ext["extnID"] = "1.2.3"
4334 Traceback (most recent call last):
4335 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4336 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4338 You can determine if sequence is ready to be encoded:
4343 Traceback (most recent call last):
4344 pyderasn.ObjNotReady: object is not ready: extnValue
4345 >>> ext["extnValue"] = OctetString(b"foobar")
4349 Value you want to assign, must have the same **type** as in
4350 corresponding specification, but it can have different tags,
4351 optional/default attributes -- they will be taken from specification
4354 class TBSCertificate(Sequence):
4356 ("version", Version(expl=tag_ctxc(0), default="v1")),
4359 >>> tbs = TBSCertificate()
4360 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4362 Assign ``None`` to remove value from sequence.
4364 You can set values in Sequence during its initialization:
4366 >>> AlgorithmIdentifier((
4367 ("algorithm", ObjectIdentifier("1.2.3")),
4368 ("parameters", Any(Null()))
4370 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4372 You can determine if value exists/set in the sequence and take its value:
4374 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4377 OBJECT IDENTIFIER 1.2.3
4379 But pay attention that if value has default, then it won't be (not
4380 in) in the sequence (because ``DEFAULT`` must not be encoded in
4381 DER), but you can read its value:
4383 >>> "critical" in ext, ext["critical"]
4384 (False, BOOLEAN False)
4385 >>> ext["critical"] = Boolean(True)
4386 >>> "critical" in ext, ext["critical"]
4387 (True, BOOLEAN True)
4389 All defaulted values are always optional.
4391 .. _allow_default_values_ctx:
4393 DER prohibits default value encoding and will raise an error if
4394 default value is unexpectedly met during decode.
4395 If :ref:`bered <bered_ctx>` context option is set, then no error
4396 will be raised, but ``bered`` attribute set. You can disable strict
4397 defaulted values existence validation by setting
4398 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4400 Two sequences are equal if they have equal specification (schema),
4401 implicit/explicit tagging and the same values.
4403 __slots__ = ("specs",)
4404 tag_default = tag_encode(form=TagFormConstructed, num=16)
4405 asn1_type_name = "SEQUENCE"
4417 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4419 schema = getattr(self, "schema", ())
4421 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4424 if value is not None:
4425 if issubclass(value.__class__, Sequence):
4426 self._value = value._value
4427 elif hasattr(value, "__iter__"):
4428 for seq_key, seq_value in value:
4429 self[seq_key] = seq_value
4431 raise InvalidValueType((Sequence,))
4432 if default is not None:
4433 if not issubclass(default.__class__, Sequence):
4434 raise InvalidValueType((Sequence,))
4435 default_value = default._value
4436 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4437 default_obj.specs = self.specs
4438 default_obj._value = default_value
4439 self.default = default_obj
4441 self._value = default_obj.copy()._value
4445 for name, spec in self.specs.items():
4446 value = self._value.get(name)
4458 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4460 return any(value.bered for value in self._value.values())
4463 obj = self.__class__(schema=self.specs)
4465 obj._expl = self._expl
4466 obj.default = self.default
4467 obj.optional = self.optional
4468 obj.offset = self.offset
4469 obj.llen = self.llen
4470 obj.vlen = self.vlen
4471 obj._value = {k: v.copy() for k, v in self._value.items()}
4474 def __eq__(self, their):
4475 if not isinstance(their, self.__class__):
4478 self.specs == their.specs and
4479 self.tag == their.tag and
4480 self._expl == their._expl and
4481 self._value == their._value
4492 return self.__class__(
4495 impl=self.tag if impl is None else impl,
4496 expl=self._expl if expl is None else expl,
4497 default=self.default if default is None else default,
4498 optional=self.optional if optional is None else optional,
4501 def __contains__(self, key):
4502 return key in self._value
4504 def __setitem__(self, key, value):
4505 spec = self.specs.get(key)
4507 raise ObjUnknown(key)
4509 self._value.pop(key, None)
4511 if not isinstance(value, spec.__class__):
4512 raise InvalidValueType((spec.__class__,))
4513 value = spec(value=value)
4514 if spec.default is not None and value == spec.default:
4515 self._value.pop(key, None)
4517 self._value[key] = value
4519 def __getitem__(self, key):
4520 value = self._value.get(key)
4521 if value is not None:
4523 spec = self.specs.get(key)
4525 raise ObjUnknown(key)
4526 if spec.default is not None:
4530 def _encoded_values(self):
4532 for name, spec in self.specs.items():
4533 value = self._value.get(name)
4537 raise ObjNotReady(name)
4538 raws.append(value.encode())
4542 v = b"".join(self._encoded_values())
4543 return b"".join((self.tag, len_encode(len(v)), v))
4545 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4547 t, tlen, lv = tag_strip(tlv)
4548 except DecodeError as err:
4549 raise err.__class__(
4551 klass=self.__class__,
4552 decode_path=decode_path,
4557 klass=self.__class__,
4558 decode_path=decode_path,
4561 if tag_only: # pragma: no cover
4564 ctx_bered = ctx.get("bered", False)
4566 l, llen, v = len_decode(lv)
4567 except LenIndefForm as err:
4569 raise err.__class__(
4571 klass=self.__class__,
4572 decode_path=decode_path,
4575 l, llen, v = 0, 1, lv[1:]
4577 except DecodeError as err:
4578 raise err.__class__(
4580 klass=self.__class__,
4581 decode_path=decode_path,
4585 raise NotEnoughData(
4586 "encoded length is longer than data",
4587 klass=self.__class__,
4588 decode_path=decode_path,
4592 v, tail = v[:l], v[l:]
4594 sub_offset = offset + tlen + llen
4597 ctx_allow_default_values = ctx.get("allow_default_values", False)
4598 for name, spec in self.specs.items():
4599 if spec.optional and (
4600 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4604 sub_decode_path = decode_path + (name,)
4606 value, v_tail = spec.decode(
4610 decode_path=sub_decode_path,
4618 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4619 if defined is not None:
4620 defined_by, defined_spec = defined
4621 if issubclass(value.__class__, SequenceOf):
4622 for i, _value in enumerate(value):
4623 sub_sub_decode_path = sub_decode_path + (
4625 DecodePathDefBy(defined_by),
4627 defined_value, defined_tail = defined_spec.decode(
4628 memoryview(bytes(_value)),
4630 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4631 if value.expled else (value.tlen + value.llen)
4634 decode_path=sub_sub_decode_path,
4637 if len(defined_tail) > 0:
4640 klass=self.__class__,
4641 decode_path=sub_sub_decode_path,
4644 _value.defined = (defined_by, defined_value)
4646 defined_value, defined_tail = defined_spec.decode(
4647 memoryview(bytes(value)),
4649 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4650 if value.expled else (value.tlen + value.llen)
4653 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4656 if len(defined_tail) > 0:
4659 klass=self.__class__,
4660 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4663 value.defined = (defined_by, defined_value)
4665 value_len = value.fulllen
4667 sub_offset += value_len
4669 if spec.default is not None and value == spec.default:
4670 if ctx_bered or ctx_allow_default_values:
4674 "DEFAULT value met",
4675 klass=self.__class__,
4676 decode_path=sub_decode_path,
4679 values[name] = value
4681 spec_defines = getattr(spec, "defines", ())
4682 if len(spec_defines) == 0:
4683 defines_by_path = ctx.get("defines_by_path", ())
4684 if len(defines_by_path) > 0:
4685 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4686 if spec_defines is not None and len(spec_defines) > 0:
4687 for rel_path, schema in spec_defines:
4688 defined = schema.get(value, None)
4689 if defined is not None:
4690 ctx.setdefault("_defines", []).append((
4691 abs_decode_path(sub_decode_path[:-1], rel_path),
4695 if v[:EOC_LEN].tobytes() != EOC:
4698 klass=self.__class__,
4699 decode_path=decode_path,
4707 klass=self.__class__,
4708 decode_path=decode_path,
4711 obj = self.__class__(
4715 default=self.default,
4716 optional=self.optional,
4717 _decoded=(offset, llen, vlen),
4720 obj.lenindef = lenindef
4721 obj.ber_encoded = ber_encoded
4725 value = pp_console_row(next(self.pps()))
4727 for name in self.specs:
4728 _value = self._value.get(name)
4731 cols.append("%s: %s" % (name, repr(_value)))
4732 return "%s[%s]" % (value, "; ".join(cols))
4734 def pps(self, decode_path=()):
4736 asn1_type_name=self.asn1_type_name,
4737 obj_name=self.__class__.__name__,
4738 decode_path=decode_path,
4739 optional=self.optional,
4740 default=self == self.default,
4741 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4742 expl=None if self._expl is None else tag_decode(self._expl),
4747 expl_offset=self.expl_offset if self.expled else None,
4748 expl_tlen=self.expl_tlen if self.expled else None,
4749 expl_llen=self.expl_llen if self.expled else None,
4750 expl_vlen=self.expl_vlen if self.expled else None,
4751 expl_lenindef=self.expl_lenindef,
4752 lenindef=self.lenindef,
4753 ber_encoded=self.ber_encoded,
4756 for name in self.specs:
4757 value = self._value.get(name)
4760 yield value.pps(decode_path=decode_path + (name,))
4761 for pp in self.pps_lenindef(decode_path):
4765 class Set(Sequence):
4766 """``SET`` structure type
4768 Its usage is identical to :py:class:`pyderasn.Sequence`.
4770 .. _allow_unordered_set_ctx:
4772 DER prohibits unordered values encoding and will raise an error
4773 during decode. If If :ref:`bered <bered_ctx>` context option is set,
4774 then no error will occure. Also you can disable strict values
4775 ordering check by setting ``"allow_unordered_set": True``
4776 :ref:`context <ctx>` option.
4779 tag_default = tag_encode(form=TagFormConstructed, num=17)
4780 asn1_type_name = "SET"
4783 raws = self._encoded_values()
4786 return b"".join((self.tag, len_encode(len(v)), v))
4788 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4790 t, tlen, lv = tag_strip(tlv)
4791 except DecodeError as err:
4792 raise err.__class__(
4794 klass=self.__class__,
4795 decode_path=decode_path,
4800 klass=self.__class__,
4801 decode_path=decode_path,
4807 ctx_bered = ctx.get("bered", False)
4809 l, llen, v = len_decode(lv)
4810 except LenIndefForm as err:
4812 raise err.__class__(
4814 klass=self.__class__,
4815 decode_path=decode_path,
4818 l, llen, v = 0, 1, lv[1:]
4820 except DecodeError as err:
4821 raise err.__class__(
4823 klass=self.__class__,
4824 decode_path=decode_path,
4828 raise NotEnoughData(
4829 "encoded length is longer than data",
4830 klass=self.__class__,
4834 v, tail = v[:l], v[l:]
4836 sub_offset = offset + tlen + llen
4839 ctx_allow_default_values = ctx.get("allow_default_values", False)
4840 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4841 value_prev = memoryview(v[:0])
4842 specs_items = self.specs.items
4844 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4846 for name, spec in specs_items():
4847 sub_decode_path = decode_path + (name,)
4853 decode_path=sub_decode_path,
4862 klass=self.__class__,
4863 decode_path=decode_path,
4866 value, v_tail = spec.decode(
4870 decode_path=sub_decode_path,
4873 value_len = value.fulllen
4874 if value_prev.tobytes() > v[:value_len].tobytes():
4875 if ctx_bered or ctx_allow_unordered_set:
4879 "unordered " + self.asn1_type_name,
4880 klass=self.__class__,
4881 decode_path=sub_decode_path,
4884 if spec.default is None or value != spec.default:
4886 elif ctx_bered or ctx_allow_default_values:
4890 "DEFAULT value met",
4891 klass=self.__class__,
4892 decode_path=sub_decode_path,
4895 values[name] = value
4896 value_prev = v[:value_len]
4897 sub_offset += value_len
4900 obj = self.__class__(
4904 default=self.default,
4905 optional=self.optional,
4906 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4909 if v[:EOC_LEN].tobytes() != EOC:
4912 klass=self.__class__,
4913 decode_path=decode_path,
4921 "not all values are ready",
4922 klass=self.__class__,
4923 decode_path=decode_path,
4926 obj.ber_encoded = ber_encoded
4930 class SequenceOf(Obj):
4931 """``SEQUENCE OF`` sequence type
4933 For that kind of type you must specify the object it will carry on
4934 (bounds are for example here, not required)::
4936 class Ints(SequenceOf):
4941 >>> ints.append(Integer(123))
4942 >>> ints.append(Integer(234))
4944 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4945 >>> [int(i) for i in ints]
4947 >>> ints.append(Integer(345))
4948 Traceback (most recent call last):
4949 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4952 >>> ints[1] = Integer(345)
4954 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4956 Also you can initialize sequence with preinitialized values:
4958 >>> ints = Ints([Integer(123), Integer(234)])
4960 __slots__ = ("spec", "_bound_min", "_bound_max")
4961 tag_default = tag_encode(form=TagFormConstructed, num=16)
4962 asn1_type_name = "SEQUENCE OF"
4975 super(SequenceOf, self).__init__(
4983 schema = getattr(self, "schema", None)
4985 raise ValueError("schema must be specified")
4987 self._bound_min, self._bound_max = getattr(
4991 ) if bounds is None else bounds
4993 if value is not None:
4994 self._value = self._value_sanitize(value)
4995 if default is not None:
4996 default_value = self._value_sanitize(default)
4997 default_obj = self.__class__(
5002 default_obj._value = default_value
5003 self.default = default_obj
5005 self._value = default_obj.copy()._value
5007 def _value_sanitize(self, value):
5008 if issubclass(value.__class__, SequenceOf):
5009 value = value._value
5010 elif hasattr(value, "__iter__"):
5013 raise InvalidValueType((self.__class__, iter))
5014 if not self._bound_min <= len(value) <= self._bound_max:
5015 raise BoundsError(self._bound_min, len(value), self._bound_max)
5017 if not isinstance(v, self.spec.__class__):
5018 raise InvalidValueType((self.spec.__class__,))
5023 return all(v.ready for v in self._value)
5027 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5029 return any(v.bered for v in self._value)
5032 obj = self.__class__(schema=self.spec)
5033 obj._bound_min = self._bound_min
5034 obj._bound_max = self._bound_max
5036 obj._expl = self._expl
5037 obj.default = self.default
5038 obj.optional = self.optional
5039 obj.offset = self.offset
5040 obj.llen = self.llen
5041 obj.vlen = self.vlen
5042 obj._value = [v.copy() for v in self._value]
5045 def __eq__(self, their):
5046 if isinstance(their, self.__class__):
5048 self.spec == their.spec and
5049 self.tag == their.tag and
5050 self._expl == their._expl and
5051 self._value == their._value
5053 if hasattr(their, "__iter__"):
5054 return self._value == list(their)
5066 return self.__class__(
5070 (self._bound_min, self._bound_max)
5071 if bounds is None else bounds
5073 impl=self.tag if impl is None else impl,
5074 expl=self._expl if expl is None else expl,
5075 default=self.default if default is None else default,
5076 optional=self.optional if optional is None else optional,
5079 def __contains__(self, key):
5080 return key in self._value
5082 def append(self, value):
5083 if not isinstance(value, self.spec.__class__):
5084 raise InvalidValueType((self.spec.__class__,))
5085 if len(self._value) + 1 > self._bound_max:
5088 len(self._value) + 1,
5091 self._value.append(value)
5094 self._assert_ready()
5095 return iter(self._value)
5098 self._assert_ready()
5099 return len(self._value)
5101 def __setitem__(self, key, value):
5102 if not isinstance(value, self.spec.__class__):
5103 raise InvalidValueType((self.spec.__class__,))
5104 self._value[key] = self.spec(value=value)
5106 def __getitem__(self, key):
5107 return self._value[key]
5109 def _encoded_values(self):
5110 return [v.encode() for v in self._value]
5113 v = b"".join(self._encoded_values())
5114 return b"".join((self.tag, len_encode(len(v)), v))
5116 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5118 t, tlen, lv = tag_strip(tlv)
5119 except DecodeError as err:
5120 raise err.__class__(
5122 klass=self.__class__,
5123 decode_path=decode_path,
5128 klass=self.__class__,
5129 decode_path=decode_path,
5135 ctx_bered = ctx.get("bered", False)
5137 l, llen, v = len_decode(lv)
5138 except LenIndefForm as err:
5140 raise err.__class__(
5142 klass=self.__class__,
5143 decode_path=decode_path,
5146 l, llen, v = 0, 1, lv[1:]
5148 except DecodeError as err:
5149 raise err.__class__(
5151 klass=self.__class__,
5152 decode_path=decode_path,
5156 raise NotEnoughData(
5157 "encoded length is longer than data",
5158 klass=self.__class__,
5159 decode_path=decode_path,
5163 v, tail = v[:l], v[l:]
5165 sub_offset = offset + tlen + llen
5167 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5168 value_prev = memoryview(v[:0])
5172 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5174 sub_decode_path = decode_path + (str(len(_value)),)
5175 value, v_tail = spec.decode(
5179 decode_path=sub_decode_path,
5182 value_len = value.fulllen
5184 if value_prev.tobytes() > v[:value_len].tobytes():
5185 if ctx_bered or ctx_allow_unordered_set:
5189 "unordered " + self.asn1_type_name,
5190 klass=self.__class__,
5191 decode_path=sub_decode_path,
5194 value_prev = v[:value_len]
5195 _value.append(value)
5196 sub_offset += value_len
5200 obj = self.__class__(
5203 bounds=(self._bound_min, self._bound_max),
5206 default=self.default,
5207 optional=self.optional,
5208 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5210 except BoundsError as err:
5213 klass=self.__class__,
5214 decode_path=decode_path,
5218 if v[:EOC_LEN].tobytes() != EOC:
5221 klass=self.__class__,
5222 decode_path=decode_path,
5227 obj.ber_encoded = ber_encoded
5232 pp_console_row(next(self.pps())),
5233 ", ".join(repr(v) for v in self._value),
5236 def pps(self, decode_path=()):
5238 asn1_type_name=self.asn1_type_name,
5239 obj_name=self.__class__.__name__,
5240 decode_path=decode_path,
5241 optional=self.optional,
5242 default=self == self.default,
5243 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5244 expl=None if self._expl is None else tag_decode(self._expl),
5249 expl_offset=self.expl_offset if self.expled else None,
5250 expl_tlen=self.expl_tlen if self.expled else None,
5251 expl_llen=self.expl_llen if self.expled else None,
5252 expl_vlen=self.expl_vlen if self.expled else None,
5253 expl_lenindef=self.expl_lenindef,
5254 lenindef=self.lenindef,
5255 ber_encoded=self.ber_encoded,
5258 for i, value in enumerate(self._value):
5259 yield value.pps(decode_path=decode_path + (str(i),))
5260 for pp in self.pps_lenindef(decode_path):
5264 class SetOf(SequenceOf):
5265 """``SET OF`` sequence type
5267 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5270 tag_default = tag_encode(form=TagFormConstructed, num=17)
5271 asn1_type_name = "SET OF"
5274 raws = self._encoded_values()
5277 return b"".join((self.tag, len_encode(len(v)), v))
5279 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5280 return super(SetOf, self)._decode(
5286 ordering_check=True,
5290 def obj_by_path(pypath): # pragma: no cover
5291 """Import object specified as string Python path
5293 Modules must be separated from classes/functions with ``:``.
5295 >>> obj_by_path("foo.bar:Baz")
5296 <class 'foo.bar.Baz'>
5297 >>> obj_by_path("foo.bar:Baz.boo")
5298 <classmethod 'foo.bar.Baz.boo'>
5300 mod, objs = pypath.rsplit(":", 1)
5301 from importlib import import_module
5302 obj = import_module(mod)
5303 for obj_name in objs.split("."):
5304 obj = getattr(obj, obj_name)
5308 def generic_decoder(): # pragma: no cover
5309 # All of this below is a big hack with self references
5310 choice = PrimitiveTypes()
5311 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5312 choice.specs["SetOf"] = SetOf(schema=choice)
5314 choice.specs["SequenceOf%d" % i] = SequenceOf(
5318 choice.specs["Any"] = Any()
5320 # Class name equals to type name, to omit it from output
5321 class SEQUENCEOF(SequenceOf):
5329 with_decode_path=False,
5330 decode_path_only=(),
5332 def _pprint_pps(pps):
5334 if hasattr(pp, "_fields"):
5336 decode_path_only != () and
5337 pp.decode_path[:len(decode_path_only)] != decode_path_only
5340 if pp.asn1_type_name == Choice.asn1_type_name:
5342 pp_kwargs = pp._asdict()
5343 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5344 pp = _pp(**pp_kwargs)
5345 yield pp_console_row(
5350 with_colours=with_colours,
5351 with_decode_path=with_decode_path,
5352 decode_path_len_decrease=len(decode_path_only),
5354 for row in pp_console_blob(
5356 decode_path_len_decrease=len(decode_path_only),
5360 for row in _pprint_pps(pp):
5362 return "\n".join(_pprint_pps(obj.pps()))
5363 return SEQUENCEOF(), pprint_any
5366 def main(): # pragma: no cover
5368 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5369 parser.add_argument(
5373 help="Skip that number of bytes from the beginning",
5375 parser.add_argument(
5377 help="Python path to dictionary with OIDs",
5379 parser.add_argument(
5381 help="Python path to schema definition to use",
5383 parser.add_argument(
5384 "--defines-by-path",
5385 help="Python path to decoder's defines_by_path",
5387 parser.add_argument(
5389 action="store_true",
5390 help="Disallow BER encoding",
5392 parser.add_argument(
5393 "--print-decode-path",
5394 action="store_true",
5395 help="Print decode paths",
5397 parser.add_argument(
5398 "--decode-path-only",
5399 help="Print only specified decode path",
5401 parser.add_argument(
5403 action="store_true",
5404 help="Allow explicit tag out-of-bound",
5406 parser.add_argument(
5408 type=argparse.FileType("rb"),
5409 help="Path to DER file you want to decode",
5411 args = parser.parse_args()
5412 args.DERFile.seek(args.skip)
5413 der = memoryview(args.DERFile.read())
5414 args.DERFile.close()
5415 oids = obj_by_path(args.oids) if args.oids else {}
5417 schema = obj_by_path(args.schema)
5418 from functools import partial
5419 pprinter = partial(pprint, big_blobs=True)
5421 schema, pprinter = generic_decoder()
5423 "bered": not args.nobered,
5424 "allow_expl_oob": args.allow_expl_oob,
5426 if args.defines_by_path is not None:
5427 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5428 obj, tail = schema().decode(der, ctx=ctx)
5432 with_colours=True if environ.get("NO_COLOR") is None else False,
5433 with_decode_path=args.print_decode_path,
5435 () if args.decode_path_only is None else
5436 tuple(args.decode_path_only.split(":"))
5440 print("\nTrailing data: %s" % hexenc(tail))
5443 if __name__ == "__main__":