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 ascii_letters
546 from string import digits
548 from six import add_metaclass
549 from six import binary_type
550 from six import byte2int
551 from six import indexbytes
552 from six import int2byte
553 from six import integer_types
554 from six import iterbytes
556 from six import string_types
557 from six import text_type
558 from six.moves import xrange as six_xrange
562 from termcolor import colored
563 except ImportError: # pragma: no cover
564 def colored(what, *args):
608 "TagClassApplication",
612 "TagFormConstructed",
623 TagClassUniversal = 0
624 TagClassApplication = 1 << 6
625 TagClassContext = 1 << 7
626 TagClassPrivate = 1 << 6 | 1 << 7
628 TagFormConstructed = 1 << 5
631 TagClassApplication: "APPLICATION ",
632 TagClassPrivate: "PRIVATE ",
633 TagClassUniversal: "UNIV ",
637 LENINDEF = b"\x80" # length indefinite mark
638 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
641 ########################################################################
643 ########################################################################
645 class DecodeError(Exception):
646 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
648 :param str msg: reason of decode failing
649 :param klass: optional exact DecodeError inherited class (like
650 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
651 :py:exc:`InvalidLength`)
652 :param decode_path: tuple of strings. It contains human
653 readable names of the fields through which
654 decoding process has passed
655 :param int offset: binary offset where failure happened
657 super(DecodeError, self).__init__()
660 self.decode_path = decode_path
666 "" if self.klass is None else self.klass.__name__,
668 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
669 if len(self.decode_path) > 0 else ""
671 ("(at %d)" % self.offset) if self.offset > 0 else "",
677 return "%s(%s)" % (self.__class__.__name__, self)
680 class NotEnoughData(DecodeError):
684 class LenIndefForm(DecodeError):
688 class TagMismatch(DecodeError):
692 class InvalidLength(DecodeError):
696 class InvalidOID(DecodeError):
700 class ObjUnknown(ValueError):
701 def __init__(self, name):
702 super(ObjUnknown, self).__init__()
706 return "object is unknown: %s" % self.name
709 return "%s(%s)" % (self.__class__.__name__, self)
712 class ObjNotReady(ValueError):
713 def __init__(self, name):
714 super(ObjNotReady, self).__init__()
718 return "object is not ready: %s" % self.name
721 return "%s(%s)" % (self.__class__.__name__, self)
724 class InvalidValueType(ValueError):
725 def __init__(self, expected_types):
726 super(InvalidValueType, self).__init__()
727 self.expected_types = expected_types
730 return "invalid value type, expected: %s" % ", ".join(
731 [repr(t) for t in self.expected_types]
735 return "%s(%s)" % (self.__class__.__name__, self)
738 class BoundsError(ValueError):
739 def __init__(self, bound_min, value, bound_max):
740 super(BoundsError, self).__init__()
741 self.bound_min = bound_min
743 self.bound_max = bound_max
746 return "unsatisfied bounds: %s <= %s <= %s" % (
753 return "%s(%s)" % (self.__class__.__name__, self)
756 ########################################################################
758 ########################################################################
760 _hexdecoder = getdecoder("hex")
761 _hexencoder = getencoder("hex")
765 """Binary data to hexadecimal string convert
767 return _hexdecoder(data)[0]
771 """Hexadecimal string to binary data convert
773 return _hexencoder(data)[0].decode("ascii")
776 def int_bytes_len(num, byte_len=8):
779 return int(ceil(float(num.bit_length()) / byte_len))
782 def zero_ended_encode(num):
783 octets = bytearray(int_bytes_len(num, 7))
785 octets[i] = num & 0x7F
789 octets[i] = 0x80 | (num & 0x7F)
795 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
796 """Encode tag to binary form
798 :param int num: tag's number
799 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
800 :py:data:`pyderasn.TagClassContext`,
801 :py:data:`pyderasn.TagClassApplication`,
802 :py:data:`pyderasn.TagClassPrivate`)
803 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
804 :py:data:`pyderasn.TagFormConstructed`)
808 return int2byte(klass | form | num)
809 # [XX|X|11111][1.......][1.......] ... [0.......]
810 return int2byte(klass | form | 31) + zero_ended_encode(num)
814 """Decode tag from binary form
818 No validation is performed, assuming that it has already passed.
820 It returns tuple with three integers, as
821 :py:func:`pyderasn.tag_encode` accepts.
823 first_octet = byte2int(tag)
824 klass = first_octet & 0xC0
825 form = first_octet & 0x20
826 if first_octet & 0x1F < 0x1F:
827 return (klass, form, first_octet & 0x1F)
829 for octet in iterbytes(tag[1:]):
832 return (klass, form, num)
836 """Create CONTEXT PRIMITIVE tag
838 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
842 """Create CONTEXT CONSTRUCTED tag
844 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
848 """Take off tag from the data
850 :returns: (encoded tag, tag length, remaining data)
853 raise NotEnoughData("no data at all")
854 if byte2int(data) & 0x1F < 31:
855 return data[:1], 1, data[1:]
860 raise DecodeError("unfinished tag")
861 if indexbytes(data, i) & 0x80 == 0:
864 return data[:i], i, data[i:]
870 octets = bytearray(int_bytes_len(l) + 1)
871 octets[0] = 0x80 | (len(octets) - 1)
872 for i in six_xrange(len(octets) - 1, 0, -1):
878 def len_decode(data):
881 :returns: (decoded length, length's length, remaining data)
882 :raises LenIndefForm: if indefinite form encoding is met
885 raise NotEnoughData("no data at all")
886 first_octet = byte2int(data)
887 if first_octet & 0x80 == 0:
888 return first_octet, 1, data[1:]
889 octets_num = first_octet & 0x7F
890 if octets_num + 1 > len(data):
891 raise NotEnoughData("encoded length is longer than data")
894 if byte2int(data[1:]) == 0:
895 raise DecodeError("leading zeros")
897 for v in iterbytes(data[1:1 + octets_num]):
900 raise DecodeError("long form instead of short one")
901 return l, 1 + octets_num, data[1 + octets_num:]
904 ########################################################################
906 ########################################################################
908 class AutoAddSlots(type):
909 def __new__(mcs, name, bases, _dict):
910 _dict["__slots__"] = _dict.get("__slots__", ())
911 return type.__new__(mcs, name, bases, _dict)
914 @add_metaclass(AutoAddSlots)
916 """Common ASN.1 object class
918 All ASN.1 types are inherited from it. It has metaclass that
919 automatically adds ``__slots__`` to all inherited classes.
943 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
944 self._expl = getattr(self, "expl", None) if expl is None else expl
945 if self.tag != self.tag_default and self._expl is not None:
946 raise ValueError("implicit and explicit tags can not be set simultaneously")
947 if default is not None:
949 self.optional = optional
950 self.offset, self.llen, self.vlen = _decoded
952 self.expl_lenindef = False
953 self.lenindef = False
954 self.ber_encoded = False
957 def ready(self): # pragma: no cover
958 """Is object ready to be encoded?
960 raise NotImplementedError()
962 def _assert_ready(self):
964 raise ObjNotReady(self.__class__.__name__)
968 """Is either object or any elements inside is BER encoded?
970 return self.expl_lenindef or self.lenindef or self.ber_encoded
974 """Is object decoded?
976 return (self.llen + self.vlen) > 0
978 def copy(self): # pragma: no cover
979 """Make a copy of object, safe to be mutated
981 raise NotImplementedError()
989 return self.tlen + self.llen + self.vlen
991 def __str__(self): # pragma: no cover
992 return self.__bytes__() if PY2 else self.__unicode__()
994 def __ne__(self, their):
995 return not(self == their)
997 def __gt__(self, their): # pragma: no cover
998 return not(self < their)
1000 def __le__(self, their): # pragma: no cover
1001 return (self == their) or (self < their)
1003 def __ge__(self, their): # pragma: no cover
1004 return (self == their) or (self > their)
1006 def _encode(self): # pragma: no cover
1007 raise NotImplementedError()
1009 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1010 raise NotImplementedError()
1013 raw = self._encode()
1014 if self._expl is None:
1016 return b"".join((self._expl, len_encode(len(raw)), raw))
1029 :param data: either binary or memoryview
1030 :param int offset: initial data's offset
1031 :param bool leavemm: do we need to leave memoryview of remaining
1032 data as is, or convert it to bytes otherwise
1033 :param ctx: optional :ref:`context <ctx>` governing decoding process.
1034 :param tag_only: decode only the tag, without length and contents
1035 (used only in Choice and Set structures, trying to
1036 determine if tag satisfies the scheme)
1037 :returns: (Obj, remaining data)
1041 tlv = memoryview(data)
1042 if self._expl is None:
1043 result = self._decode(
1046 decode_path=decode_path,
1055 t, tlen, lv = tag_strip(tlv)
1056 except DecodeError as err:
1057 raise err.__class__(
1059 klass=self.__class__,
1060 decode_path=decode_path,
1065 klass=self.__class__,
1066 decode_path=decode_path,
1070 l, llen, v = len_decode(lv)
1071 except LenIndefForm as err:
1072 if not ctx.get("bered", False):
1073 raise err.__class__(
1075 klass=self.__class__,
1076 decode_path=decode_path,
1080 offset += tlen + llen
1081 result = self._decode(
1084 decode_path=decode_path,
1088 if tag_only: # pragma: no cover
1091 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1092 if eoc_expected.tobytes() != EOC:
1095 klass=self.__class__,
1096 decode_path=decode_path,
1100 obj.expl_lenindef = True
1101 except DecodeError as err:
1102 raise err.__class__(
1104 klass=self.__class__,
1105 decode_path=decode_path,
1110 raise NotEnoughData(
1111 "encoded length is longer than data",
1112 klass=self.__class__,
1113 decode_path=decode_path,
1116 result = self._decode(
1118 offset=offset + tlen + llen,
1119 decode_path=decode_path,
1123 if tag_only: # pragma: no cover
1126 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1128 "explicit tag out-of-bound, longer than data",
1129 klass=self.__class__,
1130 decode_path=decode_path,
1133 return obj, (tail if leavemm else tail.tobytes())
1137 return self._expl is not None
1144 def expl_tlen(self):
1145 return len(self._expl)
1148 def expl_llen(self):
1149 if self.expl_lenindef:
1151 return len(len_encode(self.tlvlen))
1154 def expl_offset(self):
1155 return self.offset - self.expl_tlen - self.expl_llen
1158 def expl_vlen(self):
1162 def expl_tlvlen(self):
1163 return self.expl_tlen + self.expl_llen + self.expl_vlen
1166 def fulloffset(self):
1167 return self.expl_offset if self.expled else self.offset
1171 return self.expl_tlvlen if self.expled else self.tlvlen
1173 def pps_lenindef(self, decode_path):
1174 if self.lenindef and not (
1175 getattr(self, "defined", None) is not None and
1176 self.defined[1].lenindef
1179 asn1_type_name="EOC",
1181 decode_path=decode_path,
1183 self.offset + self.tlvlen -
1184 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1192 if self.expl_lenindef:
1194 asn1_type_name="EOC",
1195 obj_name="EXPLICIT",
1196 decode_path=decode_path,
1197 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1206 class DecodePathDefBy(object):
1207 """DEFINED BY representation inside decode path
1209 __slots__ = ("defined_by",)
1211 def __init__(self, defined_by):
1212 self.defined_by = defined_by
1214 def __ne__(self, their):
1215 return not(self == their)
1217 def __eq__(self, their):
1218 if not isinstance(their, self.__class__):
1220 return self.defined_by == their.defined_by
1223 return "DEFINED BY " + str(self.defined_by)
1226 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1229 ########################################################################
1231 ########################################################################
1233 PP = namedtuple("PP", (
1259 asn1_type_name="unknown",
1276 expl_lenindef=False,
1306 def _colourize(what, colour, with_colours, attrs=("bold",)):
1307 return colored(what, colour, attrs=attrs) if with_colours else what
1316 with_decode_path=False,
1317 decode_path_len_decrease=0,
1324 " " if pp.expl_offset is None else
1325 ("-%d" % (pp.offset - pp.expl_offset))
1327 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1329 col = _colourize(col, "red", with_colours, ())
1330 col += _colourize("B", "red", with_colours) if pp.bered else " "
1332 col = "[%d,%d,%4d]%s" % (
1336 LENINDEF_PP_CHAR if pp.lenindef else " "
1338 col = _colourize(col, "green", with_colours, ())
1340 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1341 if decode_path_len > 0:
1342 cols.append(" ." * decode_path_len)
1343 ent = pp.decode_path[-1]
1344 if isinstance(ent, DecodePathDefBy):
1345 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1346 value = str(ent.defined_by)
1348 oids is not None and
1349 ent.defined_by.asn1_type_name ==
1350 ObjectIdentifier.asn1_type_name and
1353 cols.append(_colourize("%s:" % oids[value], "green", with_colours))
1355 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1357 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1358 if pp.expl is not None:
1359 klass, _, num = pp.expl
1360 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1361 cols.append(_colourize(col, "blue", with_colours))
1362 if pp.impl is not None:
1363 klass, _, num = pp.impl
1364 col = "[%s%d]" % (TagClassReprs[klass], num)
1365 cols.append(_colourize(col, "blue", with_colours))
1366 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1367 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1369 cols.append(_colourize("BER", "red", with_colours))
1370 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1371 if pp.value is not None:
1373 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1375 oids is not None and
1376 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1379 cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
1381 if isinstance(pp.blob, binary_type):
1382 cols.append(hexenc(pp.blob))
1383 elif isinstance(pp.blob, tuple):
1384 cols.append(", ".join(pp.blob))
1386 cols.append(_colourize("OPTIONAL", "red", with_colours))
1388 cols.append(_colourize("DEFAULT", "red", with_colours))
1389 if with_decode_path:
1390 cols.append(_colourize(
1391 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1395 return " ".join(cols)
1398 def pp_console_blob(pp, decode_path_len_decrease=0):
1399 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1400 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1401 if decode_path_len > 0:
1402 cols.append(" ." * (decode_path_len + 1))
1403 if isinstance(pp.blob, binary_type):
1404 blob = hexenc(pp.blob).upper()
1405 for i in range(0, len(blob), 32):
1406 chunk = blob[i:i + 32]
1407 yield " ".join(cols + [":".join(
1408 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1410 elif isinstance(pp.blob, tuple):
1411 yield " ".join(cols + [", ".join(pp.blob)])
1419 with_decode_path=False,
1420 decode_path_only=(),
1422 """Pretty print object
1424 :param Obj obj: object you want to pretty print
1425 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1426 from it is met, then its humand readable form is printed
1427 :param big_blobs: if large binary objects are met (like OctetString
1428 values), do we need to print them too, on separate
1430 :param with_colours: colourize output, if ``termcolor`` library
1432 :param with_decode_path: print decode path
1433 :param decode_path_only: print only that specified decode path
1435 def _pprint_pps(pps):
1437 if hasattr(pp, "_fields"):
1439 decode_path_only != () and
1441 str(p) for p in pp.decode_path[:len(decode_path_only)]
1442 ) != decode_path_only
1446 yield pp_console_row(
1451 with_colours=with_colours,
1452 with_decode_path=with_decode_path,
1453 decode_path_len_decrease=len(decode_path_only),
1455 for row in pp_console_blob(
1457 decode_path_len_decrease=len(decode_path_only),
1461 yield pp_console_row(
1466 with_colours=with_colours,
1467 with_decode_path=with_decode_path,
1468 decode_path_len_decrease=len(decode_path_only),
1471 for row in _pprint_pps(pp):
1473 return "\n".join(_pprint_pps(obj.pps()))
1476 ########################################################################
1477 # ASN.1 primitive types
1478 ########################################################################
1481 """``BOOLEAN`` boolean type
1483 >>> b = Boolean(True)
1485 >>> b == Boolean(True)
1491 tag_default = tag_encode(1)
1492 asn1_type_name = "BOOLEAN"
1504 :param value: set the value. Either boolean type, or
1505 :py:class:`pyderasn.Boolean` object
1506 :param bytes impl: override default tag with ``IMPLICIT`` one
1507 :param bytes expl: override default tag with ``EXPLICIT`` one
1508 :param default: set default value. Type same as in ``value``
1509 :param bool optional: is object ``OPTIONAL`` in sequence
1511 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1512 self._value = None if value is None else self._value_sanitize(value)
1513 if default is not None:
1514 default = self._value_sanitize(default)
1515 self.default = self.__class__(
1521 self._value = default
1523 def _value_sanitize(self, value):
1524 if issubclass(value.__class__, Boolean):
1526 if isinstance(value, bool):
1528 raise InvalidValueType((self.__class__, bool))
1532 return self._value is not None
1535 obj = self.__class__()
1536 obj._value = self._value
1538 obj._expl = self._expl
1539 obj.default = self.default
1540 obj.optional = self.optional
1541 obj.offset = self.offset
1542 obj.llen = self.llen
1543 obj.vlen = self.vlen
1546 def __nonzero__(self):
1547 self._assert_ready()
1551 self._assert_ready()
1554 def __eq__(self, their):
1555 if isinstance(their, bool):
1556 return self._value == their
1557 if not issubclass(their.__class__, Boolean):
1560 self._value == their._value and
1561 self.tag == their.tag and
1562 self._expl == their._expl
1573 return self.__class__(
1575 impl=self.tag if impl is None else impl,
1576 expl=self._expl if expl is None else expl,
1577 default=self.default if default is None else default,
1578 optional=self.optional if optional is None else optional,
1582 self._assert_ready()
1586 (b"\xFF" if self._value else b"\x00"),
1589 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1591 t, _, lv = tag_strip(tlv)
1592 except DecodeError as err:
1593 raise err.__class__(
1595 klass=self.__class__,
1596 decode_path=decode_path,
1601 klass=self.__class__,
1602 decode_path=decode_path,
1608 l, _, v = len_decode(lv)
1609 except DecodeError as err:
1610 raise err.__class__(
1612 klass=self.__class__,
1613 decode_path=decode_path,
1617 raise InvalidLength(
1618 "Boolean's length must be equal to 1",
1619 klass=self.__class__,
1620 decode_path=decode_path,
1624 raise NotEnoughData(
1625 "encoded length is longer than data",
1626 klass=self.__class__,
1627 decode_path=decode_path,
1630 first_octet = byte2int(v)
1632 if first_octet == 0:
1634 elif first_octet == 0xFF:
1636 elif ctx.get("bered", False):
1641 "unacceptable Boolean value",
1642 klass=self.__class__,
1643 decode_path=decode_path,
1646 obj = self.__class__(
1650 default=self.default,
1651 optional=self.optional,
1652 _decoded=(offset, 1, 1),
1654 obj.ber_encoded = ber_encoded
1658 return pp_console_row(next(self.pps()))
1660 def pps(self, decode_path=()):
1662 asn1_type_name=self.asn1_type_name,
1663 obj_name=self.__class__.__name__,
1664 decode_path=decode_path,
1665 value=str(self._value) if self.ready else None,
1666 optional=self.optional,
1667 default=self == self.default,
1668 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1669 expl=None if self._expl is None else tag_decode(self._expl),
1674 expl_offset=self.expl_offset if self.expled else None,
1675 expl_tlen=self.expl_tlen if self.expled else None,
1676 expl_llen=self.expl_llen if self.expled else None,
1677 expl_vlen=self.expl_vlen if self.expled else None,
1678 expl_lenindef=self.expl_lenindef,
1679 ber_encoded=self.ber_encoded,
1682 for pp in self.pps_lenindef(decode_path):
1687 """``INTEGER`` integer type
1689 >>> b = Integer(-123)
1691 >>> b == Integer(-123)
1696 >>> Integer(2, bounds=(1, 3))
1698 >>> Integer(5, bounds=(1, 3))
1699 Traceback (most recent call last):
1700 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1704 class Version(Integer):
1711 >>> v = Version("v1")
1718 {'v3': 2, 'v1': 0, 'v2': 1}
1720 __slots__ = ("specs", "_bound_min", "_bound_max")
1721 tag_default = tag_encode(2)
1722 asn1_type_name = "INTEGER"
1736 :param value: set the value. Either integer type, named value
1737 (if ``schema`` is specified in the class), or
1738 :py:class:`pyderasn.Integer` object
1739 :param bounds: set ``(MIN, MAX)`` value constraint.
1740 (-inf, +inf) by default
1741 :param bytes impl: override default tag with ``IMPLICIT`` one
1742 :param bytes expl: override default tag with ``EXPLICIT`` one
1743 :param default: set default value. Type same as in ``value``
1744 :param bool optional: is object ``OPTIONAL`` in sequence
1746 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1748 specs = getattr(self, "schema", {}) if _specs is None else _specs
1749 self.specs = specs if isinstance(specs, dict) else dict(specs)
1750 self._bound_min, self._bound_max = getattr(
1753 (float("-inf"), float("+inf")),
1754 ) if bounds is None else bounds
1755 if value is not None:
1756 self._value = self._value_sanitize(value)
1757 if default is not None:
1758 default = self._value_sanitize(default)
1759 self.default = self.__class__(
1765 if self._value is None:
1766 self._value = default
1768 def _value_sanitize(self, value):
1769 if issubclass(value.__class__, Integer):
1770 value = value._value
1771 elif isinstance(value, integer_types):
1773 elif isinstance(value, str):
1774 value = self.specs.get(value)
1776 raise ObjUnknown("integer value: %s" % value)
1778 raise InvalidValueType((self.__class__, int, str))
1779 if not self._bound_min <= value <= self._bound_max:
1780 raise BoundsError(self._bound_min, value, self._bound_max)
1785 return self._value is not None
1788 obj = self.__class__(_specs=self.specs)
1789 obj._value = self._value
1790 obj._bound_min = self._bound_min
1791 obj._bound_max = self._bound_max
1793 obj._expl = self._expl
1794 obj.default = self.default
1795 obj.optional = self.optional
1796 obj.offset = self.offset
1797 obj.llen = self.llen
1798 obj.vlen = self.vlen
1802 self._assert_ready()
1803 return int(self._value)
1806 self._assert_ready()
1809 bytes(self._expl or b"") +
1810 str(self._value).encode("ascii"),
1813 def __eq__(self, their):
1814 if isinstance(their, integer_types):
1815 return self._value == their
1816 if not issubclass(their.__class__, Integer):
1819 self._value == their._value and
1820 self.tag == their.tag and
1821 self._expl == their._expl
1824 def __lt__(self, their):
1825 return self._value < their._value
1829 for name, value in self.specs.items():
1830 if value == self._value:
1842 return self.__class__(
1845 (self._bound_min, self._bound_max)
1846 if bounds is None else bounds
1848 impl=self.tag if impl is None else impl,
1849 expl=self._expl if expl is None else expl,
1850 default=self.default if default is None else default,
1851 optional=self.optional if optional is None else optional,
1856 self._assert_ready()
1860 octets = bytearray([0])
1864 octets = bytearray()
1866 octets.append((value & 0xFF) ^ 0xFF)
1868 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1871 octets = bytearray()
1873 octets.append(value & 0xFF)
1875 if octets[-1] & 0x80 > 0:
1878 octets = bytes(octets)
1880 bytes_len = ceil(value.bit_length() / 8) or 1
1883 octets = value.to_bytes(
1888 except OverflowError:
1892 return b"".join((self.tag, len_encode(len(octets)), octets))
1894 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1896 t, _, lv = tag_strip(tlv)
1897 except DecodeError as err:
1898 raise err.__class__(
1900 klass=self.__class__,
1901 decode_path=decode_path,
1906 klass=self.__class__,
1907 decode_path=decode_path,
1913 l, llen, v = len_decode(lv)
1914 except DecodeError as err:
1915 raise err.__class__(
1917 klass=self.__class__,
1918 decode_path=decode_path,
1922 raise NotEnoughData(
1923 "encoded length is longer than data",
1924 klass=self.__class__,
1925 decode_path=decode_path,
1929 raise NotEnoughData(
1931 klass=self.__class__,
1932 decode_path=decode_path,
1935 v, tail = v[:l], v[l:]
1936 first_octet = byte2int(v)
1938 second_octet = byte2int(v[1:])
1940 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1941 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1944 "non normalized integer",
1945 klass=self.__class__,
1946 decode_path=decode_path,
1951 if first_octet & 0x80 > 0:
1952 octets = bytearray()
1953 for octet in bytearray(v):
1954 octets.append(octet ^ 0xFF)
1955 for octet in octets:
1956 value = (value << 8) | octet
1960 for octet in bytearray(v):
1961 value = (value << 8) | octet
1963 value = int.from_bytes(v, byteorder="big", signed=True)
1965 obj = self.__class__(
1967 bounds=(self._bound_min, self._bound_max),
1970 default=self.default,
1971 optional=self.optional,
1973 _decoded=(offset, llen, l),
1975 except BoundsError as err:
1978 klass=self.__class__,
1979 decode_path=decode_path,
1985 return pp_console_row(next(self.pps()))
1987 def pps(self, decode_path=()):
1989 asn1_type_name=self.asn1_type_name,
1990 obj_name=self.__class__.__name__,
1991 decode_path=decode_path,
1992 value=(self.named or str(self._value)) if self.ready else None,
1993 optional=self.optional,
1994 default=self == self.default,
1995 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1996 expl=None if self._expl is None else tag_decode(self._expl),
2001 expl_offset=self.expl_offset if self.expled else None,
2002 expl_tlen=self.expl_tlen if self.expled else None,
2003 expl_llen=self.expl_llen if self.expled else None,
2004 expl_vlen=self.expl_vlen if self.expled else None,
2005 expl_lenindef=self.expl_lenindef,
2008 for pp in self.pps_lenindef(decode_path):
2012 class BitString(Obj):
2013 """``BIT STRING`` bit string type
2015 >>> BitString(b"hello world")
2016 BIT STRING 88 bits 68656c6c6f20776f726c64
2019 >>> b == b"hello world"
2024 >>> BitString("'0A3B5F291CD'H")
2025 BIT STRING 44 bits 0a3b5f291cd0
2026 >>> b = BitString("'010110000000'B")
2027 BIT STRING 12 bits 5800
2030 >>> b[0], b[1], b[2], b[3]
2031 (False, True, False, True)
2035 [False, True, False, True, True, False, False, False, False, False, False, False]
2039 class KeyUsage(BitString):
2041 ("digitalSignature", 0),
2042 ("nonRepudiation", 1),
2043 ("keyEncipherment", 2),
2046 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2047 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2049 ['nonRepudiation', 'keyEncipherment']
2051 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2055 Pay attention that BIT STRING can be encoded both in primitive
2056 and constructed forms. Decoder always checks constructed form tag
2057 additionally to specified primitive one. If BER decoding is
2058 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2059 of DER restrictions.
2061 __slots__ = ("tag_constructed", "specs", "defined")
2062 tag_default = tag_encode(3)
2063 asn1_type_name = "BIT STRING"
2076 :param value: set the value. Either binary type, tuple of named
2077 values (if ``schema`` is specified in the class),
2078 string in ``'XXX...'B`` form, or
2079 :py:class:`pyderasn.BitString` object
2080 :param bytes impl: override default tag with ``IMPLICIT`` one
2081 :param bytes expl: override default tag with ``EXPLICIT`` one
2082 :param default: set default value. Type same as in ``value``
2083 :param bool optional: is object ``OPTIONAL`` in sequence
2085 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2086 specs = getattr(self, "schema", {}) if _specs is None else _specs
2087 self.specs = specs if isinstance(specs, dict) else dict(specs)
2088 self._value = None if value is None else self._value_sanitize(value)
2089 if default is not None:
2090 default = self._value_sanitize(default)
2091 self.default = self.__class__(
2097 self._value = default
2099 tag_klass, _, tag_num = tag_decode(self.tag)
2100 self.tag_constructed = tag_encode(
2102 form=TagFormConstructed,
2106 def _bits2octets(self, bits):
2107 if len(self.specs) > 0:
2108 bits = bits.rstrip("0")
2110 bits += "0" * ((8 - (bit_len % 8)) % 8)
2111 octets = bytearray(len(bits) // 8)
2112 for i in six_xrange(len(octets)):
2113 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2114 return bit_len, bytes(octets)
2116 def _value_sanitize(self, value):
2117 if issubclass(value.__class__, BitString):
2119 if isinstance(value, (string_types, binary_type)):
2121 isinstance(value, string_types) and
2122 value.startswith("'")
2124 if value.endswith("'B"):
2126 if not set(value) <= set(("0", "1")):
2127 raise ValueError("B's coding contains unacceptable chars")
2128 return self._bits2octets(value)
2129 elif value.endswith("'H"):
2133 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2135 if isinstance(value, binary_type):
2136 return (len(value) * 8, value)
2138 raise InvalidValueType((self.__class__, string_types, binary_type))
2139 if isinstance(value, tuple):
2142 isinstance(value[0], integer_types) and
2143 isinstance(value[1], binary_type)
2148 bit = self.specs.get(name)
2150 raise ObjUnknown("BitString value: %s" % name)
2153 return self._bits2octets("")
2155 return self._bits2octets("".join(
2156 ("1" if bit in bits else "0")
2157 for bit in six_xrange(max(bits) + 1)
2159 raise InvalidValueType((self.__class__, binary_type, string_types))
2163 return self._value is not None
2166 obj = self.__class__(_specs=self.specs)
2168 if value is not None:
2169 value = (value[0], value[1])
2172 obj._expl = self._expl
2173 obj.default = self.default
2174 obj.optional = self.optional
2175 obj.offset = self.offset
2176 obj.llen = self.llen
2177 obj.vlen = self.vlen
2181 self._assert_ready()
2182 for i in six_xrange(self._value[0]):
2187 self._assert_ready()
2188 return self._value[0]
2190 def __bytes__(self):
2191 self._assert_ready()
2192 return self._value[1]
2194 def __eq__(self, their):
2195 if isinstance(their, bytes):
2196 return self._value[1] == their
2197 if not issubclass(their.__class__, BitString):
2200 self._value == their._value and
2201 self.tag == their.tag and
2202 self._expl == their._expl
2207 return [name for name, bit in self.specs.items() if self[bit]]
2217 return self.__class__(
2219 impl=self.tag if impl is None else impl,
2220 expl=self._expl if expl is None else expl,
2221 default=self.default if default is None else default,
2222 optional=self.optional if optional is None else optional,
2226 def __getitem__(self, key):
2227 if isinstance(key, int):
2228 bit_len, octets = self._value
2232 byte2int(memoryview(octets)[key // 8:]) >>
2235 if isinstance(key, string_types):
2236 value = self.specs.get(key)
2238 raise ObjUnknown("BitString value: %s" % key)
2240 raise InvalidValueType((int, str))
2243 self._assert_ready()
2244 bit_len, octets = self._value
2247 len_encode(len(octets) + 1),
2248 int2byte((8 - bit_len % 8) % 8),
2252 def _decode_chunk(self, lv, offset, decode_path, ctx):
2254 l, llen, v = len_decode(lv)
2255 except DecodeError as err:
2256 raise err.__class__(
2258 klass=self.__class__,
2259 decode_path=decode_path,
2263 raise NotEnoughData(
2264 "encoded length is longer than data",
2265 klass=self.__class__,
2266 decode_path=decode_path,
2270 raise NotEnoughData(
2272 klass=self.__class__,
2273 decode_path=decode_path,
2276 pad_size = byte2int(v)
2277 if l == 1 and pad_size != 0:
2279 "invalid empty value",
2280 klass=self.__class__,
2281 decode_path=decode_path,
2287 klass=self.__class__,
2288 decode_path=decode_path,
2291 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2294 klass=self.__class__,
2295 decode_path=decode_path,
2298 v, tail = v[:l], v[l:]
2299 obj = self.__class__(
2300 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2303 default=self.default,
2304 optional=self.optional,
2306 _decoded=(offset, llen, l),
2310 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2312 t, tlen, lv = tag_strip(tlv)
2313 except DecodeError as err:
2314 raise err.__class__(
2316 klass=self.__class__,
2317 decode_path=decode_path,
2321 if tag_only: # pragma: no cover
2323 return self._decode_chunk(lv, offset, decode_path, ctx)
2324 if t == self.tag_constructed:
2325 if not ctx.get("bered", False):
2327 "unallowed BER constructed encoding",
2328 klass=self.__class__,
2329 decode_path=decode_path,
2332 if tag_only: # pragma: no cover
2336 l, llen, v = len_decode(lv)
2337 except LenIndefForm:
2338 llen, l, v = 1, 0, lv[1:]
2340 except DecodeError as err:
2341 raise err.__class__(
2343 klass=self.__class__,
2344 decode_path=decode_path,
2348 raise NotEnoughData(
2349 "encoded length is longer than data",
2350 klass=self.__class__,
2351 decode_path=decode_path,
2354 if not lenindef and l == 0:
2355 raise NotEnoughData(
2357 klass=self.__class__,
2358 decode_path=decode_path,
2362 sub_offset = offset + tlen + llen
2366 if v[:EOC_LEN].tobytes() == EOC:
2373 "chunk out of bounds",
2374 klass=self.__class__,
2375 decode_path=decode_path + (str(len(chunks) - 1),),
2376 offset=chunks[-1].offset,
2378 sub_decode_path = decode_path + (str(len(chunks)),)
2380 chunk, v_tail = BitString().decode(
2383 decode_path=sub_decode_path,
2389 "expected BitString encoded chunk",
2390 klass=self.__class__,
2391 decode_path=sub_decode_path,
2394 chunks.append(chunk)
2395 sub_offset += chunk.tlvlen
2396 vlen += chunk.tlvlen
2398 if len(chunks) == 0:
2401 klass=self.__class__,
2402 decode_path=decode_path,
2407 for chunk_i, chunk in enumerate(chunks[:-1]):
2408 if chunk.bit_len % 8 != 0:
2410 "BitString chunk is not multiple of 8 bits",
2411 klass=self.__class__,
2412 decode_path=decode_path + (str(chunk_i),),
2413 offset=chunk.offset,
2415 values.append(bytes(chunk))
2416 bit_len += chunk.bit_len
2417 chunk_last = chunks[-1]
2418 values.append(bytes(chunk_last))
2419 bit_len += chunk_last.bit_len
2420 obj = self.__class__(
2421 value=(bit_len, b"".join(values)),
2424 default=self.default,
2425 optional=self.optional,
2427 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2429 obj.lenindef = lenindef
2430 obj.ber_encoded = True
2431 return obj, (v[EOC_LEN:] if lenindef else v)
2433 klass=self.__class__,
2434 decode_path=decode_path,
2439 return pp_console_row(next(self.pps()))
2441 def pps(self, decode_path=()):
2445 bit_len, blob = self._value
2446 value = "%d bits" % bit_len
2447 if len(self.specs) > 0:
2448 blob = tuple(self.named)
2450 asn1_type_name=self.asn1_type_name,
2451 obj_name=self.__class__.__name__,
2452 decode_path=decode_path,
2455 optional=self.optional,
2456 default=self == self.default,
2457 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2458 expl=None if self._expl is None else tag_decode(self._expl),
2463 expl_offset=self.expl_offset if self.expled else None,
2464 expl_tlen=self.expl_tlen if self.expled else None,
2465 expl_llen=self.expl_llen if self.expled else None,
2466 expl_vlen=self.expl_vlen if self.expled else None,
2467 expl_lenindef=self.expl_lenindef,
2468 lenindef=self.lenindef,
2469 ber_encoded=self.ber_encoded,
2472 defined_by, defined = self.defined or (None, None)
2473 if defined_by is not None:
2475 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2477 for pp in self.pps_lenindef(decode_path):
2481 class OctetString(Obj):
2482 """``OCTET STRING`` binary string type
2484 >>> s = OctetString(b"hello world")
2485 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2486 >>> s == OctetString(b"hello world")
2491 >>> OctetString(b"hello", bounds=(4, 4))
2492 Traceback (most recent call last):
2493 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2494 >>> OctetString(b"hell", bounds=(4, 4))
2495 OCTET STRING 4 bytes 68656c6c
2499 Pay attention that OCTET STRING can be encoded both in primitive
2500 and constructed forms. Decoder always checks constructed form tag
2501 additionally to specified primitive one. If BER decoding is
2502 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2503 of DER restrictions.
2505 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2506 tag_default = tag_encode(4)
2507 asn1_type_name = "OCTET STRING"
2520 :param value: set the value. Either binary type, or
2521 :py:class:`pyderasn.OctetString` object
2522 :param bounds: set ``(MIN, MAX)`` value size constraint.
2523 (-inf, +inf) by default
2524 :param bytes impl: override default tag with ``IMPLICIT`` one
2525 :param bytes expl: override default tag with ``EXPLICIT`` one
2526 :param default: set default value. Type same as in ``value``
2527 :param bool optional: is object ``OPTIONAL`` in sequence
2529 super(OctetString, self).__init__(
2537 self._bound_min, self._bound_max = getattr(
2541 ) if bounds is None else bounds
2542 if value is not None:
2543 self._value = self._value_sanitize(value)
2544 if default is not None:
2545 default = self._value_sanitize(default)
2546 self.default = self.__class__(
2551 if self._value is None:
2552 self._value = default
2554 tag_klass, _, tag_num = tag_decode(self.tag)
2555 self.tag_constructed = tag_encode(
2557 form=TagFormConstructed,
2561 def _value_sanitize(self, value):
2562 if issubclass(value.__class__, OctetString):
2563 value = value._value
2564 elif isinstance(value, binary_type):
2567 raise InvalidValueType((self.__class__, bytes))
2568 if not self._bound_min <= len(value) <= self._bound_max:
2569 raise BoundsError(self._bound_min, len(value), self._bound_max)
2574 return self._value is not None
2577 obj = self.__class__()
2578 obj._value = self._value
2579 obj._bound_min = self._bound_min
2580 obj._bound_max = self._bound_max
2582 obj._expl = self._expl
2583 obj.default = self.default
2584 obj.optional = self.optional
2585 obj.offset = self.offset
2586 obj.llen = self.llen
2587 obj.vlen = self.vlen
2590 def __bytes__(self):
2591 self._assert_ready()
2594 def __eq__(self, their):
2595 if isinstance(their, binary_type):
2596 return self._value == their
2597 if not issubclass(their.__class__, OctetString):
2600 self._value == their._value and
2601 self.tag == their.tag and
2602 self._expl == their._expl
2605 def __lt__(self, their):
2606 return self._value < their._value
2617 return self.__class__(
2620 (self._bound_min, self._bound_max)
2621 if bounds is None else bounds
2623 impl=self.tag if impl is None else impl,
2624 expl=self._expl if expl is None else expl,
2625 default=self.default if default is None else default,
2626 optional=self.optional if optional is None else optional,
2630 self._assert_ready()
2633 len_encode(len(self._value)),
2637 def _decode_chunk(self, lv, offset, decode_path, ctx):
2639 l, llen, v = len_decode(lv)
2640 except DecodeError as err:
2641 raise err.__class__(
2643 klass=self.__class__,
2644 decode_path=decode_path,
2648 raise NotEnoughData(
2649 "encoded length is longer than data",
2650 klass=self.__class__,
2651 decode_path=decode_path,
2654 v, tail = v[:l], v[l:]
2656 obj = self.__class__(
2658 bounds=(self._bound_min, self._bound_max),
2661 default=self.default,
2662 optional=self.optional,
2663 _decoded=(offset, llen, l),
2665 except DecodeError as err:
2668 klass=self.__class__,
2669 decode_path=decode_path,
2672 except BoundsError as err:
2675 klass=self.__class__,
2676 decode_path=decode_path,
2681 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2683 t, tlen, lv = tag_strip(tlv)
2684 except DecodeError as err:
2685 raise err.__class__(
2687 klass=self.__class__,
2688 decode_path=decode_path,
2694 return self._decode_chunk(lv, offset, decode_path, ctx)
2695 if t == self.tag_constructed:
2696 if not ctx.get("bered", False):
2698 "unallowed BER constructed encoding",
2699 klass=self.__class__,
2700 decode_path=decode_path,
2707 l, llen, v = len_decode(lv)
2708 except LenIndefForm:
2709 llen, l, v = 1, 0, lv[1:]
2711 except DecodeError as err:
2712 raise err.__class__(
2714 klass=self.__class__,
2715 decode_path=decode_path,
2719 raise NotEnoughData(
2720 "encoded length is longer than data",
2721 klass=self.__class__,
2722 decode_path=decode_path,
2726 sub_offset = offset + tlen + llen
2730 if v[:EOC_LEN].tobytes() == EOC:
2737 "chunk out of bounds",
2738 klass=self.__class__,
2739 decode_path=decode_path + (str(len(chunks) - 1),),
2740 offset=chunks[-1].offset,
2742 sub_decode_path = decode_path + (str(len(chunks)),)
2744 chunk, v_tail = OctetString().decode(
2747 decode_path=sub_decode_path,
2753 "expected OctetString encoded chunk",
2754 klass=self.__class__,
2755 decode_path=sub_decode_path,
2758 chunks.append(chunk)
2759 sub_offset += chunk.tlvlen
2760 vlen += chunk.tlvlen
2763 obj = self.__class__(
2764 value=b"".join(bytes(chunk) for chunk in chunks),
2765 bounds=(self._bound_min, self._bound_max),
2768 default=self.default,
2769 optional=self.optional,
2770 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2772 except DecodeError as err:
2775 klass=self.__class__,
2776 decode_path=decode_path,
2779 except BoundsError as err:
2782 klass=self.__class__,
2783 decode_path=decode_path,
2786 obj.lenindef = lenindef
2787 obj.ber_encoded = True
2788 return obj, (v[EOC_LEN:] if lenindef else v)
2790 klass=self.__class__,
2791 decode_path=decode_path,
2796 return pp_console_row(next(self.pps()))
2798 def pps(self, decode_path=()):
2800 asn1_type_name=self.asn1_type_name,
2801 obj_name=self.__class__.__name__,
2802 decode_path=decode_path,
2803 value=("%d bytes" % len(self._value)) if self.ready else None,
2804 blob=self._value if self.ready else None,
2805 optional=self.optional,
2806 default=self == self.default,
2807 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2808 expl=None if self._expl is None else tag_decode(self._expl),
2813 expl_offset=self.expl_offset if self.expled else None,
2814 expl_tlen=self.expl_tlen if self.expled else None,
2815 expl_llen=self.expl_llen if self.expled else None,
2816 expl_vlen=self.expl_vlen if self.expled else None,
2817 expl_lenindef=self.expl_lenindef,
2818 lenindef=self.lenindef,
2819 ber_encoded=self.ber_encoded,
2822 defined_by, defined = self.defined or (None, None)
2823 if defined_by is not None:
2825 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2827 for pp in self.pps_lenindef(decode_path):
2832 """``NULL`` null object
2840 tag_default = tag_encode(5)
2841 asn1_type_name = "NULL"
2845 value=None, # unused, but Sequence passes it
2852 :param bytes impl: override default tag with ``IMPLICIT`` one
2853 :param bytes expl: override default tag with ``EXPLICIT`` one
2854 :param bool optional: is object ``OPTIONAL`` in sequence
2856 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2864 obj = self.__class__()
2866 obj._expl = self._expl
2867 obj.default = self.default
2868 obj.optional = self.optional
2869 obj.offset = self.offset
2870 obj.llen = self.llen
2871 obj.vlen = self.vlen
2874 def __eq__(self, their):
2875 if not issubclass(their.__class__, Null):
2878 self.tag == their.tag and
2879 self._expl == their._expl
2889 return self.__class__(
2890 impl=self.tag if impl is None else impl,
2891 expl=self._expl if expl is None else expl,
2892 optional=self.optional if optional is None else optional,
2896 return self.tag + len_encode(0)
2898 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2900 t, _, lv = tag_strip(tlv)
2901 except DecodeError as err:
2902 raise err.__class__(
2904 klass=self.__class__,
2905 decode_path=decode_path,
2910 klass=self.__class__,
2911 decode_path=decode_path,
2914 if tag_only: # pragma: no cover
2917 l, _, v = len_decode(lv)
2918 except DecodeError as err:
2919 raise err.__class__(
2921 klass=self.__class__,
2922 decode_path=decode_path,
2926 raise InvalidLength(
2927 "Null must have zero length",
2928 klass=self.__class__,
2929 decode_path=decode_path,
2932 obj = self.__class__(
2935 optional=self.optional,
2936 _decoded=(offset, 1, 0),
2941 return pp_console_row(next(self.pps()))
2943 def pps(self, decode_path=()):
2945 asn1_type_name=self.asn1_type_name,
2946 obj_name=self.__class__.__name__,
2947 decode_path=decode_path,
2948 optional=self.optional,
2949 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2950 expl=None if self._expl is None else tag_decode(self._expl),
2955 expl_offset=self.expl_offset if self.expled else None,
2956 expl_tlen=self.expl_tlen if self.expled else None,
2957 expl_llen=self.expl_llen if self.expled else None,
2958 expl_vlen=self.expl_vlen if self.expled else None,
2959 expl_lenindef=self.expl_lenindef,
2962 for pp in self.pps_lenindef(decode_path):
2966 class ObjectIdentifier(Obj):
2967 """``OBJECT IDENTIFIER`` OID type
2969 >>> oid = ObjectIdentifier((1, 2, 3))
2970 OBJECT IDENTIFIER 1.2.3
2971 >>> oid == ObjectIdentifier("1.2.3")
2977 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2978 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2980 >>> str(ObjectIdentifier((3, 1)))
2981 Traceback (most recent call last):
2982 pyderasn.InvalidOID: unacceptable first arc value
2984 __slots__ = ("defines",)
2985 tag_default = tag_encode(6)
2986 asn1_type_name = "OBJECT IDENTIFIER"
2999 :param value: set the value. Either tuples of integers,
3000 string of "."-concatenated integers, or
3001 :py:class:`pyderasn.ObjectIdentifier` object
3002 :param defines: sequence of tuples. Each tuple has two elements.
3003 First one is relative to current one decode
3004 path, aiming to the field defined by that OID.
3005 Read about relative path in
3006 :py:func:`pyderasn.abs_decode_path`. Second
3007 tuple element is ``{OID: pyderasn.Obj()}``
3008 dictionary, mapping between current OID value
3009 and structure applied to defined field.
3010 :ref:`Read about DEFINED BY <definedby>`
3011 :param bytes impl: override default tag with ``IMPLICIT`` one
3012 :param bytes expl: override default tag with ``EXPLICIT`` one
3013 :param default: set default value. Type same as in ``value``
3014 :param bool optional: is object ``OPTIONAL`` in sequence
3016 super(ObjectIdentifier, self).__init__(
3024 if value is not None:
3025 self._value = self._value_sanitize(value)
3026 if default is not None:
3027 default = self._value_sanitize(default)
3028 self.default = self.__class__(
3033 if self._value is None:
3034 self._value = default
3035 self.defines = defines
3037 def __add__(self, their):
3038 if isinstance(their, self.__class__):
3039 return self.__class__(self._value + their._value)
3040 if isinstance(their, tuple):
3041 return self.__class__(self._value + their)
3042 raise InvalidValueType((self.__class__, tuple))
3044 def _value_sanitize(self, value):
3045 if issubclass(value.__class__, ObjectIdentifier):
3047 if isinstance(value, string_types):
3049 value = tuple(int(arc) for arc in value.split("."))
3051 raise InvalidOID("unacceptable arcs values")
3052 if isinstance(value, tuple):
3054 raise InvalidOID("less than 2 arcs")
3055 first_arc = value[0]
3056 if first_arc in (0, 1):
3057 if not (0 <= value[1] <= 39):
3058 raise InvalidOID("second arc is too wide")
3059 elif first_arc == 2:
3062 raise InvalidOID("unacceptable first arc value")
3064 raise InvalidValueType((self.__class__, str, tuple))
3068 return self._value is not None
3071 obj = self.__class__()
3072 obj._value = self._value
3073 obj.defines = self.defines
3075 obj._expl = self._expl
3076 obj.default = self.default
3077 obj.optional = self.optional
3078 obj.offset = self.offset
3079 obj.llen = self.llen
3080 obj.vlen = self.vlen
3084 self._assert_ready()
3085 return iter(self._value)
3088 return ".".join(str(arc) for arc in self._value or ())
3091 self._assert_ready()
3094 bytes(self._expl or b"") +
3095 str(self._value).encode("ascii"),
3098 def __eq__(self, their):
3099 if isinstance(their, tuple):
3100 return self._value == their
3101 if not issubclass(their.__class__, ObjectIdentifier):
3104 self.tag == their.tag and
3105 self._expl == their._expl and
3106 self._value == their._value
3109 def __lt__(self, their):
3110 return self._value < their._value
3121 return self.__class__(
3123 defines=self.defines if defines is None else defines,
3124 impl=self.tag if impl is None else impl,
3125 expl=self._expl if expl is None else expl,
3126 default=self.default if default is None else default,
3127 optional=self.optional if optional is None else optional,
3131 self._assert_ready()
3133 first_value = value[1]
3134 first_arc = value[0]
3137 elif first_arc == 1:
3139 elif first_arc == 2:
3141 else: # pragma: no cover
3142 raise RuntimeError("invalid arc is stored")
3143 octets = [zero_ended_encode(first_value)]
3144 for arc in value[2:]:
3145 octets.append(zero_ended_encode(arc))
3146 v = b"".join(octets)
3147 return b"".join((self.tag, len_encode(len(v)), v))
3149 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3151 t, _, lv = tag_strip(tlv)
3152 except DecodeError as err:
3153 raise err.__class__(
3155 klass=self.__class__,
3156 decode_path=decode_path,
3161 klass=self.__class__,
3162 decode_path=decode_path,
3165 if tag_only: # pragma: no cover
3168 l, llen, v = len_decode(lv)
3169 except DecodeError as err:
3170 raise err.__class__(
3172 klass=self.__class__,
3173 decode_path=decode_path,
3177 raise NotEnoughData(
3178 "encoded length is longer than data",
3179 klass=self.__class__,
3180 decode_path=decode_path,
3184 raise NotEnoughData(
3186 klass=self.__class__,
3187 decode_path=decode_path,
3190 v, tail = v[:l], v[l:]
3196 octet = indexbytes(v, i)
3197 arc = (arc << 7) | (octet & 0x7F)
3198 if octet & 0x80 == 0:
3206 klass=self.__class__,
3207 decode_path=decode_path,
3211 second_arc = arcs[0]
3212 if 0 <= second_arc <= 39:
3214 elif 40 <= second_arc <= 79:
3220 obj = self.__class__(
3221 value=tuple([first_arc, second_arc] + arcs[1:]),
3224 default=self.default,
3225 optional=self.optional,
3226 _decoded=(offset, llen, l),
3231 return pp_console_row(next(self.pps()))
3233 def pps(self, decode_path=()):
3235 asn1_type_name=self.asn1_type_name,
3236 obj_name=self.__class__.__name__,
3237 decode_path=decode_path,
3238 value=str(self) if self.ready else None,
3239 optional=self.optional,
3240 default=self == self.default,
3241 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3242 expl=None if self._expl is None else tag_decode(self._expl),
3247 expl_offset=self.expl_offset if self.expled else None,
3248 expl_tlen=self.expl_tlen if self.expled else None,
3249 expl_llen=self.expl_llen if self.expled else None,
3250 expl_vlen=self.expl_vlen if self.expled else None,
3251 expl_lenindef=self.expl_lenindef,
3254 for pp in self.pps_lenindef(decode_path):
3258 class Enumerated(Integer):
3259 """``ENUMERATED`` integer type
3261 This type is identical to :py:class:`pyderasn.Integer`, but requires
3262 schema to be specified and does not accept values missing from it.
3265 tag_default = tag_encode(10)
3266 asn1_type_name = "ENUMERATED"
3277 bounds=None, # dummy argument, workability for Integer.decode
3279 super(Enumerated, self).__init__(
3288 if len(self.specs) == 0:
3289 raise ValueError("schema must be specified")
3291 def _value_sanitize(self, value):
3292 if isinstance(value, self.__class__):
3293 value = value._value
3294 elif isinstance(value, integer_types):
3295 if value not in list(self.specs.values()):
3297 "unknown integer value: %s" % value,
3298 klass=self.__class__,
3300 elif isinstance(value, string_types):
3301 value = self.specs.get(value)
3303 raise ObjUnknown("integer value: %s" % value)
3305 raise InvalidValueType((self.__class__, int, str))
3309 obj = self.__class__(_specs=self.specs)
3310 obj._value = self._value
3311 obj._bound_min = self._bound_min
3312 obj._bound_max = self._bound_max
3314 obj._expl = self._expl
3315 obj.default = self.default
3316 obj.optional = self.optional
3317 obj.offset = self.offset
3318 obj.llen = self.llen
3319 obj.vlen = self.vlen
3331 return self.__class__(
3333 impl=self.tag if impl is None else impl,
3334 expl=self._expl if expl is None else expl,
3335 default=self.default if default is None else default,
3336 optional=self.optional if optional is None else optional,
3341 class CommonString(OctetString):
3342 """Common class for all strings
3344 Everything resembles :py:class:`pyderasn.OctetString`, except
3345 ability to deal with unicode text strings.
3347 >>> hexenc("привет мир".encode("utf-8"))
3348 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3349 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3351 >>> s = UTF8String("привет мир")
3352 UTF8String UTF8String привет мир
3354 'привет мир'
3355 >>> hexenc(bytes(s))
3356 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3358 >>> PrintableString("привет мир")
3359 Traceback (most recent call last):
3360 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3362 >>> BMPString("ада", bounds=(2, 2))
3363 Traceback (most recent call last):
3364 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3365 >>> s = BMPString("ад", bounds=(2, 2))
3368 >>> hexenc(bytes(s))
3376 * - :py:class:`pyderasn.UTF8String`
3378 * - :py:class:`pyderasn.NumericString`
3380 * - :py:class:`pyderasn.PrintableString`
3382 * - :py:class:`pyderasn.TeletexString`
3384 * - :py:class:`pyderasn.T61String`
3386 * - :py:class:`pyderasn.VideotexString`
3388 * - :py:class:`pyderasn.IA5String`
3390 * - :py:class:`pyderasn.GraphicString`
3392 * - :py:class:`pyderasn.VisibleString`
3394 * - :py:class:`pyderasn.ISO646String`
3396 * - :py:class:`pyderasn.GeneralString`
3398 * - :py:class:`pyderasn.UniversalString`
3400 * - :py:class:`pyderasn.BMPString`
3403 __slots__ = ("encoding",)
3405 def _value_sanitize(self, value):
3407 value_decoded = None
3408 if isinstance(value, self.__class__):
3409 value_raw = value._value
3410 elif isinstance(value, text_type):
3411 value_decoded = value
3412 elif isinstance(value, binary_type):
3415 raise InvalidValueType((self.__class__, text_type, binary_type))
3418 value_decoded.encode(self.encoding)
3419 if value_raw is None else value_raw
3422 value_raw.decode(self.encoding)
3423 if value_decoded is None else value_decoded
3425 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3426 raise DecodeError(str(err))
3427 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3435 def __eq__(self, their):
3436 if isinstance(their, binary_type):
3437 return self._value == their
3438 if isinstance(their, text_type):
3439 return self._value == their.encode(self.encoding)
3440 if not isinstance(their, self.__class__):
3443 self._value == their._value and
3444 self.tag == their.tag and
3445 self._expl == their._expl
3448 def __unicode__(self):
3450 return self._value.decode(self.encoding)
3451 return text_type(self._value)
3454 return pp_console_row(next(self.pps(no_unicode=PY2)))
3456 def pps(self, decode_path=(), no_unicode=False):
3459 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3461 asn1_type_name=self.asn1_type_name,
3462 obj_name=self.__class__.__name__,
3463 decode_path=decode_path,
3465 optional=self.optional,
3466 default=self == self.default,
3467 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3468 expl=None if self._expl is None else tag_decode(self._expl),
3473 expl_offset=self.expl_offset if self.expled else None,
3474 expl_tlen=self.expl_tlen if self.expled else None,
3475 expl_llen=self.expl_llen if self.expled else None,
3476 expl_vlen=self.expl_vlen if self.expled else None,
3477 expl_lenindef=self.expl_lenindef,
3478 ber_encoded=self.ber_encoded,
3481 for pp in self.pps_lenindef(decode_path):
3485 class UTF8String(CommonString):
3487 tag_default = tag_encode(12)
3489 asn1_type_name = "UTF8String"
3492 class NumericString(CommonString):
3495 Its value is properly sanitized: only ASCII digits can be stored.
3498 tag_default = tag_encode(18)
3500 asn1_type_name = "NumericString"
3501 allowable_chars = set(digits.encode("ascii") + b" ")
3503 def _value_sanitize(self, value):
3504 value = super(NumericString, self)._value_sanitize(value)
3505 if not set(value) <= self.allowable_chars:
3506 raise DecodeError("non-numeric value")
3510 class PrintableString(CommonString):
3512 tag_default = tag_encode(19)
3514 asn1_type_name = "PrintableString"
3515 allowable_chars = set((ascii_letters + digits + " '()+,-./:=?").encode("ascii"))
3517 def _value_sanitize(self, value):
3518 value = super(PrintableString, self)._value_sanitize(value)
3519 if not set(value) <= self.allowable_chars:
3520 raise DecodeError("non-printable value")
3524 class TeletexString(CommonString):
3526 tag_default = tag_encode(20)
3528 asn1_type_name = "TeletexString"
3531 class T61String(TeletexString):
3533 asn1_type_name = "T61String"
3536 class VideotexString(CommonString):
3538 tag_default = tag_encode(21)
3539 encoding = "iso-8859-1"
3540 asn1_type_name = "VideotexString"
3543 class IA5String(CommonString):
3545 tag_default = tag_encode(22)
3547 asn1_type_name = "IA5"
3550 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3551 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3552 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3555 class UTCTime(CommonString):
3556 """``UTCTime`` datetime type
3558 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3559 UTCTime UTCTime 2017-09-30T22:07:50
3565 datetime.datetime(2017, 9, 30, 22, 7, 50)
3566 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3567 datetime.datetime(1957, 9, 30, 22, 7, 50)
3570 tag_default = tag_encode(23)
3572 asn1_type_name = "UTCTime"
3574 fmt = "%y%m%d%H%M%SZ"
3584 bounds=None, # dummy argument, workability for OctetString.decode
3587 :param value: set the value. Either datetime type, or
3588 :py:class:`pyderasn.UTCTime` object
3589 :param bytes impl: override default tag with ``IMPLICIT`` one
3590 :param bytes expl: override default tag with ``EXPLICIT`` one
3591 :param default: set default value. Type same as in ``value``
3592 :param bool optional: is object ``OPTIONAL`` in sequence
3594 super(UTCTime, self).__init__(
3602 if value is not None:
3603 self._value = self._value_sanitize(value)
3604 if default is not None:
3605 default = self._value_sanitize(default)
3606 self.default = self.__class__(
3611 if self._value is None:
3612 self._value = default
3614 def _value_sanitize(self, value):
3615 if isinstance(value, self.__class__):
3617 if isinstance(value, datetime):
3618 return value.strftime(self.fmt).encode("ascii")
3619 if isinstance(value, binary_type):
3621 value_decoded = value.decode("ascii")
3622 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3623 raise DecodeError("invalid UTCTime encoding")
3624 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3626 datetime.strptime(value_decoded, self.fmt)
3627 except (TypeError, ValueError):
3628 raise DecodeError("invalid UTCTime format")
3631 raise DecodeError("invalid UTCTime length")
3632 raise InvalidValueType((self.__class__, datetime))
3634 def __eq__(self, their):
3635 if isinstance(their, binary_type):
3636 return self._value == their
3637 if isinstance(their, datetime):
3638 return self.todatetime() == their
3639 if not isinstance(their, self.__class__):
3642 self._value == their._value and
3643 self.tag == their.tag and
3644 self._expl == their._expl
3647 def todatetime(self):
3648 """Convert to datetime
3652 Pay attention that UTCTime can not hold full year, so all years
3653 having < 50 years are treated as 20xx, 19xx otherwise, according
3654 to X.509 recomendation.
3656 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3657 year = value.year % 100
3659 year=(2000 + year) if year < 50 else (1900 + year),
3663 minute=value.minute,
3664 second=value.second,
3668 return pp_console_row(next(self.pps()))
3670 def pps(self, decode_path=()):
3672 asn1_type_name=self.asn1_type_name,
3673 obj_name=self.__class__.__name__,
3674 decode_path=decode_path,
3675 value=self.todatetime().isoformat() if self.ready else None,
3676 optional=self.optional,
3677 default=self == self.default,
3678 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3679 expl=None if self._expl is None else tag_decode(self._expl),
3684 expl_offset=self.expl_offset if self.expled else None,
3685 expl_tlen=self.expl_tlen if self.expled else None,
3686 expl_llen=self.expl_llen if self.expled else None,
3687 expl_vlen=self.expl_vlen if self.expled else None,
3688 expl_lenindef=self.expl_lenindef,
3689 ber_encoded=self.ber_encoded,
3692 for pp in self.pps_lenindef(decode_path):
3696 class GeneralizedTime(UTCTime):
3697 """``GeneralizedTime`` datetime type
3699 This type is similar to :py:class:`pyderasn.UTCTime`.
3701 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3702 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3704 '20170930220750.000123Z'
3705 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3706 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3709 tag_default = tag_encode(24)
3710 asn1_type_name = "GeneralizedTime"
3712 fmt = "%Y%m%d%H%M%SZ"
3713 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3715 def _value_sanitize(self, value):
3716 if isinstance(value, self.__class__):
3718 if isinstance(value, datetime):
3719 return value.strftime(
3720 self.fmt_ms if value.microsecond > 0 else self.fmt
3722 if isinstance(value, binary_type):
3724 value_decoded = value.decode("ascii")
3725 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3726 raise DecodeError("invalid GeneralizedTime encoding")
3727 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3729 datetime.strptime(value_decoded, self.fmt)
3730 except (TypeError, ValueError):
3732 "invalid GeneralizedTime (without ms) format",
3735 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3737 datetime.strptime(value_decoded, self.fmt_ms)
3738 except (TypeError, ValueError):
3740 "invalid GeneralizedTime (with ms) format",
3745 "invalid GeneralizedTime length",
3746 klass=self.__class__,
3748 raise InvalidValueType((self.__class__, datetime))
3750 def todatetime(self):
3751 value = self._value.decode("ascii")
3752 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3753 return datetime.strptime(value, self.fmt)
3754 return datetime.strptime(value, self.fmt_ms)
3757 class GraphicString(CommonString):
3759 tag_default = tag_encode(25)
3760 encoding = "iso-8859-1"
3761 asn1_type_name = "GraphicString"
3764 class VisibleString(CommonString):
3766 tag_default = tag_encode(26)
3768 asn1_type_name = "VisibleString"
3771 class ISO646String(VisibleString):
3773 asn1_type_name = "ISO646String"
3776 class GeneralString(CommonString):
3778 tag_default = tag_encode(27)
3779 encoding = "iso-8859-1"
3780 asn1_type_name = "GeneralString"
3783 class UniversalString(CommonString):
3785 tag_default = tag_encode(28)
3786 encoding = "utf-32-be"
3787 asn1_type_name = "UniversalString"
3790 class BMPString(CommonString):
3792 tag_default = tag_encode(30)
3793 encoding = "utf-16-be"
3794 asn1_type_name = "BMPString"
3798 """``CHOICE`` special type
3802 class GeneralName(Choice):
3804 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3805 ("dNSName", IA5String(impl=tag_ctxp(2))),
3808 >>> gn = GeneralName()
3810 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3811 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3812 >>> gn["dNSName"] = IA5String("bar.baz")
3813 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3814 >>> gn["rfc822Name"]
3817 [2] IA5String IA5 bar.baz
3820 >>> gn.value == gn["dNSName"]
3823 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3825 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3826 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3828 __slots__ = ("specs",)
3830 asn1_type_name = "CHOICE"
3843 :param value: set the value. Either ``(choice, value)`` tuple, or
3844 :py:class:`pyderasn.Choice` object
3845 :param bytes impl: can not be set, do **not** use it
3846 :param bytes expl: override default tag with ``EXPLICIT`` one
3847 :param default: set default value. Type same as in ``value``
3848 :param bool optional: is object ``OPTIONAL`` in sequence
3850 if impl is not None:
3851 raise ValueError("no implicit tag allowed for CHOICE")
3852 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3854 schema = getattr(self, "schema", ())
3855 if len(schema) == 0:
3856 raise ValueError("schema must be specified")
3858 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3861 if value is not None:
3862 self._value = self._value_sanitize(value)
3863 if default is not None:
3864 default_value = self._value_sanitize(default)
3865 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3866 default_obj.specs = self.specs
3867 default_obj._value = default_value
3868 self.default = default_obj
3870 self._value = default_obj.copy()._value
3872 def _value_sanitize(self, value):
3873 if isinstance(value, self.__class__):
3875 if isinstance(value, tuple) and len(value) == 2:
3877 spec = self.specs.get(choice)
3879 raise ObjUnknown(choice)
3880 if not isinstance(obj, spec.__class__):
3881 raise InvalidValueType((spec,))
3882 return (choice, spec(obj))
3883 raise InvalidValueType((self.__class__, tuple))
3887 return self._value is not None and self._value[1].ready
3891 return self.expl_lenindef or (
3892 (self._value is not None) and
3893 self._value[1].bered
3897 obj = self.__class__(schema=self.specs)
3898 obj._expl = self._expl
3899 obj.default = self.default
3900 obj.optional = self.optional
3901 obj.offset = self.offset
3902 obj.llen = self.llen
3903 obj.vlen = self.vlen
3905 if value is not None:
3906 obj._value = (value[0], value[1].copy())
3909 def __eq__(self, their):
3910 if isinstance(their, tuple) and len(their) == 2:
3911 return self._value == their
3912 if not isinstance(their, self.__class__):
3915 self.specs == their.specs and
3916 self._value == their._value
3926 return self.__class__(
3929 expl=self._expl if expl is None else expl,
3930 default=self.default if default is None else default,
3931 optional=self.optional if optional is None else optional,
3936 self._assert_ready()
3937 return self._value[0]
3941 self._assert_ready()
3942 return self._value[1]
3944 def __getitem__(self, key):
3945 if key not in self.specs:
3946 raise ObjUnknown(key)
3947 if self._value is None:
3949 choice, value = self._value
3954 def __setitem__(self, key, value):
3955 spec = self.specs.get(key)
3957 raise ObjUnknown(key)
3958 if not isinstance(value, spec.__class__):
3959 raise InvalidValueType((spec.__class__,))
3960 self._value = (key, spec(value))
3968 return self._value[1].decoded if self.ready else False
3971 self._assert_ready()
3972 return self._value[1].encode()
3974 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3975 for choice, spec in self.specs.items():
3976 sub_decode_path = decode_path + (choice,)
3982 decode_path=sub_decode_path,
3991 klass=self.__class__,
3992 decode_path=decode_path,
3995 if tag_only: # pragma: no cover
3997 value, tail = spec.decode(
4001 decode_path=sub_decode_path,
4004 obj = self.__class__(
4007 default=self.default,
4008 optional=self.optional,
4009 _decoded=(offset, 0, value.fulllen),
4011 obj._value = (choice, value)
4015 value = pp_console_row(next(self.pps()))
4017 value = "%s[%r]" % (value, self.value)
4020 def pps(self, decode_path=()):
4022 asn1_type_name=self.asn1_type_name,
4023 obj_name=self.__class__.__name__,
4024 decode_path=decode_path,
4025 value=self.choice if self.ready else None,
4026 optional=self.optional,
4027 default=self == self.default,
4028 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4029 expl=None if self._expl is None else tag_decode(self._expl),
4034 expl_lenindef=self.expl_lenindef,
4038 yield self.value.pps(decode_path=decode_path + (self.choice,))
4039 for pp in self.pps_lenindef(decode_path):
4043 class PrimitiveTypes(Choice):
4044 """Predefined ``CHOICE`` for all generic primitive types
4046 It could be useful for general decoding of some unspecified values:
4048 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4049 OCTET STRING 3 bytes 666f6f
4050 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4054 schema = tuple((klass.__name__, klass()) for klass in (
4079 """``ANY`` special type
4081 >>> Any(Integer(-123))
4083 >>> a = Any(OctetString(b"hello world").encode())
4084 ANY 040b68656c6c6f20776f726c64
4085 >>> hexenc(bytes(a))
4086 b'0x040x0bhello world'
4088 __slots__ = ("defined",)
4089 tag_default = tag_encode(0)
4090 asn1_type_name = "ANY"
4100 :param value: set the value. Either any kind of pyderasn's
4101 **ready** object, or bytes. Pay attention that
4102 **no** validation is performed is raw binary value
4104 :param bytes expl: override default tag with ``EXPLICIT`` one
4105 :param bool optional: is object ``OPTIONAL`` in sequence
4107 super(Any, self).__init__(None, expl, None, optional, _decoded)
4108 self._value = None if value is None else self._value_sanitize(value)
4111 def _value_sanitize(self, value):
4112 if isinstance(value, self.__class__):
4114 if isinstance(value, Obj):
4115 return value.encode()
4116 if isinstance(value, binary_type):
4118 raise InvalidValueType((self.__class__, Obj, binary_type))
4122 return self._value is not None
4126 if self.expl_lenindef or self.lenindef:
4128 if self.defined is None:
4130 return self.defined[1].bered
4133 obj = self.__class__()
4134 obj._value = self._value
4136 obj._expl = self._expl
4137 obj.optional = self.optional
4138 obj.offset = self.offset
4139 obj.llen = self.llen
4140 obj.vlen = self.vlen
4143 def __eq__(self, their):
4144 if isinstance(their, binary_type):
4145 return self._value == their
4146 if issubclass(their.__class__, Any):
4147 return self._value == their._value
4156 return self.__class__(
4158 expl=self._expl if expl is None else expl,
4159 optional=self.optional if optional is None else optional,
4162 def __bytes__(self):
4163 self._assert_ready()
4171 self._assert_ready()
4174 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4176 t, tlen, lv = tag_strip(tlv)
4177 except DecodeError as err:
4178 raise err.__class__(
4180 klass=self.__class__,
4181 decode_path=decode_path,
4185 l, llen, v = len_decode(lv)
4186 except LenIndefForm as err:
4187 if not ctx.get("bered", False):
4188 raise err.__class__(
4190 klass=self.__class__,
4191 decode_path=decode_path,
4194 llen, vlen, v = 1, 0, lv[1:]
4195 sub_offset = offset + tlen + llen
4197 while v[:EOC_LEN].tobytes() != EOC:
4198 chunk, v = Any().decode(
4201 decode_path=decode_path + (str(chunk_i),),
4205 vlen += chunk.tlvlen
4206 sub_offset += chunk.tlvlen
4208 tlvlen = tlen + llen + vlen + EOC_LEN
4209 obj = self.__class__(
4210 value=tlv[:tlvlen].tobytes(),
4212 optional=self.optional,
4213 _decoded=(offset, 0, tlvlen),
4217 return obj, v[EOC_LEN:]
4218 except DecodeError as err:
4219 raise err.__class__(
4221 klass=self.__class__,
4222 decode_path=decode_path,
4226 raise NotEnoughData(
4227 "encoded length is longer than data",
4228 klass=self.__class__,
4229 decode_path=decode_path,
4232 tlvlen = tlen + llen + l
4233 v, tail = tlv[:tlvlen], v[l:]
4234 obj = self.__class__(
4237 optional=self.optional,
4238 _decoded=(offset, 0, tlvlen),
4244 return pp_console_row(next(self.pps()))
4246 def pps(self, decode_path=()):
4248 asn1_type_name=self.asn1_type_name,
4249 obj_name=self.__class__.__name__,
4250 decode_path=decode_path,
4251 blob=self._value if self.ready else None,
4252 optional=self.optional,
4253 default=self == self.default,
4254 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4255 expl=None if self._expl is None else tag_decode(self._expl),
4260 expl_offset=self.expl_offset if self.expled else None,
4261 expl_tlen=self.expl_tlen if self.expled else None,
4262 expl_llen=self.expl_llen if self.expled else None,
4263 expl_vlen=self.expl_vlen if self.expled else None,
4264 expl_lenindef=self.expl_lenindef,
4265 lenindef=self.lenindef,
4268 defined_by, defined = self.defined or (None, None)
4269 if defined_by is not None:
4271 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4273 for pp in self.pps_lenindef(decode_path):
4277 ########################################################################
4278 # ASN.1 constructed types
4279 ########################################################################
4281 def get_def_by_path(defines_by_path, sub_decode_path):
4282 """Get define by decode path
4284 for path, define in defines_by_path:
4285 if len(path) != len(sub_decode_path):
4287 for p1, p2 in zip(path, sub_decode_path):
4288 if (p1 != any) and (p1 != p2):
4294 def abs_decode_path(decode_path, rel_path):
4295 """Create an absolute decode path from current and relative ones
4297 :param decode_path: current decode path, starting point.
4299 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4300 If first tuple's element is "/", then treat it as
4301 an absolute path, ignoring ``decode_path`` as
4302 starting point. Also this tuple can contain ".."
4303 elements, stripping the leading element from
4306 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4307 ("foo", "bar", "baz", "whatever")
4308 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4310 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4313 if rel_path[0] == "/":
4315 if rel_path[0] == "..":
4316 return abs_decode_path(decode_path[:-1], rel_path[1:])
4317 return decode_path + rel_path
4320 class Sequence(Obj):
4321 """``SEQUENCE`` structure type
4323 You have to make specification of sequence::
4325 class Extension(Sequence):
4327 ("extnID", ObjectIdentifier()),
4328 ("critical", Boolean(default=False)),
4329 ("extnValue", OctetString()),
4332 Then, you can work with it as with dictionary.
4334 >>> ext = Extension()
4335 >>> Extension().specs
4337 ('extnID', OBJECT IDENTIFIER),
4338 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4339 ('extnValue', OCTET STRING),
4341 >>> ext["extnID"] = "1.2.3"
4342 Traceback (most recent call last):
4343 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4344 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4346 You can determine if sequence is ready to be encoded:
4351 Traceback (most recent call last):
4352 pyderasn.ObjNotReady: object is not ready: extnValue
4353 >>> ext["extnValue"] = OctetString(b"foobar")
4357 Value you want to assign, must have the same **type** as in
4358 corresponding specification, but it can have different tags,
4359 optional/default attributes -- they will be taken from specification
4362 class TBSCertificate(Sequence):
4364 ("version", Version(expl=tag_ctxc(0), default="v1")),
4367 >>> tbs = TBSCertificate()
4368 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4370 Assign ``None`` to remove value from sequence.
4372 You can set values in Sequence during its initialization:
4374 >>> AlgorithmIdentifier((
4375 ("algorithm", ObjectIdentifier("1.2.3")),
4376 ("parameters", Any(Null()))
4378 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4380 You can determine if value exists/set in the sequence and take its value:
4382 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4385 OBJECT IDENTIFIER 1.2.3
4387 But pay attention that if value has default, then it won't be (not
4388 in) in the sequence (because ``DEFAULT`` must not be encoded in
4389 DER), but you can read its value:
4391 >>> "critical" in ext, ext["critical"]
4392 (False, BOOLEAN False)
4393 >>> ext["critical"] = Boolean(True)
4394 >>> "critical" in ext, ext["critical"]
4395 (True, BOOLEAN True)
4397 All defaulted values are always optional.
4399 .. _allow_default_values_ctx:
4401 DER prohibits default value encoding and will raise an error if
4402 default value is unexpectedly met during decode.
4403 If :ref:`bered <bered_ctx>` context option is set, then no error
4404 will be raised, but ``bered`` attribute set. You can disable strict
4405 defaulted values existence validation by setting
4406 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4408 Two sequences are equal if they have equal specification (schema),
4409 implicit/explicit tagging and the same values.
4411 __slots__ = ("specs",)
4412 tag_default = tag_encode(form=TagFormConstructed, num=16)
4413 asn1_type_name = "SEQUENCE"
4425 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4427 schema = getattr(self, "schema", ())
4429 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4432 if value is not None:
4433 if issubclass(value.__class__, Sequence):
4434 self._value = value._value
4435 elif hasattr(value, "__iter__"):
4436 for seq_key, seq_value in value:
4437 self[seq_key] = seq_value
4439 raise InvalidValueType((Sequence,))
4440 if default is not None:
4441 if not issubclass(default.__class__, Sequence):
4442 raise InvalidValueType((Sequence,))
4443 default_value = default._value
4444 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4445 default_obj.specs = self.specs
4446 default_obj._value = default_value
4447 self.default = default_obj
4449 self._value = default_obj.copy()._value
4453 for name, spec in self.specs.items():
4454 value = self._value.get(name)
4466 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4468 return any(value.bered for value in self._value.values())
4471 obj = self.__class__(schema=self.specs)
4473 obj._expl = self._expl
4474 obj.default = self.default
4475 obj.optional = self.optional
4476 obj.offset = self.offset
4477 obj.llen = self.llen
4478 obj.vlen = self.vlen
4479 obj._value = {k: v.copy() for k, v in self._value.items()}
4482 def __eq__(self, their):
4483 if not isinstance(their, self.__class__):
4486 self.specs == their.specs and
4487 self.tag == their.tag and
4488 self._expl == their._expl and
4489 self._value == their._value
4500 return self.__class__(
4503 impl=self.tag if impl is None else impl,
4504 expl=self._expl if expl is None else expl,
4505 default=self.default if default is None else default,
4506 optional=self.optional if optional is None else optional,
4509 def __contains__(self, key):
4510 return key in self._value
4512 def __setitem__(self, key, value):
4513 spec = self.specs.get(key)
4515 raise ObjUnknown(key)
4517 self._value.pop(key, None)
4519 if not isinstance(value, spec.__class__):
4520 raise InvalidValueType((spec.__class__,))
4521 value = spec(value=value)
4522 if spec.default is not None and value == spec.default:
4523 self._value.pop(key, None)
4525 self._value[key] = value
4527 def __getitem__(self, key):
4528 value = self._value.get(key)
4529 if value is not None:
4531 spec = self.specs.get(key)
4533 raise ObjUnknown(key)
4534 if spec.default is not None:
4538 def _encoded_values(self):
4540 for name, spec in self.specs.items():
4541 value = self._value.get(name)
4545 raise ObjNotReady(name)
4546 raws.append(value.encode())
4550 v = b"".join(self._encoded_values())
4551 return b"".join((self.tag, len_encode(len(v)), v))
4553 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4555 t, tlen, lv = tag_strip(tlv)
4556 except DecodeError as err:
4557 raise err.__class__(
4559 klass=self.__class__,
4560 decode_path=decode_path,
4565 klass=self.__class__,
4566 decode_path=decode_path,
4569 if tag_only: # pragma: no cover
4572 ctx_bered = ctx.get("bered", False)
4574 l, llen, v = len_decode(lv)
4575 except LenIndefForm as err:
4577 raise err.__class__(
4579 klass=self.__class__,
4580 decode_path=decode_path,
4583 l, llen, v = 0, 1, lv[1:]
4585 except DecodeError as err:
4586 raise err.__class__(
4588 klass=self.__class__,
4589 decode_path=decode_path,
4593 raise NotEnoughData(
4594 "encoded length is longer than data",
4595 klass=self.__class__,
4596 decode_path=decode_path,
4600 v, tail = v[:l], v[l:]
4602 sub_offset = offset + tlen + llen
4605 ctx_allow_default_values = ctx.get("allow_default_values", False)
4606 for name, spec in self.specs.items():
4607 if spec.optional and (
4608 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4612 sub_decode_path = decode_path + (name,)
4614 value, v_tail = spec.decode(
4618 decode_path=sub_decode_path,
4626 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4627 if defined is not None:
4628 defined_by, defined_spec = defined
4629 if issubclass(value.__class__, SequenceOf):
4630 for i, _value in enumerate(value):
4631 sub_sub_decode_path = sub_decode_path + (
4633 DecodePathDefBy(defined_by),
4635 defined_value, defined_tail = defined_spec.decode(
4636 memoryview(bytes(_value)),
4638 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4639 if value.expled else (value.tlen + value.llen)
4642 decode_path=sub_sub_decode_path,
4645 if len(defined_tail) > 0:
4648 klass=self.__class__,
4649 decode_path=sub_sub_decode_path,
4652 _value.defined = (defined_by, defined_value)
4654 defined_value, defined_tail = defined_spec.decode(
4655 memoryview(bytes(value)),
4657 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4658 if value.expled else (value.tlen + value.llen)
4661 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4664 if len(defined_tail) > 0:
4667 klass=self.__class__,
4668 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4671 value.defined = (defined_by, defined_value)
4673 value_len = value.fulllen
4675 sub_offset += value_len
4677 if spec.default is not None and value == spec.default:
4678 if ctx_bered or ctx_allow_default_values:
4682 "DEFAULT value met",
4683 klass=self.__class__,
4684 decode_path=sub_decode_path,
4687 values[name] = value
4689 spec_defines = getattr(spec, "defines", ())
4690 if len(spec_defines) == 0:
4691 defines_by_path = ctx.get("defines_by_path", ())
4692 if len(defines_by_path) > 0:
4693 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4694 if spec_defines is not None and len(spec_defines) > 0:
4695 for rel_path, schema in spec_defines:
4696 defined = schema.get(value, None)
4697 if defined is not None:
4698 ctx.setdefault("_defines", []).append((
4699 abs_decode_path(sub_decode_path[:-1], rel_path),
4703 if v[:EOC_LEN].tobytes() != EOC:
4706 klass=self.__class__,
4707 decode_path=decode_path,
4715 klass=self.__class__,
4716 decode_path=decode_path,
4719 obj = self.__class__(
4723 default=self.default,
4724 optional=self.optional,
4725 _decoded=(offset, llen, vlen),
4728 obj.lenindef = lenindef
4729 obj.ber_encoded = ber_encoded
4733 value = pp_console_row(next(self.pps()))
4735 for name in self.specs:
4736 _value = self._value.get(name)
4739 cols.append("%s: %s" % (name, repr(_value)))
4740 return "%s[%s]" % (value, "; ".join(cols))
4742 def pps(self, decode_path=()):
4744 asn1_type_name=self.asn1_type_name,
4745 obj_name=self.__class__.__name__,
4746 decode_path=decode_path,
4747 optional=self.optional,
4748 default=self == self.default,
4749 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4750 expl=None if self._expl is None else tag_decode(self._expl),
4755 expl_offset=self.expl_offset if self.expled else None,
4756 expl_tlen=self.expl_tlen if self.expled else None,
4757 expl_llen=self.expl_llen if self.expled else None,
4758 expl_vlen=self.expl_vlen if self.expled else None,
4759 expl_lenindef=self.expl_lenindef,
4760 lenindef=self.lenindef,
4761 ber_encoded=self.ber_encoded,
4764 for name in self.specs:
4765 value = self._value.get(name)
4768 yield value.pps(decode_path=decode_path + (name,))
4769 for pp in self.pps_lenindef(decode_path):
4773 class Set(Sequence):
4774 """``SET`` structure type
4776 Its usage is identical to :py:class:`pyderasn.Sequence`.
4778 .. _allow_unordered_set_ctx:
4780 DER prohibits unordered values encoding and will raise an error
4781 during decode. If If :ref:`bered <bered_ctx>` context option is set,
4782 then no error will occure. Also you can disable strict values
4783 ordering check by setting ``"allow_unordered_set": True``
4784 :ref:`context <ctx>` option.
4787 tag_default = tag_encode(form=TagFormConstructed, num=17)
4788 asn1_type_name = "SET"
4791 raws = self._encoded_values()
4794 return b"".join((self.tag, len_encode(len(v)), v))
4796 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4798 t, tlen, lv = tag_strip(tlv)
4799 except DecodeError as err:
4800 raise err.__class__(
4802 klass=self.__class__,
4803 decode_path=decode_path,
4808 klass=self.__class__,
4809 decode_path=decode_path,
4815 ctx_bered = ctx.get("bered", False)
4817 l, llen, v = len_decode(lv)
4818 except LenIndefForm as err:
4820 raise err.__class__(
4822 klass=self.__class__,
4823 decode_path=decode_path,
4826 l, llen, v = 0, 1, lv[1:]
4828 except DecodeError as err:
4829 raise err.__class__(
4831 klass=self.__class__,
4832 decode_path=decode_path,
4836 raise NotEnoughData(
4837 "encoded length is longer than data",
4838 klass=self.__class__,
4842 v, tail = v[:l], v[l:]
4844 sub_offset = offset + tlen + llen
4847 ctx_allow_default_values = ctx.get("allow_default_values", False)
4848 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
4849 value_prev = memoryview(v[:0])
4850 specs_items = self.specs.items
4852 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4854 for name, spec in specs_items():
4855 sub_decode_path = decode_path + (name,)
4861 decode_path=sub_decode_path,
4870 klass=self.__class__,
4871 decode_path=decode_path,
4874 value, v_tail = spec.decode(
4878 decode_path=sub_decode_path,
4881 value_len = value.fulllen
4882 if value_prev.tobytes() > v[:value_len].tobytes():
4883 if ctx_bered or ctx_allow_unordered_set:
4887 "unordered " + self.asn1_type_name,
4888 klass=self.__class__,
4889 decode_path=sub_decode_path,
4892 if spec.default is None or value != spec.default:
4894 elif ctx_bered or ctx_allow_default_values:
4898 "DEFAULT value met",
4899 klass=self.__class__,
4900 decode_path=sub_decode_path,
4903 values[name] = value
4904 value_prev = v[:value_len]
4905 sub_offset += value_len
4908 obj = self.__class__(
4912 default=self.default,
4913 optional=self.optional,
4914 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4917 if v[:EOC_LEN].tobytes() != EOC:
4920 klass=self.__class__,
4921 decode_path=decode_path,
4929 "not all values are ready",
4930 klass=self.__class__,
4931 decode_path=decode_path,
4934 obj.ber_encoded = ber_encoded
4938 class SequenceOf(Obj):
4939 """``SEQUENCE OF`` sequence type
4941 For that kind of type you must specify the object it will carry on
4942 (bounds are for example here, not required)::
4944 class Ints(SequenceOf):
4949 >>> ints.append(Integer(123))
4950 >>> ints.append(Integer(234))
4952 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4953 >>> [int(i) for i in ints]
4955 >>> ints.append(Integer(345))
4956 Traceback (most recent call last):
4957 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4960 >>> ints[1] = Integer(345)
4962 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4964 Also you can initialize sequence with preinitialized values:
4966 >>> ints = Ints([Integer(123), Integer(234)])
4968 __slots__ = ("spec", "_bound_min", "_bound_max")
4969 tag_default = tag_encode(form=TagFormConstructed, num=16)
4970 asn1_type_name = "SEQUENCE OF"
4983 super(SequenceOf, self).__init__(
4991 schema = getattr(self, "schema", None)
4993 raise ValueError("schema must be specified")
4995 self._bound_min, self._bound_max = getattr(
4999 ) if bounds is None else bounds
5001 if value is not None:
5002 self._value = self._value_sanitize(value)
5003 if default is not None:
5004 default_value = self._value_sanitize(default)
5005 default_obj = self.__class__(
5010 default_obj._value = default_value
5011 self.default = default_obj
5013 self._value = default_obj.copy()._value
5015 def _value_sanitize(self, value):
5016 if issubclass(value.__class__, SequenceOf):
5017 value = value._value
5018 elif hasattr(value, "__iter__"):
5021 raise InvalidValueType((self.__class__, iter))
5022 if not self._bound_min <= len(value) <= self._bound_max:
5023 raise BoundsError(self._bound_min, len(value), self._bound_max)
5025 if not isinstance(v, self.spec.__class__):
5026 raise InvalidValueType((self.spec.__class__,))
5031 return all(v.ready for v in self._value)
5035 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5037 return any(v.bered for v in self._value)
5040 obj = self.__class__(schema=self.spec)
5041 obj._bound_min = self._bound_min
5042 obj._bound_max = self._bound_max
5044 obj._expl = self._expl
5045 obj.default = self.default
5046 obj.optional = self.optional
5047 obj.offset = self.offset
5048 obj.llen = self.llen
5049 obj.vlen = self.vlen
5050 obj._value = [v.copy() for v in self._value]
5053 def __eq__(self, their):
5054 if isinstance(their, self.__class__):
5056 self.spec == their.spec and
5057 self.tag == their.tag and
5058 self._expl == their._expl and
5059 self._value == their._value
5061 if hasattr(their, "__iter__"):
5062 return self._value == list(their)
5074 return self.__class__(
5078 (self._bound_min, self._bound_max)
5079 if bounds is None else bounds
5081 impl=self.tag if impl is None else impl,
5082 expl=self._expl if expl is None else expl,
5083 default=self.default if default is None else default,
5084 optional=self.optional if optional is None else optional,
5087 def __contains__(self, key):
5088 return key in self._value
5090 def append(self, value):
5091 if not isinstance(value, self.spec.__class__):
5092 raise InvalidValueType((self.spec.__class__,))
5093 if len(self._value) + 1 > self._bound_max:
5096 len(self._value) + 1,
5099 self._value.append(value)
5102 self._assert_ready()
5103 return iter(self._value)
5106 self._assert_ready()
5107 return len(self._value)
5109 def __setitem__(self, key, value):
5110 if not isinstance(value, self.spec.__class__):
5111 raise InvalidValueType((self.spec.__class__,))
5112 self._value[key] = self.spec(value=value)
5114 def __getitem__(self, key):
5115 return self._value[key]
5117 def _encoded_values(self):
5118 return [v.encode() for v in self._value]
5121 v = b"".join(self._encoded_values())
5122 return b"".join((self.tag, len_encode(len(v)), v))
5124 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5126 t, tlen, lv = tag_strip(tlv)
5127 except DecodeError as err:
5128 raise err.__class__(
5130 klass=self.__class__,
5131 decode_path=decode_path,
5136 klass=self.__class__,
5137 decode_path=decode_path,
5143 ctx_bered = ctx.get("bered", False)
5145 l, llen, v = len_decode(lv)
5146 except LenIndefForm as err:
5148 raise err.__class__(
5150 klass=self.__class__,
5151 decode_path=decode_path,
5154 l, llen, v = 0, 1, lv[1:]
5156 except DecodeError as err:
5157 raise err.__class__(
5159 klass=self.__class__,
5160 decode_path=decode_path,
5164 raise NotEnoughData(
5165 "encoded length is longer than data",
5166 klass=self.__class__,
5167 decode_path=decode_path,
5171 v, tail = v[:l], v[l:]
5173 sub_offset = offset + tlen + llen
5175 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5176 value_prev = memoryview(v[:0])
5180 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5182 sub_decode_path = decode_path + (str(len(_value)),)
5183 value, v_tail = spec.decode(
5187 decode_path=sub_decode_path,
5190 value_len = value.fulllen
5192 if value_prev.tobytes() > v[:value_len].tobytes():
5193 if ctx_bered or ctx_allow_unordered_set:
5197 "unordered " + self.asn1_type_name,
5198 klass=self.__class__,
5199 decode_path=sub_decode_path,
5202 value_prev = v[:value_len]
5203 _value.append(value)
5204 sub_offset += value_len
5208 obj = self.__class__(
5211 bounds=(self._bound_min, self._bound_max),
5214 default=self.default,
5215 optional=self.optional,
5216 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5218 except BoundsError as err:
5221 klass=self.__class__,
5222 decode_path=decode_path,
5226 if v[:EOC_LEN].tobytes() != EOC:
5229 klass=self.__class__,
5230 decode_path=decode_path,
5235 obj.ber_encoded = ber_encoded
5240 pp_console_row(next(self.pps())),
5241 ", ".join(repr(v) for v in self._value),
5244 def pps(self, decode_path=()):
5246 asn1_type_name=self.asn1_type_name,
5247 obj_name=self.__class__.__name__,
5248 decode_path=decode_path,
5249 optional=self.optional,
5250 default=self == self.default,
5251 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5252 expl=None if self._expl is None else tag_decode(self._expl),
5257 expl_offset=self.expl_offset if self.expled else None,
5258 expl_tlen=self.expl_tlen if self.expled else None,
5259 expl_llen=self.expl_llen if self.expled else None,
5260 expl_vlen=self.expl_vlen if self.expled else None,
5261 expl_lenindef=self.expl_lenindef,
5262 lenindef=self.lenindef,
5263 ber_encoded=self.ber_encoded,
5266 for i, value in enumerate(self._value):
5267 yield value.pps(decode_path=decode_path + (str(i),))
5268 for pp in self.pps_lenindef(decode_path):
5272 class SetOf(SequenceOf):
5273 """``SET OF`` sequence type
5275 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5278 tag_default = tag_encode(form=TagFormConstructed, num=17)
5279 asn1_type_name = "SET OF"
5282 raws = self._encoded_values()
5285 return b"".join((self.tag, len_encode(len(v)), v))
5287 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5288 return super(SetOf, self)._decode(
5294 ordering_check=True,
5298 def obj_by_path(pypath): # pragma: no cover
5299 """Import object specified as string Python path
5301 Modules must be separated from classes/functions with ``:``.
5303 >>> obj_by_path("foo.bar:Baz")
5304 <class 'foo.bar.Baz'>
5305 >>> obj_by_path("foo.bar:Baz.boo")
5306 <classmethod 'foo.bar.Baz.boo'>
5308 mod, objs = pypath.rsplit(":", 1)
5309 from importlib import import_module
5310 obj = import_module(mod)
5311 for obj_name in objs.split("."):
5312 obj = getattr(obj, obj_name)
5316 def generic_decoder(): # pragma: no cover
5317 # All of this below is a big hack with self references
5318 choice = PrimitiveTypes()
5319 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5320 choice.specs["SetOf"] = SetOf(schema=choice)
5322 choice.specs["SequenceOf%d" % i] = SequenceOf(
5326 choice.specs["Any"] = Any()
5328 # Class name equals to type name, to omit it from output
5329 class SEQUENCEOF(SequenceOf):
5337 with_decode_path=False,
5338 decode_path_only=(),
5340 def _pprint_pps(pps):
5342 if hasattr(pp, "_fields"):
5344 decode_path_only != () and
5345 pp.decode_path[:len(decode_path_only)] != decode_path_only
5348 if pp.asn1_type_name == Choice.asn1_type_name:
5350 pp_kwargs = pp._asdict()
5351 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5352 pp = _pp(**pp_kwargs)
5353 yield pp_console_row(
5358 with_colours=with_colours,
5359 with_decode_path=with_decode_path,
5360 decode_path_len_decrease=len(decode_path_only),
5362 for row in pp_console_blob(
5364 decode_path_len_decrease=len(decode_path_only),
5368 for row in _pprint_pps(pp):
5370 return "\n".join(_pprint_pps(obj.pps()))
5371 return SEQUENCEOF(), pprint_any
5374 def main(): # pragma: no cover
5376 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5377 parser.add_argument(
5381 help="Skip that number of bytes from the beginning",
5383 parser.add_argument(
5385 help="Python path to dictionary with OIDs",
5387 parser.add_argument(
5389 help="Python path to schema definition to use",
5391 parser.add_argument(
5392 "--defines-by-path",
5393 help="Python path to decoder's defines_by_path",
5395 parser.add_argument(
5397 action="store_true",
5398 help="Disallow BER encoding",
5400 parser.add_argument(
5401 "--print-decode-path",
5402 action="store_true",
5403 help="Print decode paths",
5405 parser.add_argument(
5406 "--decode-path-only",
5407 help="Print only specified decode path",
5409 parser.add_argument(
5411 action="store_true",
5412 help="Allow explicit tag out-of-bound",
5414 parser.add_argument(
5416 type=argparse.FileType("rb"),
5417 help="Path to DER file you want to decode",
5419 args = parser.parse_args()
5420 args.DERFile.seek(args.skip)
5421 der = memoryview(args.DERFile.read())
5422 args.DERFile.close()
5423 oids = obj_by_path(args.oids) if args.oids else {}
5425 schema = obj_by_path(args.schema)
5426 from functools import partial
5427 pprinter = partial(pprint, big_blobs=True)
5429 schema, pprinter = generic_decoder()
5431 "bered": not args.nobered,
5432 "allow_expl_oob": args.allow_expl_oob,
5434 if args.defines_by_path is not None:
5435 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5436 obj, tail = schema().decode(der, ctx=ctx)
5440 with_colours=True if environ.get("NO_COLOR") is None else False,
5441 with_decode_path=args.print_decode_path,
5443 () if args.decode_path_only is None else
5444 tuple(args.decode_path_only.split(":"))
5448 print("\nTrailing data: %s" % hexenc(tail))
5451 if __name__ == "__main__":