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:`bered <bered_ctx>`
217 * :ref:`defines_by_path <defines_by_path_ctx>`
218 * :ref:`strict_default_existence <strict_default_existence_ctx>`
225 All objects have ``pps()`` method, that is a generator of
226 :py:class:`pyderasn.PP` namedtuple, holding various raw information
227 about the object. If ``pps`` is called on sequences, then all underlying
228 ``PP`` will be yielded.
230 You can use :py:func:`pyderasn.pp_console_row` function, converting
231 those ``PP`` to human readable string. Actually exactly it is used for
232 all object ``repr``. But it is easy to write custom formatters.
234 >>> from pyderasn import pprint
235 >>> encoded = Integer(-12345).encode()
236 >>> obj, tail = Integer().decode(encoded)
237 >>> print(pprint(obj))
238 0 [1,1, 2] INTEGER -12345
245 ASN.1 structures often have ANY and OCTET STRING fields, that are
246 DEFINED BY some previously met ObjectIdentifier. This library provides
247 ability to specify mapping between some OID and field that must be
248 decoded with specific specification.
253 :py:class:`pyderasn.ObjectIdentifier` field inside
254 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
255 necessary for decoding structures. For example, CMS (:rfc:`5652`)
258 class ContentInfo(Sequence):
260 ("contentType", ContentType(defines=((("content",), {
261 id_digestedData: DigestedData(),
262 id_signedData: SignedData(),
264 ("content", Any(expl=tag_ctxc(0))),
267 ``contentType`` field tells that it defines that ``content`` must be
268 decoded with ``SignedData`` specification, if ``contentType`` equals to
269 ``id-signedData``. The same applies to ``DigestedData``. If
270 ``contentType`` contains unknown OID, then no automatic decoding is
273 You can specify multiple fields, that will be autodecoded -- that is why
274 ``defines`` kwarg is a sequence. You can specify defined field
275 relatively or absolutely to current decode path. For example ``defines``
276 for AlgorithmIdentifier of X.509's
277 ``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
281 id_ecPublicKey: ECParameters(),
282 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
284 (("..", "subjectPublicKey"), {
285 id_rsaEncryption: RSAPublicKey(),
286 id_GostR3410_2001: OctetString(),
290 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
291 autodecode its parameters inside SPKI's algorithm and its public key
294 Following types can be automatically decoded (DEFINED BY):
296 * :py:class:`pyderasn.Any`
297 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
298 * :py:class:`pyderasn.OctetString`
299 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
300 ``Any``/``BitString``/``OctetString``-s
302 When any of those fields is automatically decoded, then ``.defined``
303 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
304 was defined, ``value`` contains corresponding decoded value. For example
305 above, ``content_info["content"].defined == (id_signedData,
308 .. _defines_by_path_ctx:
310 defines_by_path context option
311 ______________________________
313 Sometimes you either can not or do not want to explicitly set *defines*
314 in the scheme. You can dynamically apply those definitions when calling
315 ``.decode()`` method.
317 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
318 value must be sequence of following tuples::
320 (decode_path, defines)
322 where ``decode_path`` is a tuple holding so-called decode path to the
323 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
324 ``defines``, holding exactly the same value as accepted in its keyword
327 For example, again for CMS, you want to automatically decode
328 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
329 structures it may hold. Also, automatically decode ``controlSequence``
332 content_info, tail = ContentInfo().decode(data, defines_by_path=(
335 ((("content",), {id_signedData: SignedData()}),),
340 DecodePathDefBy(id_signedData),
345 id_cct_PKIData: PKIData(),
346 id_cct_PKIResponse: PKIResponse(),
352 DecodePathDefBy(id_signedData),
355 DecodePathDefBy(id_cct_PKIResponse),
361 id_cmc_recipientNonce: RecipientNonce(),
362 id_cmc_senderNonce: SenderNonce(),
363 id_cmc_statusInfoV2: CMCStatusInfoV2(),
364 id_cmc_transactionId: TransactionId(),
369 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
370 First function is useful for path construction when some automatic
371 decoding is already done. ``any`` means literally any value it meet --
372 useful for SEQUENCE/SET OF-s.
379 By default PyDERASN accepts only DER encoded data. It always encodes to
380 DER. But you can optionally enable BER decoding with setting ``bered``
381 :ref:`context <ctx>` argument to True. Indefinite lengths and
382 constructed primitive types should be parsed successfully.
384 * If object is encoded in BER form (not the DER one), then ``bered``
385 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
386 STRING`` can contain it.
387 * If object has an indefinite length encoding, then its ``lenindef``
388 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
389 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
391 * If object has an indefinite length encoded explicit tag, then
392 ``expl_lenindef`` is set to True.
394 EOC (end-of-contents) token's length is taken in advance in object's
402 .. autoclass:: pyderasn.Boolean
407 .. autoclass:: pyderasn.Integer
412 .. autoclass:: pyderasn.BitString
417 .. autoclass:: pyderasn.OctetString
422 .. autoclass:: pyderasn.Null
427 .. autoclass:: pyderasn.ObjectIdentifier
432 .. autoclass:: pyderasn.Enumerated
436 .. autoclass:: pyderasn.CommonString
440 .. autoclass:: pyderasn.NumericString
444 .. autoclass:: pyderasn.UTCTime
445 :members: __init__, todatetime
449 .. autoclass:: pyderasn.GeneralizedTime
456 .. autoclass:: pyderasn.Choice
461 .. autoclass:: PrimitiveTypes
465 .. autoclass:: pyderasn.Any
473 .. autoclass:: pyderasn.Sequence
478 .. autoclass:: pyderasn.Set
483 .. autoclass:: pyderasn.SequenceOf
488 .. autoclass:: pyderasn.SetOf
494 .. autofunction:: pyderasn.abs_decode_path
495 .. autofunction:: pyderasn.hexenc
496 .. autofunction:: pyderasn.hexdec
497 .. autofunction:: pyderasn.tag_encode
498 .. autofunction:: pyderasn.tag_decode
499 .. autofunction:: pyderasn.tag_ctxp
500 .. autofunction:: pyderasn.tag_ctxc
501 .. autoclass:: pyderasn.Obj
502 .. autoclass:: pyderasn.DecodeError
504 .. autoclass:: pyderasn.NotEnoughData
505 .. autoclass:: pyderasn.LenIndefForm
506 .. autoclass:: pyderasn.TagMismatch
507 .. autoclass:: pyderasn.InvalidLength
508 .. autoclass:: pyderasn.InvalidOID
509 .. autoclass:: pyderasn.ObjUnknown
510 .. autoclass:: pyderasn.ObjNotReady
511 .. autoclass:: pyderasn.InvalidValueType
512 .. autoclass:: pyderasn.BoundsError
515 from codecs import getdecoder
516 from codecs import getencoder
517 from collections import namedtuple
518 from collections import OrderedDict
519 from datetime import datetime
520 from math import ceil
521 from os import environ
522 from string import digits
524 from six import add_metaclass
525 from six import binary_type
526 from six import byte2int
527 from six import indexbytes
528 from six import int2byte
529 from six import integer_types
530 from six import iterbytes
532 from six import string_types
533 from six import text_type
534 from six.moves import xrange as six_xrange
538 from termcolor import colored
540 def colored(what, *args):
584 "TagClassApplication",
588 "TagFormConstructed",
599 TagClassUniversal = 0
600 TagClassApplication = 1 << 6
601 TagClassContext = 1 << 7
602 TagClassPrivate = 1 << 6 | 1 << 7
604 TagFormConstructed = 1 << 5
607 TagClassApplication: "APPLICATION ",
608 TagClassPrivate: "PRIVATE ",
609 TagClassUniversal: "UNIV ",
613 LENINDEF = b"\x80" # length indefinite mark
614 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
617 ########################################################################
619 ########################################################################
621 class DecodeError(Exception):
622 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
624 :param str msg: reason of decode failing
625 :param klass: optional exact DecodeError inherited class (like
626 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
627 :py:exc:`InvalidLength`)
628 :param decode_path: tuple of strings. It contains human
629 readable names of the fields through which
630 decoding process has passed
631 :param int offset: binary offset where failure happened
633 super(DecodeError, self).__init__()
636 self.decode_path = decode_path
642 "" if self.klass is None else self.klass.__name__,
644 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
645 if len(self.decode_path) > 0 else ""
647 ("(at %d)" % self.offset) if self.offset > 0 else "",
653 return "%s(%s)" % (self.__class__.__name__, self)
656 class NotEnoughData(DecodeError):
660 class LenIndefForm(DecodeError):
664 class TagMismatch(DecodeError):
668 class InvalidLength(DecodeError):
672 class InvalidOID(DecodeError):
676 class ObjUnknown(ValueError):
677 def __init__(self, name):
678 super(ObjUnknown, self).__init__()
682 return "object is unknown: %s" % self.name
685 return "%s(%s)" % (self.__class__.__name__, self)
688 class ObjNotReady(ValueError):
689 def __init__(self, name):
690 super(ObjNotReady, self).__init__()
694 return "object is not ready: %s" % self.name
697 return "%s(%s)" % (self.__class__.__name__, self)
700 class InvalidValueType(ValueError):
701 def __init__(self, expected_types):
702 super(InvalidValueType, self).__init__()
703 self.expected_types = expected_types
706 return "invalid value type, expected: %s" % ", ".join(
707 [repr(t) for t in self.expected_types]
711 return "%s(%s)" % (self.__class__.__name__, self)
714 class BoundsError(ValueError):
715 def __init__(self, bound_min, value, bound_max):
716 super(BoundsError, self).__init__()
717 self.bound_min = bound_min
719 self.bound_max = bound_max
722 return "unsatisfied bounds: %s <= %s <= %s" % (
729 return "%s(%s)" % (self.__class__.__name__, self)
732 ########################################################################
734 ########################################################################
736 _hexdecoder = getdecoder("hex")
737 _hexencoder = getencoder("hex")
741 """Binary data to hexadecimal string convert
743 return _hexdecoder(data)[0]
747 """Hexadecimal string to binary data convert
749 return _hexencoder(data)[0].decode("ascii")
752 def int_bytes_len(num, byte_len=8):
755 return int(ceil(float(num.bit_length()) / byte_len))
758 def zero_ended_encode(num):
759 octets = bytearray(int_bytes_len(num, 7))
761 octets[i] = num & 0x7F
765 octets[i] = 0x80 | (num & 0x7F)
771 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
772 """Encode tag to binary form
774 :param int num: tag's number
775 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
776 :py:data:`pyderasn.TagClassContext`,
777 :py:data:`pyderasn.TagClassApplication`,
778 :py:data:`pyderasn.TagClassPrivate`)
779 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
780 :py:data:`pyderasn.TagFormConstructed`)
784 return int2byte(klass | form | num)
785 # [XX|X|11111][1.......][1.......] ... [0.......]
786 return int2byte(klass | form | 31) + zero_ended_encode(num)
790 """Decode tag from binary form
794 No validation is performed, assuming that it has already passed.
796 It returns tuple with three integers, as
797 :py:func:`pyderasn.tag_encode` accepts.
799 first_octet = byte2int(tag)
800 klass = first_octet & 0xC0
801 form = first_octet & 0x20
802 if first_octet & 0x1F < 0x1F:
803 return (klass, form, first_octet & 0x1F)
805 for octet in iterbytes(tag[1:]):
808 return (klass, form, num)
812 """Create CONTEXT PRIMITIVE tag
814 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
818 """Create CONTEXT CONSTRUCTED tag
820 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
824 """Take off tag from the data
826 :returns: (encoded tag, tag length, remaining data)
829 raise NotEnoughData("no data at all")
830 if byte2int(data) & 0x1F < 31:
831 return data[:1], 1, data[1:]
836 raise DecodeError("unfinished tag")
837 if indexbytes(data, i) & 0x80 == 0:
840 return data[:i], i, data[i:]
846 octets = bytearray(int_bytes_len(l) + 1)
847 octets[0] = 0x80 | (len(octets) - 1)
848 for i in six_xrange(len(octets) - 1, 0, -1):
854 def len_decode(data):
857 :returns: (decoded length, length's length, remaining data)
858 :raises LenIndefForm: if indefinite form encoding is met
861 raise NotEnoughData("no data at all")
862 first_octet = byte2int(data)
863 if first_octet & 0x80 == 0:
864 return first_octet, 1, data[1:]
865 octets_num = first_octet & 0x7F
866 if octets_num + 1 > len(data):
867 raise NotEnoughData("encoded length is longer than data")
870 if byte2int(data[1:]) == 0:
871 raise DecodeError("leading zeros")
873 for v in iterbytes(data[1:1 + octets_num]):
876 raise DecodeError("long form instead of short one")
877 return l, 1 + octets_num, data[1 + octets_num:]
880 ########################################################################
882 ########################################################################
884 class AutoAddSlots(type):
885 def __new__(mcs, name, bases, _dict):
886 _dict["__slots__"] = _dict.get("__slots__", ())
887 return type.__new__(mcs, name, bases, _dict)
890 @add_metaclass(AutoAddSlots)
892 """Common ASN.1 object class
894 All ASN.1 types are inherited from it. It has metaclass that
895 automatically adds ``__slots__`` to all inherited classes.
919 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
920 self._expl = getattr(self, "expl", None) if expl is None else expl
921 if self.tag != self.tag_default and self._expl is not None:
922 raise ValueError("implicit and explicit tags can not be set simultaneously")
923 if default is not None:
925 self.optional = optional
926 self.offset, self.llen, self.vlen = _decoded
928 self.expl_lenindef = False
929 self.lenindef = False
933 def ready(self): # pragma: no cover
934 """Is object ready to be encoded?
936 raise NotImplementedError()
938 def _assert_ready(self):
940 raise ObjNotReady(self.__class__.__name__)
944 """Is object decoded?
946 return (self.llen + self.vlen) > 0
948 def copy(self): # pragma: no cover
949 """Make a copy of object, safe to be mutated
951 raise NotImplementedError()
959 return self.tlen + self.llen + self.vlen
961 def __str__(self): # pragma: no cover
962 return self.__bytes__() if PY2 else self.__unicode__()
964 def __ne__(self, their):
965 return not(self == their)
967 def __gt__(self, their): # pragma: no cover
968 return not(self < their)
970 def __le__(self, their): # pragma: no cover
971 return (self == their) or (self < their)
973 def __ge__(self, their): # pragma: no cover
974 return (self == their) or (self > their)
976 def _encode(self): # pragma: no cover
977 raise NotImplementedError()
979 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
980 raise NotImplementedError()
984 if self._expl is None:
986 return b"".join((self._expl, len_encode(len(raw)), raw))
999 :param data: either binary or memoryview
1000 :param int offset: initial data's offset
1001 :param bool leavemm: do we need to leave memoryview of remaining
1002 data as is, or convert it to bytes otherwise
1003 :param ctx: optional :ref:`context <ctx>` governing decoding process.
1004 :param tag_only: decode only the tag, without length and contents
1005 (used only in Choice and Set structures, trying to
1006 determine if tag satisfies the scheme)
1007 :returns: (Obj, remaining data)
1011 tlv = memoryview(data)
1012 if self._expl is None:
1013 result = self._decode(
1016 decode_path=decode_path,
1025 t, tlen, lv = tag_strip(tlv)
1026 except DecodeError as err:
1027 raise err.__class__(
1029 klass=self.__class__,
1030 decode_path=decode_path,
1035 klass=self.__class__,
1036 decode_path=decode_path,
1040 l, llen, v = len_decode(lv)
1041 except LenIndefForm as err:
1042 if not ctx.get("bered", False):
1043 raise err.__class__(
1045 klass=self.__class__,
1046 decode_path=decode_path,
1050 offset += tlen + llen
1051 result = self._decode(
1054 decode_path=decode_path,
1061 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1062 if eoc_expected.tobytes() != EOC:
1065 klass=self.__class__,
1066 decode_path=decode_path,
1070 obj.expl_lenindef = True
1071 except DecodeError as err:
1072 raise err.__class__(
1074 klass=self.__class__,
1075 decode_path=decode_path,
1080 raise NotEnoughData(
1081 "encoded length is longer than data",
1082 klass=self.__class__,
1083 decode_path=decode_path,
1086 result = self._decode(
1088 offset=offset + tlen + llen,
1089 decode_path=decode_path,
1096 return obj, (tail if leavemm else tail.tobytes())
1100 return self._expl is not None
1107 def expl_tlen(self):
1108 return len(self._expl)
1111 def expl_llen(self):
1112 if self.expl_lenindef:
1114 return len(len_encode(self.tlvlen))
1117 def expl_offset(self):
1118 return self.offset - self.expl_tlen - self.expl_llen
1121 def expl_vlen(self):
1125 def expl_tlvlen(self):
1126 return self.expl_tlen + self.expl_llen + self.expl_vlen
1129 def fulloffset(self):
1130 return self.expl_offset if self.expled else self.offset
1134 return self.expl_tlvlen if self.expled else self.tlvlen
1136 def pps_lenindef(self, decode_path):
1139 asn1_type_name="EOC",
1141 decode_path=decode_path,
1143 self.offset + self.tlvlen -
1144 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1151 if self.expl_lenindef:
1153 asn1_type_name="EOC",
1154 obj_name="EXPLICIT",
1155 decode_path=decode_path,
1156 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1164 class DecodePathDefBy(object):
1165 """DEFINED BY representation inside decode path
1167 __slots__ = ("defined_by",)
1169 def __init__(self, defined_by):
1170 self.defined_by = defined_by
1172 def __ne__(self, their):
1173 return not(self == their)
1175 def __eq__(self, their):
1176 if not isinstance(their, self.__class__):
1178 return self.defined_by == their.defined_by
1181 return "DEFINED BY " + str(self.defined_by)
1184 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1187 ########################################################################
1189 ########################################################################
1191 PP = namedtuple("PP", (
1216 asn1_type_name="unknown",
1233 expl_lenindef=False,
1261 def _colorize(what, colour, with_colours, attrs=("bold",)):
1262 return colored(what, colour, attrs=attrs) if with_colours else what
1277 " " if pp.expl_offset is None else
1278 ("-%d" % (pp.offset - pp.expl_offset))
1280 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1282 cols.append(_colorize(col, "red", with_colours, ()))
1283 col = "[%d,%d,%4d]%s" % (
1287 LENINDEF_PP_CHAR if pp.lenindef else " "
1289 col = _colorize(col, "green", with_colours, ())
1291 if len(pp.decode_path) > 0:
1292 cols.append(" ." * (len(pp.decode_path)))
1293 ent = pp.decode_path[-1]
1294 if isinstance(ent, DecodePathDefBy):
1295 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1296 value = str(ent.defined_by)
1298 oids is not None and
1299 ent.defined_by.asn1_type_name ==
1300 ObjectIdentifier.asn1_type_name and
1303 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1305 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1307 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1308 if pp.expl is not None:
1309 klass, _, num = pp.expl
1310 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1311 cols.append(_colorize(col, "blue", with_colours))
1312 if pp.impl is not None:
1313 klass, _, num = pp.impl
1314 col = "[%s%d]" % (TagClassReprs[klass], num)
1315 cols.append(_colorize(col, "blue", with_colours))
1316 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1317 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1319 cols.append(_colorize("BER", "red", with_colours))
1320 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1321 if pp.value is not None:
1323 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1325 oids is not None and
1326 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1329 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1331 if isinstance(pp.blob, binary_type):
1332 cols.append(hexenc(pp.blob))
1333 elif isinstance(pp.blob, tuple):
1334 cols.append(", ".join(pp.blob))
1336 cols.append(_colorize("OPTIONAL", "red", with_colours))
1338 cols.append(_colorize("DEFAULT", "red", with_colours))
1339 return " ".join(cols)
1342 def pp_console_blob(pp):
1343 cols = [" " * len("XXXXXYYZ [X,X,XXXX]Z")]
1344 if len(pp.decode_path) > 0:
1345 cols.append(" ." * (len(pp.decode_path) + 1))
1346 if isinstance(pp.blob, binary_type):
1347 blob = hexenc(pp.blob).upper()
1348 for i in range(0, len(blob), 32):
1349 chunk = blob[i:i + 32]
1350 yield " ".join(cols + [":".join(
1351 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1353 elif isinstance(pp.blob, tuple):
1354 yield " ".join(cols + [", ".join(pp.blob)])
1357 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1358 """Pretty print object
1360 :param Obj obj: object you want to pretty print
1361 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1362 from it is met, then its humand readable form is printed
1363 :param big_blobs: if large binary objects are met (like OctetString
1364 values), do we need to print them too, on separate
1366 :param with_colours: colourize output, if ``termcolor`` library
1369 def _pprint_pps(pps):
1371 if hasattr(pp, "_fields"):
1373 yield pp_console_row(
1378 with_colours=with_colours,
1380 for row in pp_console_blob(pp):
1383 yield pp_console_row(
1388 with_colours=with_colours,
1391 for row in _pprint_pps(pp):
1393 return "\n".join(_pprint_pps(obj.pps()))
1396 ########################################################################
1397 # ASN.1 primitive types
1398 ########################################################################
1401 """``BOOLEAN`` boolean type
1403 >>> b = Boolean(True)
1405 >>> b == Boolean(True)
1411 tag_default = tag_encode(1)
1412 asn1_type_name = "BOOLEAN"
1424 :param value: set the value. Either boolean type, or
1425 :py:class:`pyderasn.Boolean` object
1426 :param bytes impl: override default tag with ``IMPLICIT`` one
1427 :param bytes expl: override default tag with ``EXPLICIT`` one
1428 :param default: set default value. Type same as in ``value``
1429 :param bool optional: is object ``OPTIONAL`` in sequence
1431 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1432 self._value = None if value is None else self._value_sanitize(value)
1433 if default is not None:
1434 default = self._value_sanitize(default)
1435 self.default = self.__class__(
1441 self._value = default
1443 def _value_sanitize(self, value):
1444 if issubclass(value.__class__, Boolean):
1446 if isinstance(value, bool):
1448 raise InvalidValueType((self.__class__, bool))
1452 return self._value is not None
1455 obj = self.__class__()
1456 obj._value = self._value
1458 obj._expl = self._expl
1459 obj.default = self.default
1460 obj.optional = self.optional
1461 obj.offset = self.offset
1462 obj.llen = self.llen
1463 obj.vlen = self.vlen
1466 def __nonzero__(self):
1467 self._assert_ready()
1471 self._assert_ready()
1474 def __eq__(self, their):
1475 if isinstance(their, bool):
1476 return self._value == their
1477 if not issubclass(their.__class__, Boolean):
1480 self._value == their._value and
1481 self.tag == their.tag and
1482 self._expl == their._expl
1493 return self.__class__(
1495 impl=self.tag if impl is None else impl,
1496 expl=self._expl if expl is None else expl,
1497 default=self.default if default is None else default,
1498 optional=self.optional if optional is None else optional,
1502 self._assert_ready()
1506 (b"\xFF" if self._value else b"\x00"),
1509 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1511 t, _, lv = tag_strip(tlv)
1512 except DecodeError as err:
1513 raise err.__class__(
1515 klass=self.__class__,
1516 decode_path=decode_path,
1521 klass=self.__class__,
1522 decode_path=decode_path,
1528 l, _, v = len_decode(lv)
1529 except DecodeError as err:
1530 raise err.__class__(
1532 klass=self.__class__,
1533 decode_path=decode_path,
1537 raise InvalidLength(
1538 "Boolean's length must be equal to 1",
1539 klass=self.__class__,
1540 decode_path=decode_path,
1544 raise NotEnoughData(
1545 "encoded length is longer than data",
1546 klass=self.__class__,
1547 decode_path=decode_path,
1550 first_octet = byte2int(v)
1552 if first_octet == 0:
1554 elif first_octet == 0xFF:
1556 elif ctx.get("bered", False):
1561 "unacceptable Boolean value",
1562 klass=self.__class__,
1563 decode_path=decode_path,
1566 obj = self.__class__(
1570 default=self.default,
1571 optional=self.optional,
1572 _decoded=(offset, 1, 1),
1578 return pp_console_row(next(self.pps()))
1580 def pps(self, decode_path=()):
1582 asn1_type_name=self.asn1_type_name,
1583 obj_name=self.__class__.__name__,
1584 decode_path=decode_path,
1585 value=str(self._value) if self.ready else None,
1586 optional=self.optional,
1587 default=self == self.default,
1588 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1589 expl=None if self._expl is None else tag_decode(self._expl),
1594 expl_offset=self.expl_offset if self.expled else None,
1595 expl_tlen=self.expl_tlen if self.expled else None,
1596 expl_llen=self.expl_llen if self.expled else None,
1597 expl_vlen=self.expl_vlen if self.expled else None,
1598 expl_lenindef=self.expl_lenindef,
1601 for pp in self.pps_lenindef(decode_path):
1606 """``INTEGER`` integer type
1608 >>> b = Integer(-123)
1610 >>> b == Integer(-123)
1615 >>> Integer(2, bounds=(1, 3))
1617 >>> Integer(5, bounds=(1, 3))
1618 Traceback (most recent call last):
1619 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1623 class Version(Integer):
1630 >>> v = Version("v1")
1637 {'v3': 2, 'v1': 0, 'v2': 1}
1639 __slots__ = ("specs", "_bound_min", "_bound_max")
1640 tag_default = tag_encode(2)
1641 asn1_type_name = "INTEGER"
1655 :param value: set the value. Either integer type, named value
1656 (if ``schema`` is specified in the class), or
1657 :py:class:`pyderasn.Integer` object
1658 :param bounds: set ``(MIN, MAX)`` value constraint.
1659 (-inf, +inf) by default
1660 :param bytes impl: override default tag with ``IMPLICIT`` one
1661 :param bytes expl: override default tag with ``EXPLICIT`` one
1662 :param default: set default value. Type same as in ``value``
1663 :param bool optional: is object ``OPTIONAL`` in sequence
1665 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1667 specs = getattr(self, "schema", {}) if _specs is None else _specs
1668 self.specs = specs if isinstance(specs, dict) else dict(specs)
1669 self._bound_min, self._bound_max = getattr(
1672 (float("-inf"), float("+inf")),
1673 ) if bounds is None else bounds
1674 if value is not None:
1675 self._value = self._value_sanitize(value)
1676 if default is not None:
1677 default = self._value_sanitize(default)
1678 self.default = self.__class__(
1684 if self._value is None:
1685 self._value = default
1687 def _value_sanitize(self, value):
1688 if issubclass(value.__class__, Integer):
1689 value = value._value
1690 elif isinstance(value, integer_types):
1692 elif isinstance(value, str):
1693 value = self.specs.get(value)
1695 raise ObjUnknown("integer value: %s" % value)
1697 raise InvalidValueType((self.__class__, int, str))
1698 if not self._bound_min <= value <= self._bound_max:
1699 raise BoundsError(self._bound_min, value, self._bound_max)
1704 return self._value is not None
1707 obj = self.__class__(_specs=self.specs)
1708 obj._value = self._value
1709 obj._bound_min = self._bound_min
1710 obj._bound_max = self._bound_max
1712 obj._expl = self._expl
1713 obj.default = self.default
1714 obj.optional = self.optional
1715 obj.offset = self.offset
1716 obj.llen = self.llen
1717 obj.vlen = self.vlen
1721 self._assert_ready()
1722 return int(self._value)
1725 self._assert_ready()
1728 bytes(self._expl or b"") +
1729 str(self._value).encode("ascii"),
1732 def __eq__(self, their):
1733 if isinstance(their, integer_types):
1734 return self._value == their
1735 if not issubclass(their.__class__, Integer):
1738 self._value == their._value and
1739 self.tag == their.tag and
1740 self._expl == their._expl
1743 def __lt__(self, their):
1744 return self._value < their._value
1748 for name, value in self.specs.items():
1749 if value == self._value:
1761 return self.__class__(
1764 (self._bound_min, self._bound_max)
1765 if bounds is None else bounds
1767 impl=self.tag if impl is None else impl,
1768 expl=self._expl if expl is None else expl,
1769 default=self.default if default is None else default,
1770 optional=self.optional if optional is None else optional,
1775 self._assert_ready()
1779 octets = bytearray([0])
1783 octets = bytearray()
1785 octets.append((value & 0xFF) ^ 0xFF)
1787 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1790 octets = bytearray()
1792 octets.append(value & 0xFF)
1794 if octets[-1] & 0x80 > 0:
1797 octets = bytes(octets)
1799 bytes_len = ceil(value.bit_length() / 8) or 1
1802 octets = value.to_bytes(
1807 except OverflowError:
1811 return b"".join((self.tag, len_encode(len(octets)), octets))
1813 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1815 t, _, lv = tag_strip(tlv)
1816 except DecodeError as err:
1817 raise err.__class__(
1819 klass=self.__class__,
1820 decode_path=decode_path,
1825 klass=self.__class__,
1826 decode_path=decode_path,
1832 l, llen, v = len_decode(lv)
1833 except DecodeError as err:
1834 raise err.__class__(
1836 klass=self.__class__,
1837 decode_path=decode_path,
1841 raise NotEnoughData(
1842 "encoded length is longer than data",
1843 klass=self.__class__,
1844 decode_path=decode_path,
1848 raise NotEnoughData(
1850 klass=self.__class__,
1851 decode_path=decode_path,
1854 v, tail = v[:l], v[l:]
1855 first_octet = byte2int(v)
1857 second_octet = byte2int(v[1:])
1859 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1860 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1863 "non normalized integer",
1864 klass=self.__class__,
1865 decode_path=decode_path,
1870 if first_octet & 0x80 > 0:
1871 octets = bytearray()
1872 for octet in bytearray(v):
1873 octets.append(octet ^ 0xFF)
1874 for octet in octets:
1875 value = (value << 8) | octet
1879 for octet in bytearray(v):
1880 value = (value << 8) | octet
1882 value = int.from_bytes(v, byteorder="big", signed=True)
1884 obj = self.__class__(
1886 bounds=(self._bound_min, self._bound_max),
1889 default=self.default,
1890 optional=self.optional,
1892 _decoded=(offset, llen, l),
1894 except BoundsError as err:
1897 klass=self.__class__,
1898 decode_path=decode_path,
1904 return pp_console_row(next(self.pps()))
1906 def pps(self, decode_path=()):
1908 asn1_type_name=self.asn1_type_name,
1909 obj_name=self.__class__.__name__,
1910 decode_path=decode_path,
1911 value=(self.named or str(self._value)) if self.ready else None,
1912 optional=self.optional,
1913 default=self == self.default,
1914 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1915 expl=None if self._expl is None else tag_decode(self._expl),
1920 expl_offset=self.expl_offset if self.expled else None,
1921 expl_tlen=self.expl_tlen if self.expled else None,
1922 expl_llen=self.expl_llen if self.expled else None,
1923 expl_vlen=self.expl_vlen if self.expled else None,
1924 expl_lenindef=self.expl_lenindef,
1926 for pp in self.pps_lenindef(decode_path):
1930 class BitString(Obj):
1931 """``BIT STRING`` bit string type
1933 >>> BitString(b"hello world")
1934 BIT STRING 88 bits 68656c6c6f20776f726c64
1937 >>> b == b"hello world"
1942 >>> BitString("'0A3B5F291CD'H")
1943 BIT STRING 44 bits 0a3b5f291cd0
1944 >>> b = BitString("'010110000000'B")
1945 BIT STRING 12 bits 5800
1948 >>> b[0], b[1], b[2], b[3]
1949 (False, True, False, True)
1953 [False, True, False, True, True, False, False, False, False, False, False, False]
1957 class KeyUsage(BitString):
1959 ("digitalSignature", 0),
1960 ("nonRepudiation", 1),
1961 ("keyEncipherment", 2),
1964 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1965 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1967 ['nonRepudiation', 'keyEncipherment']
1969 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1973 Pay attention that BIT STRING can be encoded both in primitive
1974 and constructed forms. Decoder always checks constructed form tag
1975 additionally to specified primitive one. If BER decoding is
1976 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
1977 of DER restrictions.
1979 __slots__ = ("tag_constructed", "specs", "defined")
1980 tag_default = tag_encode(3)
1981 asn1_type_name = "BIT STRING"
1994 :param value: set the value. Either binary type, tuple of named
1995 values (if ``schema`` is specified in the class),
1996 string in ``'XXX...'B`` form, or
1997 :py:class:`pyderasn.BitString` object
1998 :param bytes impl: override default tag with ``IMPLICIT`` one
1999 :param bytes expl: override default tag with ``EXPLICIT`` one
2000 :param default: set default value. Type same as in ``value``
2001 :param bool optional: is object ``OPTIONAL`` in sequence
2003 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2004 specs = getattr(self, "schema", {}) if _specs is None else _specs
2005 self.specs = specs if isinstance(specs, dict) else dict(specs)
2006 self._value = None if value is None else self._value_sanitize(value)
2007 if default is not None:
2008 default = self._value_sanitize(default)
2009 self.default = self.__class__(
2015 self._value = default
2017 tag_klass, _, tag_num = tag_decode(self.tag)
2018 self.tag_constructed = tag_encode(
2020 form=TagFormConstructed,
2024 def _bits2octets(self, bits):
2025 if len(self.specs) > 0:
2026 bits = bits.rstrip("0")
2028 bits += "0" * ((8 - (bit_len % 8)) % 8)
2029 octets = bytearray(len(bits) // 8)
2030 for i in six_xrange(len(octets)):
2031 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2032 return bit_len, bytes(octets)
2034 def _value_sanitize(self, value):
2035 if issubclass(value.__class__, BitString):
2037 if isinstance(value, (string_types, binary_type)):
2039 isinstance(value, string_types) and
2040 value.startswith("'")
2042 if value.endswith("'B"):
2044 if not set(value) <= set(("0", "1")):
2045 raise ValueError("B's coding contains unacceptable chars")
2046 return self._bits2octets(value)
2047 elif value.endswith("'H"):
2051 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2053 if isinstance(value, binary_type):
2054 return (len(value) * 8, value)
2056 raise InvalidValueType((self.__class__, string_types, binary_type))
2057 if isinstance(value, tuple):
2060 isinstance(value[0], integer_types) and
2061 isinstance(value[1], binary_type)
2066 bit = self.specs.get(name)
2068 raise ObjUnknown("BitString value: %s" % name)
2071 return self._bits2octets("")
2073 return self._bits2octets("".join(
2074 ("1" if bit in bits else "0")
2075 for bit in six_xrange(max(bits) + 1)
2077 raise InvalidValueType((self.__class__, binary_type, string_types))
2081 return self._value is not None
2084 obj = self.__class__(_specs=self.specs)
2086 if value is not None:
2087 value = (value[0], value[1])
2090 obj._expl = self._expl
2091 obj.default = self.default
2092 obj.optional = self.optional
2093 obj.offset = self.offset
2094 obj.llen = self.llen
2095 obj.vlen = self.vlen
2099 self._assert_ready()
2100 for i in six_xrange(self._value[0]):
2105 self._assert_ready()
2106 return self._value[0]
2108 def __bytes__(self):
2109 self._assert_ready()
2110 return self._value[1]
2112 def __eq__(self, their):
2113 if isinstance(their, bytes):
2114 return self._value[1] == their
2115 if not issubclass(their.__class__, BitString):
2118 self._value == their._value and
2119 self.tag == their.tag and
2120 self._expl == their._expl
2125 return [name for name, bit in self.specs.items() if self[bit]]
2135 return self.__class__(
2137 impl=self.tag if impl is None else impl,
2138 expl=self._expl if expl is None else expl,
2139 default=self.default if default is None else default,
2140 optional=self.optional if optional is None else optional,
2144 def __getitem__(self, key):
2145 if isinstance(key, int):
2146 bit_len, octets = self._value
2150 byte2int(memoryview(octets)[key // 8:]) >>
2153 if isinstance(key, string_types):
2154 value = self.specs.get(key)
2156 raise ObjUnknown("BitString value: %s" % key)
2158 raise InvalidValueType((int, str))
2161 self._assert_ready()
2162 bit_len, octets = self._value
2165 len_encode(len(octets) + 1),
2166 int2byte((8 - bit_len % 8) % 8),
2170 def _decode_chunk(self, lv, offset, decode_path, ctx):
2172 l, llen, v = len_decode(lv)
2173 except DecodeError as err:
2174 raise err.__class__(
2176 klass=self.__class__,
2177 decode_path=decode_path,
2181 raise NotEnoughData(
2182 "encoded length is longer than data",
2183 klass=self.__class__,
2184 decode_path=decode_path,
2188 raise NotEnoughData(
2190 klass=self.__class__,
2191 decode_path=decode_path,
2194 pad_size = byte2int(v)
2195 if l == 1 and pad_size != 0:
2197 "invalid empty value",
2198 klass=self.__class__,
2199 decode_path=decode_path,
2205 klass=self.__class__,
2206 decode_path=decode_path,
2209 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2212 klass=self.__class__,
2213 decode_path=decode_path,
2216 v, tail = v[:l], v[l:]
2217 obj = self.__class__(
2218 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2221 default=self.default,
2222 optional=self.optional,
2224 _decoded=(offset, llen, l),
2228 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2230 t, tlen, lv = tag_strip(tlv)
2231 except DecodeError as err:
2232 raise err.__class__(
2234 klass=self.__class__,
2235 decode_path=decode_path,
2241 return self._decode_chunk(lv, offset, decode_path, ctx)
2242 if t == self.tag_constructed:
2243 if not ctx.get("bered", False):
2245 "unallowed BER constructed encoding",
2246 klass=self.__class__,
2247 decode_path=decode_path,
2254 l, llen, v = len_decode(lv)
2255 except LenIndefForm:
2256 llen, l, v = 1, 0, lv[1:]
2258 except DecodeError as err:
2259 raise err.__class__(
2261 klass=self.__class__,
2262 decode_path=decode_path,
2266 raise NotEnoughData(
2267 "encoded length is longer than data",
2268 klass=self.__class__,
2269 decode_path=decode_path,
2272 if not lenindef and l == 0:
2273 raise NotEnoughData(
2275 klass=self.__class__,
2276 decode_path=decode_path,
2280 sub_offset = offset + tlen + llen
2284 if v[:EOC_LEN].tobytes() == EOC:
2291 "chunk out of bounds",
2292 klass=self.__class__,
2293 decode_path=decode_path + (str(len(chunks) - 1),),
2294 offset=chunks[-1].offset,
2296 sub_decode_path = decode_path + (str(len(chunks)),)
2298 chunk, v_tail = BitString().decode(
2301 decode_path=sub_decode_path,
2307 "expected BitString encoded chunk",
2308 klass=self.__class__,
2309 decode_path=sub_decode_path,
2312 chunks.append(chunk)
2313 sub_offset += chunk.tlvlen
2314 vlen += chunk.tlvlen
2316 if len(chunks) == 0:
2319 klass=self.__class__,
2320 decode_path=decode_path,
2325 for chunk_i, chunk in enumerate(chunks[:-1]):
2326 if chunk.bit_len % 8 != 0:
2328 "BitString chunk is not multiple of 8 bits",
2329 klass=self.__class__,
2330 decode_path=decode_path + (str(chunk_i),),
2331 offset=chunk.offset,
2333 values.append(bytes(chunk))
2334 bit_len += chunk.bit_len
2335 chunk_last = chunks[-1]
2336 values.append(bytes(chunk_last))
2337 bit_len += chunk_last.bit_len
2338 obj = self.__class__(
2339 value=(bit_len, b"".join(values)),
2342 default=self.default,
2343 optional=self.optional,
2345 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2347 obj.lenindef = lenindef
2349 return obj, (v[EOC_LEN:] if lenindef else v)
2351 klass=self.__class__,
2352 decode_path=decode_path,
2357 return pp_console_row(next(self.pps()))
2359 def pps(self, decode_path=()):
2363 bit_len, blob = self._value
2364 value = "%d bits" % bit_len
2365 if len(self.specs) > 0:
2366 blob = tuple(self.named)
2368 asn1_type_name=self.asn1_type_name,
2369 obj_name=self.__class__.__name__,
2370 decode_path=decode_path,
2373 optional=self.optional,
2374 default=self == self.default,
2375 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2376 expl=None if self._expl is None else tag_decode(self._expl),
2381 expl_offset=self.expl_offset if self.expled else None,
2382 expl_tlen=self.expl_tlen if self.expled else None,
2383 expl_llen=self.expl_llen if self.expled else None,
2384 expl_vlen=self.expl_vlen if self.expled else None,
2385 expl_lenindef=self.expl_lenindef,
2386 lenindef=self.lenindef,
2389 defined_by, defined = self.defined or (None, None)
2390 if defined_by is not None:
2392 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2394 for pp in self.pps_lenindef(decode_path):
2398 class OctetString(Obj):
2399 """``OCTET STRING`` binary string type
2401 >>> s = OctetString(b"hello world")
2402 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2403 >>> s == OctetString(b"hello world")
2408 >>> OctetString(b"hello", bounds=(4, 4))
2409 Traceback (most recent call last):
2410 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2411 >>> OctetString(b"hell", bounds=(4, 4))
2412 OCTET STRING 4 bytes 68656c6c
2416 Pay attention that OCTET STRING can be encoded both in primitive
2417 and constructed forms. Decoder always checks constructed form tag
2418 additionally to specified primitive one. If BER decoding is
2419 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2420 of DER restrictions.
2422 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2423 tag_default = tag_encode(4)
2424 asn1_type_name = "OCTET STRING"
2437 :param value: set the value. Either binary type, or
2438 :py:class:`pyderasn.OctetString` object
2439 :param bounds: set ``(MIN, MAX)`` value size constraint.
2440 (-inf, +inf) by default
2441 :param bytes impl: override default tag with ``IMPLICIT`` one
2442 :param bytes expl: override default tag with ``EXPLICIT`` one
2443 :param default: set default value. Type same as in ``value``
2444 :param bool optional: is object ``OPTIONAL`` in sequence
2446 super(OctetString, self).__init__(
2454 self._bound_min, self._bound_max = getattr(
2458 ) if bounds is None else bounds
2459 if value is not None:
2460 self._value = self._value_sanitize(value)
2461 if default is not None:
2462 default = self._value_sanitize(default)
2463 self.default = self.__class__(
2468 if self._value is None:
2469 self._value = default
2471 tag_klass, _, tag_num = tag_decode(self.tag)
2472 self.tag_constructed = tag_encode(
2474 form=TagFormConstructed,
2478 def _value_sanitize(self, value):
2479 if issubclass(value.__class__, OctetString):
2480 value = value._value
2481 elif isinstance(value, binary_type):
2484 raise InvalidValueType((self.__class__, bytes))
2485 if not self._bound_min <= len(value) <= self._bound_max:
2486 raise BoundsError(self._bound_min, len(value), self._bound_max)
2491 return self._value is not None
2494 obj = self.__class__()
2495 obj._value = self._value
2496 obj._bound_min = self._bound_min
2497 obj._bound_max = self._bound_max
2499 obj._expl = self._expl
2500 obj.default = self.default
2501 obj.optional = self.optional
2502 obj.offset = self.offset
2503 obj.llen = self.llen
2504 obj.vlen = self.vlen
2507 def __bytes__(self):
2508 self._assert_ready()
2511 def __eq__(self, their):
2512 if isinstance(their, binary_type):
2513 return self._value == their
2514 if not issubclass(their.__class__, OctetString):
2517 self._value == their._value and
2518 self.tag == their.tag and
2519 self._expl == their._expl
2522 def __lt__(self, their):
2523 return self._value < their._value
2534 return self.__class__(
2537 (self._bound_min, self._bound_max)
2538 if bounds is None else bounds
2540 impl=self.tag if impl is None else impl,
2541 expl=self._expl if expl is None else expl,
2542 default=self.default if default is None else default,
2543 optional=self.optional if optional is None else optional,
2547 self._assert_ready()
2550 len_encode(len(self._value)),
2554 def _decode_chunk(self, lv, offset, decode_path, ctx):
2556 l, llen, v = len_decode(lv)
2557 except DecodeError as err:
2558 raise err.__class__(
2560 klass=self.__class__,
2561 decode_path=decode_path,
2565 raise NotEnoughData(
2566 "encoded length is longer than data",
2567 klass=self.__class__,
2568 decode_path=decode_path,
2571 v, tail = v[:l], v[l:]
2573 obj = self.__class__(
2575 bounds=(self._bound_min, self._bound_max),
2578 default=self.default,
2579 optional=self.optional,
2580 _decoded=(offset, llen, l),
2582 except DecodeError as err:
2585 klass=self.__class__,
2586 decode_path=decode_path,
2589 except BoundsError as err:
2592 klass=self.__class__,
2593 decode_path=decode_path,
2598 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2600 t, tlen, lv = tag_strip(tlv)
2601 except DecodeError as err:
2602 raise err.__class__(
2604 klass=self.__class__,
2605 decode_path=decode_path,
2611 return self._decode_chunk(lv, offset, decode_path, ctx)
2612 if t == self.tag_constructed:
2613 if not ctx.get("bered", False):
2615 "unallowed BER constructed encoding",
2616 klass=self.__class__,
2617 decode_path=decode_path,
2624 l, llen, v = len_decode(lv)
2625 except LenIndefForm:
2626 llen, l, v = 1, 0, lv[1:]
2628 except DecodeError as err:
2629 raise err.__class__(
2631 klass=self.__class__,
2632 decode_path=decode_path,
2636 raise NotEnoughData(
2637 "encoded length is longer than data",
2638 klass=self.__class__,
2639 decode_path=decode_path,
2643 sub_offset = offset + tlen + llen
2647 if v[:EOC_LEN].tobytes() == EOC:
2654 "chunk out of bounds",
2655 klass=self.__class__,
2656 decode_path=decode_path + (str(len(chunks) - 1),),
2657 offset=chunks[-1].offset,
2659 sub_decode_path = decode_path + (str(len(chunks)),)
2661 chunk, v_tail = OctetString().decode(
2664 decode_path=sub_decode_path,
2670 "expected OctetString encoded chunk",
2671 klass=self.__class__,
2672 decode_path=sub_decode_path,
2675 chunks.append(chunk)
2676 sub_offset += chunk.tlvlen
2677 vlen += chunk.tlvlen
2680 obj = self.__class__(
2681 value=b"".join(bytes(chunk) for chunk in chunks),
2682 bounds=(self._bound_min, self._bound_max),
2685 default=self.default,
2686 optional=self.optional,
2687 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2689 except DecodeError as err:
2692 klass=self.__class__,
2693 decode_path=decode_path,
2696 except BoundsError as err:
2699 klass=self.__class__,
2700 decode_path=decode_path,
2703 obj.lenindef = lenindef
2705 return obj, (v[EOC_LEN:] if lenindef else v)
2707 klass=self.__class__,
2708 decode_path=decode_path,
2713 return pp_console_row(next(self.pps()))
2715 def pps(self, decode_path=()):
2717 asn1_type_name=self.asn1_type_name,
2718 obj_name=self.__class__.__name__,
2719 decode_path=decode_path,
2720 value=("%d bytes" % len(self._value)) if self.ready else None,
2721 blob=self._value if self.ready else None,
2722 optional=self.optional,
2723 default=self == self.default,
2724 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2725 expl=None if self._expl is None else tag_decode(self._expl),
2730 expl_offset=self.expl_offset if self.expled else None,
2731 expl_tlen=self.expl_tlen if self.expled else None,
2732 expl_llen=self.expl_llen if self.expled else None,
2733 expl_vlen=self.expl_vlen if self.expled else None,
2734 expl_lenindef=self.expl_lenindef,
2735 lenindef=self.lenindef,
2738 defined_by, defined = self.defined or (None, None)
2739 if defined_by is not None:
2741 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2743 for pp in self.pps_lenindef(decode_path):
2748 """``NULL`` null object
2756 tag_default = tag_encode(5)
2757 asn1_type_name = "NULL"
2761 value=None, # unused, but Sequence passes it
2768 :param bytes impl: override default tag with ``IMPLICIT`` one
2769 :param bytes expl: override default tag with ``EXPLICIT`` one
2770 :param bool optional: is object ``OPTIONAL`` in sequence
2772 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2780 obj = self.__class__()
2782 obj._expl = self._expl
2783 obj.default = self.default
2784 obj.optional = self.optional
2785 obj.offset = self.offset
2786 obj.llen = self.llen
2787 obj.vlen = self.vlen
2790 def __eq__(self, their):
2791 if not issubclass(their.__class__, Null):
2794 self.tag == their.tag and
2795 self._expl == their._expl
2805 return self.__class__(
2806 impl=self.tag if impl is None else impl,
2807 expl=self._expl if expl is None else expl,
2808 optional=self.optional if optional is None else optional,
2812 return self.tag + len_encode(0)
2814 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2816 t, _, lv = tag_strip(tlv)
2817 except DecodeError as err:
2818 raise err.__class__(
2820 klass=self.__class__,
2821 decode_path=decode_path,
2826 klass=self.__class__,
2827 decode_path=decode_path,
2833 l, _, v = len_decode(lv)
2834 except DecodeError as err:
2835 raise err.__class__(
2837 klass=self.__class__,
2838 decode_path=decode_path,
2842 raise InvalidLength(
2843 "Null must have zero length",
2844 klass=self.__class__,
2845 decode_path=decode_path,
2848 obj = self.__class__(
2851 optional=self.optional,
2852 _decoded=(offset, 1, 0),
2857 return pp_console_row(next(self.pps()))
2859 def pps(self, decode_path=()):
2861 asn1_type_name=self.asn1_type_name,
2862 obj_name=self.__class__.__name__,
2863 decode_path=decode_path,
2864 optional=self.optional,
2865 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2866 expl=None if self._expl is None else tag_decode(self._expl),
2871 expl_offset=self.expl_offset if self.expled else None,
2872 expl_tlen=self.expl_tlen if self.expled else None,
2873 expl_llen=self.expl_llen if self.expled else None,
2874 expl_vlen=self.expl_vlen if self.expled else None,
2875 expl_lenindef=self.expl_lenindef,
2877 for pp in self.pps_lenindef(decode_path):
2881 class ObjectIdentifier(Obj):
2882 """``OBJECT IDENTIFIER`` OID type
2884 >>> oid = ObjectIdentifier((1, 2, 3))
2885 OBJECT IDENTIFIER 1.2.3
2886 >>> oid == ObjectIdentifier("1.2.3")
2892 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2893 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2895 >>> str(ObjectIdentifier((3, 1)))
2896 Traceback (most recent call last):
2897 pyderasn.InvalidOID: unacceptable first arc value
2899 __slots__ = ("defines",)
2900 tag_default = tag_encode(6)
2901 asn1_type_name = "OBJECT IDENTIFIER"
2914 :param value: set the value. Either tuples of integers,
2915 string of "."-concatenated integers, or
2916 :py:class:`pyderasn.ObjectIdentifier` object
2917 :param defines: sequence of tuples. Each tuple has two elements.
2918 First one is relative to current one decode
2919 path, aiming to the field defined by that OID.
2920 Read about relative path in
2921 :py:func:`pyderasn.abs_decode_path`. Second
2922 tuple element is ``{OID: pyderasn.Obj()}``
2923 dictionary, mapping between current OID value
2924 and structure applied to defined field.
2925 :ref:`Read about DEFINED BY <definedby>`
2926 :param bytes impl: override default tag with ``IMPLICIT`` one
2927 :param bytes expl: override default tag with ``EXPLICIT`` one
2928 :param default: set default value. Type same as in ``value``
2929 :param bool optional: is object ``OPTIONAL`` in sequence
2931 super(ObjectIdentifier, self).__init__(
2939 if value is not None:
2940 self._value = self._value_sanitize(value)
2941 if default is not None:
2942 default = self._value_sanitize(default)
2943 self.default = self.__class__(
2948 if self._value is None:
2949 self._value = default
2950 self.defines = defines
2952 def __add__(self, their):
2953 if isinstance(their, self.__class__):
2954 return self.__class__(self._value + their._value)
2955 if isinstance(their, tuple):
2956 return self.__class__(self._value + their)
2957 raise InvalidValueType((self.__class__, tuple))
2959 def _value_sanitize(self, value):
2960 if issubclass(value.__class__, ObjectIdentifier):
2962 if isinstance(value, string_types):
2964 value = tuple(int(arc) for arc in value.split("."))
2966 raise InvalidOID("unacceptable arcs values")
2967 if isinstance(value, tuple):
2969 raise InvalidOID("less than 2 arcs")
2970 first_arc = value[0]
2971 if first_arc in (0, 1):
2972 if not (0 <= value[1] <= 39):
2973 raise InvalidOID("second arc is too wide")
2974 elif first_arc == 2:
2977 raise InvalidOID("unacceptable first arc value")
2979 raise InvalidValueType((self.__class__, str, tuple))
2983 return self._value is not None
2986 obj = self.__class__()
2987 obj._value = self._value
2988 obj.defines = self.defines
2990 obj._expl = self._expl
2991 obj.default = self.default
2992 obj.optional = self.optional
2993 obj.offset = self.offset
2994 obj.llen = self.llen
2995 obj.vlen = self.vlen
2999 self._assert_ready()
3000 return iter(self._value)
3003 return ".".join(str(arc) for arc in self._value or ())
3006 self._assert_ready()
3009 bytes(self._expl or b"") +
3010 str(self._value).encode("ascii"),
3013 def __eq__(self, their):
3014 if isinstance(their, tuple):
3015 return self._value == their
3016 if not issubclass(their.__class__, ObjectIdentifier):
3019 self.tag == their.tag and
3020 self._expl == their._expl and
3021 self._value == their._value
3024 def __lt__(self, their):
3025 return self._value < their._value
3036 return self.__class__(
3038 defines=self.defines if defines is None else defines,
3039 impl=self.tag if impl is None else impl,
3040 expl=self._expl if expl is None else expl,
3041 default=self.default if default is None else default,
3042 optional=self.optional if optional is None else optional,
3046 self._assert_ready()
3048 first_value = value[1]
3049 first_arc = value[0]
3052 elif first_arc == 1:
3054 elif first_arc == 2:
3056 else: # pragma: no cover
3057 raise RuntimeError("invalid arc is stored")
3058 octets = [zero_ended_encode(first_value)]
3059 for arc in value[2:]:
3060 octets.append(zero_ended_encode(arc))
3061 v = b"".join(octets)
3062 return b"".join((self.tag, len_encode(len(v)), v))
3064 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3066 t, _, lv = tag_strip(tlv)
3067 except DecodeError as err:
3068 raise err.__class__(
3070 klass=self.__class__,
3071 decode_path=decode_path,
3076 klass=self.__class__,
3077 decode_path=decode_path,
3083 l, llen, v = len_decode(lv)
3084 except DecodeError as err:
3085 raise err.__class__(
3087 klass=self.__class__,
3088 decode_path=decode_path,
3092 raise NotEnoughData(
3093 "encoded length is longer than data",
3094 klass=self.__class__,
3095 decode_path=decode_path,
3099 raise NotEnoughData(
3101 klass=self.__class__,
3102 decode_path=decode_path,
3105 v, tail = v[:l], v[l:]
3111 octet = indexbytes(v, i)
3112 arc = (arc << 7) | (octet & 0x7F)
3113 if octet & 0x80 == 0:
3121 klass=self.__class__,
3122 decode_path=decode_path,
3126 second_arc = arcs[0]
3127 if 0 <= second_arc <= 39:
3129 elif 40 <= second_arc <= 79:
3135 obj = self.__class__(
3136 value=tuple([first_arc, second_arc] + arcs[1:]),
3139 default=self.default,
3140 optional=self.optional,
3141 _decoded=(offset, llen, l),
3146 return pp_console_row(next(self.pps()))
3148 def pps(self, decode_path=()):
3150 asn1_type_name=self.asn1_type_name,
3151 obj_name=self.__class__.__name__,
3152 decode_path=decode_path,
3153 value=str(self) if self.ready else None,
3154 optional=self.optional,
3155 default=self == self.default,
3156 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3157 expl=None if self._expl is None else tag_decode(self._expl),
3162 expl_offset=self.expl_offset if self.expled else None,
3163 expl_tlen=self.expl_tlen if self.expled else None,
3164 expl_llen=self.expl_llen if self.expled else None,
3165 expl_vlen=self.expl_vlen if self.expled else None,
3166 expl_lenindef=self.expl_lenindef,
3168 for pp in self.pps_lenindef(decode_path):
3172 class Enumerated(Integer):
3173 """``ENUMERATED`` integer type
3175 This type is identical to :py:class:`pyderasn.Integer`, but requires
3176 schema to be specified and does not accept values missing from it.
3179 tag_default = tag_encode(10)
3180 asn1_type_name = "ENUMERATED"
3191 bounds=None, # dummy argument, workability for Integer.decode
3193 super(Enumerated, self).__init__(
3202 if len(self.specs) == 0:
3203 raise ValueError("schema must be specified")
3205 def _value_sanitize(self, value):
3206 if isinstance(value, self.__class__):
3207 value = value._value
3208 elif isinstance(value, integer_types):
3209 if value not in list(self.specs.values()):
3211 "unknown integer value: %s" % value,
3212 klass=self.__class__,
3214 elif isinstance(value, string_types):
3215 value = self.specs.get(value)
3217 raise ObjUnknown("integer value: %s" % value)
3219 raise InvalidValueType((self.__class__, int, str))
3223 obj = self.__class__(_specs=self.specs)
3224 obj._value = self._value
3225 obj._bound_min = self._bound_min
3226 obj._bound_max = self._bound_max
3228 obj._expl = self._expl
3229 obj.default = self.default
3230 obj.optional = self.optional
3231 obj.offset = self.offset
3232 obj.llen = self.llen
3233 obj.vlen = self.vlen
3245 return self.__class__(
3247 impl=self.tag if impl is None else impl,
3248 expl=self._expl if expl is None else expl,
3249 default=self.default if default is None else default,
3250 optional=self.optional if optional is None else optional,
3255 class CommonString(OctetString):
3256 """Common class for all strings
3258 Everything resembles :py:class:`pyderasn.OctetString`, except
3259 ability to deal with unicode text strings.
3261 >>> hexenc("привет мир".encode("utf-8"))
3262 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3263 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3265 >>> s = UTF8String("привет мир")
3266 UTF8String UTF8String привет мир
3268 'привет мир'
3269 >>> hexenc(bytes(s))
3270 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3272 >>> PrintableString("привет мир")
3273 Traceback (most recent call last):
3274 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3276 >>> BMPString("ада", bounds=(2, 2))
3277 Traceback (most recent call last):
3278 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3279 >>> s = BMPString("ад", bounds=(2, 2))
3282 >>> hexenc(bytes(s))
3290 * - :py:class:`pyderasn.UTF8String`
3292 * - :py:class:`pyderasn.NumericString`
3294 * - :py:class:`pyderasn.PrintableString`
3296 * - :py:class:`pyderasn.TeletexString`
3298 * - :py:class:`pyderasn.T61String`
3300 * - :py:class:`pyderasn.VideotexString`
3302 * - :py:class:`pyderasn.IA5String`
3304 * - :py:class:`pyderasn.GraphicString`
3306 * - :py:class:`pyderasn.VisibleString`
3308 * - :py:class:`pyderasn.ISO646String`
3310 * - :py:class:`pyderasn.GeneralString`
3312 * - :py:class:`pyderasn.UniversalString`
3314 * - :py:class:`pyderasn.BMPString`
3317 __slots__ = ("encoding",)
3319 def _value_sanitize(self, value):
3321 value_decoded = None
3322 if isinstance(value, self.__class__):
3323 value_raw = value._value
3324 elif isinstance(value, text_type):
3325 value_decoded = value
3326 elif isinstance(value, binary_type):
3329 raise InvalidValueType((self.__class__, text_type, binary_type))
3332 value_decoded.encode(self.encoding)
3333 if value_raw is None else value_raw
3336 value_raw.decode(self.encoding)
3337 if value_decoded is None else value_decoded
3339 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3340 raise DecodeError(str(err))
3341 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3349 def __eq__(self, their):
3350 if isinstance(their, binary_type):
3351 return self._value == their
3352 if isinstance(their, text_type):
3353 return self._value == their.encode(self.encoding)
3354 if not isinstance(their, self.__class__):
3357 self._value == their._value and
3358 self.tag == their.tag and
3359 self._expl == their._expl
3362 def __unicode__(self):
3364 return self._value.decode(self.encoding)
3365 return text_type(self._value)
3368 return pp_console_row(next(self.pps(no_unicode=PY2)))
3370 def pps(self, decode_path=(), no_unicode=False):
3373 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3375 asn1_type_name=self.asn1_type_name,
3376 obj_name=self.__class__.__name__,
3377 decode_path=decode_path,
3379 optional=self.optional,
3380 default=self == self.default,
3381 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3382 expl=None if self._expl is None else tag_decode(self._expl),
3387 expl_offset=self.expl_offset if self.expled else None,
3388 expl_tlen=self.expl_tlen if self.expled else None,
3389 expl_llen=self.expl_llen if self.expled else None,
3390 expl_vlen=self.expl_vlen if self.expled else None,
3391 expl_lenindef=self.expl_lenindef,
3393 for pp in self.pps_lenindef(decode_path):
3397 class UTF8String(CommonString):
3399 tag_default = tag_encode(12)
3401 asn1_type_name = "UTF8String"
3404 class NumericString(CommonString):
3407 Its value is properly sanitized: only ASCII digits can be stored.
3410 tag_default = tag_encode(18)
3412 asn1_type_name = "NumericString"
3413 allowable_chars = set(digits.encode("ascii"))
3415 def _value_sanitize(self, value):
3416 value = super(NumericString, self)._value_sanitize(value)
3417 if not set(value) <= self.allowable_chars:
3418 raise DecodeError("non-numeric value")
3422 class PrintableString(CommonString):
3424 tag_default = tag_encode(19)
3426 asn1_type_name = "PrintableString"
3429 class TeletexString(CommonString):
3431 tag_default = tag_encode(20)
3433 asn1_type_name = "TeletexString"
3436 class T61String(TeletexString):
3438 asn1_type_name = "T61String"
3441 class VideotexString(CommonString):
3443 tag_default = tag_encode(21)
3444 encoding = "iso-8859-1"
3445 asn1_type_name = "VideotexString"
3448 class IA5String(CommonString):
3450 tag_default = tag_encode(22)
3452 asn1_type_name = "IA5"
3455 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3456 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3457 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3460 class UTCTime(CommonString):
3461 """``UTCTime`` datetime type
3463 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3464 UTCTime UTCTime 2017-09-30T22:07:50
3470 datetime.datetime(2017, 9, 30, 22, 7, 50)
3471 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3472 datetime.datetime(1957, 9, 30, 22, 7, 50)
3475 tag_default = tag_encode(23)
3477 asn1_type_name = "UTCTime"
3479 fmt = "%y%m%d%H%M%SZ"
3489 bounds=None, # dummy argument, workability for OctetString.decode
3492 :param value: set the value. Either datetime type, or
3493 :py:class:`pyderasn.UTCTime` object
3494 :param bytes impl: override default tag with ``IMPLICIT`` one
3495 :param bytes expl: override default tag with ``EXPLICIT`` one
3496 :param default: set default value. Type same as in ``value``
3497 :param bool optional: is object ``OPTIONAL`` in sequence
3499 super(UTCTime, self).__init__(
3507 if value is not None:
3508 self._value = self._value_sanitize(value)
3509 if default is not None:
3510 default = self._value_sanitize(default)
3511 self.default = self.__class__(
3516 if self._value is None:
3517 self._value = default
3519 def _value_sanitize(self, value):
3520 if isinstance(value, self.__class__):
3522 if isinstance(value, datetime):
3523 return value.strftime(self.fmt).encode("ascii")
3524 if isinstance(value, binary_type):
3526 value_decoded = value.decode("ascii")
3527 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3528 raise DecodeError("invalid UTCTime encoding")
3529 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3531 datetime.strptime(value_decoded, self.fmt)
3532 except (TypeError, ValueError):
3533 raise DecodeError("invalid UTCTime format")
3536 raise DecodeError("invalid UTCTime length")
3537 raise InvalidValueType((self.__class__, datetime))
3539 def __eq__(self, their):
3540 if isinstance(their, binary_type):
3541 return self._value == their
3542 if isinstance(their, datetime):
3543 return self.todatetime() == their
3544 if not isinstance(their, self.__class__):
3547 self._value == their._value and
3548 self.tag == their.tag and
3549 self._expl == their._expl
3552 def todatetime(self):
3553 """Convert to datetime
3557 Pay attention that UTCTime can not hold full year, so all years
3558 having < 50 years are treated as 20xx, 19xx otherwise, according
3559 to X.509 recomendation.
3561 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3562 year = value.year % 100
3564 year=(2000 + year) if year < 50 else (1900 + year),
3568 minute=value.minute,
3569 second=value.second,
3573 return pp_console_row(next(self.pps()))
3575 def pps(self, decode_path=()):
3577 asn1_type_name=self.asn1_type_name,
3578 obj_name=self.__class__.__name__,
3579 decode_path=decode_path,
3580 value=self.todatetime().isoformat() if self.ready else None,
3581 optional=self.optional,
3582 default=self == self.default,
3583 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3584 expl=None if self._expl is None else tag_decode(self._expl),
3589 expl_offset=self.expl_offset if self.expled else None,
3590 expl_tlen=self.expl_tlen if self.expled else None,
3591 expl_llen=self.expl_llen if self.expled else None,
3592 expl_vlen=self.expl_vlen if self.expled else None,
3593 expl_lenindef=self.expl_lenindef,
3595 for pp in self.pps_lenindef(decode_path):
3599 class GeneralizedTime(UTCTime):
3600 """``GeneralizedTime`` datetime type
3602 This type is similar to :py:class:`pyderasn.UTCTime`.
3604 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3605 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3607 '20170930220750.000123Z'
3608 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3609 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3612 tag_default = tag_encode(24)
3613 asn1_type_name = "GeneralizedTime"
3615 fmt = "%Y%m%d%H%M%SZ"
3616 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3618 def _value_sanitize(self, value):
3619 if isinstance(value, self.__class__):
3621 if isinstance(value, datetime):
3622 return value.strftime(
3623 self.fmt_ms if value.microsecond > 0 else self.fmt
3625 if isinstance(value, binary_type):
3627 value_decoded = value.decode("ascii")
3628 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3629 raise DecodeError("invalid GeneralizedTime encoding")
3630 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3632 datetime.strptime(value_decoded, self.fmt)
3633 except (TypeError, ValueError):
3635 "invalid GeneralizedTime (without ms) format",
3638 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3640 datetime.strptime(value_decoded, self.fmt_ms)
3641 except (TypeError, ValueError):
3643 "invalid GeneralizedTime (with ms) format",
3648 "invalid GeneralizedTime length",
3649 klass=self.__class__,
3651 raise InvalidValueType((self.__class__, datetime))
3653 def todatetime(self):
3654 value = self._value.decode("ascii")
3655 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3656 return datetime.strptime(value, self.fmt)
3657 return datetime.strptime(value, self.fmt_ms)
3660 class GraphicString(CommonString):
3662 tag_default = tag_encode(25)
3663 encoding = "iso-8859-1"
3664 asn1_type_name = "GraphicString"
3667 class VisibleString(CommonString):
3669 tag_default = tag_encode(26)
3671 asn1_type_name = "VisibleString"
3674 class ISO646String(VisibleString):
3676 asn1_type_name = "ISO646String"
3679 class GeneralString(CommonString):
3681 tag_default = tag_encode(27)
3682 encoding = "iso-8859-1"
3683 asn1_type_name = "GeneralString"
3686 class UniversalString(CommonString):
3688 tag_default = tag_encode(28)
3689 encoding = "utf-32-be"
3690 asn1_type_name = "UniversalString"
3693 class BMPString(CommonString):
3695 tag_default = tag_encode(30)
3696 encoding = "utf-16-be"
3697 asn1_type_name = "BMPString"
3701 """``CHOICE`` special type
3705 class GeneralName(Choice):
3707 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3708 ("dNSName", IA5String(impl=tag_ctxp(2))),
3711 >>> gn = GeneralName()
3713 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3714 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3715 >>> gn["dNSName"] = IA5String("bar.baz")
3716 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3717 >>> gn["rfc822Name"]
3720 [2] IA5String IA5 bar.baz
3723 >>> gn.value == gn["dNSName"]
3726 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3728 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3729 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3731 __slots__ = ("specs",)
3733 asn1_type_name = "CHOICE"
3746 :param value: set the value. Either ``(choice, value)`` tuple, or
3747 :py:class:`pyderasn.Choice` object
3748 :param bytes impl: can not be set, do **not** use it
3749 :param bytes expl: override default tag with ``EXPLICIT`` one
3750 :param default: set default value. Type same as in ``value``
3751 :param bool optional: is object ``OPTIONAL`` in sequence
3753 if impl is not None:
3754 raise ValueError("no implicit tag allowed for CHOICE")
3755 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3757 schema = getattr(self, "schema", ())
3758 if len(schema) == 0:
3759 raise ValueError("schema must be specified")
3761 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3764 if value is not None:
3765 self._value = self._value_sanitize(value)
3766 if default is not None:
3767 default_value = self._value_sanitize(default)
3768 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3769 default_obj.specs = self.specs
3770 default_obj._value = default_value
3771 self.default = default_obj
3773 self._value = default_obj.copy()._value
3775 def _value_sanitize(self, value):
3776 if isinstance(value, self.__class__):
3778 if isinstance(value, tuple) and len(value) == 2:
3780 spec = self.specs.get(choice)
3782 raise ObjUnknown(choice)
3783 if not isinstance(obj, spec.__class__):
3784 raise InvalidValueType((spec,))
3785 return (choice, spec(obj))
3786 raise InvalidValueType((self.__class__, tuple))
3790 return self._value is not None and self._value[1].ready
3793 obj = self.__class__(schema=self.specs)
3794 obj._expl = self._expl
3795 obj.default = self.default
3796 obj.optional = self.optional
3797 obj.offset = self.offset
3798 obj.llen = self.llen
3799 obj.vlen = self.vlen
3801 if value is not None:
3802 obj._value = (value[0], value[1].copy())
3805 def __eq__(self, their):
3806 if isinstance(their, tuple) and len(their) == 2:
3807 return self._value == their
3808 if not isinstance(their, self.__class__):
3811 self.specs == their.specs and
3812 self._value == their._value
3822 return self.__class__(
3825 expl=self._expl if expl is None else expl,
3826 default=self.default if default is None else default,
3827 optional=self.optional if optional is None else optional,
3832 self._assert_ready()
3833 return self._value[0]
3837 self._assert_ready()
3838 return self._value[1]
3840 def __getitem__(self, key):
3841 if key not in self.specs:
3842 raise ObjUnknown(key)
3843 if self._value is None:
3845 choice, value = self._value
3850 def __setitem__(self, key, value):
3851 spec = self.specs.get(key)
3853 raise ObjUnknown(key)
3854 if not isinstance(value, spec.__class__):
3855 raise InvalidValueType((spec.__class__,))
3856 self._value = (key, spec(value))
3864 return self._value[1].decoded if self.ready else False
3867 self._assert_ready()
3868 return self._value[1].encode()
3870 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3871 for choice, spec in self.specs.items():
3872 sub_decode_path = decode_path + (choice,)
3878 decode_path=sub_decode_path,
3887 klass=self.__class__,
3888 decode_path=decode_path,
3893 value, tail = spec.decode(
3897 decode_path=sub_decode_path,
3900 obj = self.__class__(
3903 default=self.default,
3904 optional=self.optional,
3905 _decoded=(offset, 0, value.fulllen),
3907 obj._value = (choice, value)
3911 value = pp_console_row(next(self.pps()))
3913 value = "%s[%r]" % (value, self.value)
3916 def pps(self, decode_path=()):
3918 asn1_type_name=self.asn1_type_name,
3919 obj_name=self.__class__.__name__,
3920 decode_path=decode_path,
3921 value=self.choice if self.ready else None,
3922 optional=self.optional,
3923 default=self == self.default,
3924 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3925 expl=None if self._expl is None else tag_decode(self._expl),
3930 expl_lenindef=self.expl_lenindef,
3933 yield self.value.pps(decode_path=decode_path + (self.choice,))
3934 for pp in self.pps_lenindef(decode_path):
3938 class PrimitiveTypes(Choice):
3939 """Predefined ``CHOICE`` for all generic primitive types
3941 It could be useful for general decoding of some unspecified values:
3943 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3944 OCTET STRING 3 bytes 666f6f
3945 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3949 schema = tuple((klass.__name__, klass()) for klass in (
3974 """``ANY`` special type
3976 >>> Any(Integer(-123))
3978 >>> a = Any(OctetString(b"hello world").encode())
3979 ANY 040b68656c6c6f20776f726c64
3980 >>> hexenc(bytes(a))
3981 b'0x040x0bhello world'
3983 __slots__ = ("defined",)
3984 tag_default = tag_encode(0)
3985 asn1_type_name = "ANY"
3995 :param value: set the value. Either any kind of pyderasn's
3996 **ready** object, or bytes. Pay attention that
3997 **no** validation is performed is raw binary value
3999 :param bytes expl: override default tag with ``EXPLICIT`` one
4000 :param bool optional: is object ``OPTIONAL`` in sequence
4002 super(Any, self).__init__(None, expl, None, optional, _decoded)
4003 self._value = None if value is None else self._value_sanitize(value)
4006 def _value_sanitize(self, value):
4007 if isinstance(value, self.__class__):
4009 if isinstance(value, Obj):
4010 return value.encode()
4011 if isinstance(value, binary_type):
4013 raise InvalidValueType((self.__class__, Obj, binary_type))
4017 return self._value is not None
4020 obj = self.__class__()
4021 obj._value = self._value
4023 obj._expl = self._expl
4024 obj.optional = self.optional
4025 obj.offset = self.offset
4026 obj.llen = self.llen
4027 obj.vlen = self.vlen
4030 def __eq__(self, their):
4031 if isinstance(their, binary_type):
4032 return self._value == their
4033 if issubclass(their.__class__, Any):
4034 return self._value == their._value
4043 return self.__class__(
4045 expl=self._expl if expl is None else expl,
4046 optional=self.optional if optional is None else optional,
4049 def __bytes__(self):
4050 self._assert_ready()
4058 self._assert_ready()
4061 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4063 t, tlen, lv = tag_strip(tlv)
4064 except DecodeError as err:
4065 raise err.__class__(
4067 klass=self.__class__,
4068 decode_path=decode_path,
4072 l, llen, v = len_decode(lv)
4073 except LenIndefForm as err:
4074 if not ctx.get("bered", False):
4075 raise err.__class__(
4077 klass=self.__class__,
4078 decode_path=decode_path,
4081 llen, vlen, v = 1, 0, lv[1:]
4082 sub_offset = offset + tlen + llen
4084 while v[:EOC_LEN].tobytes() != EOC:
4085 chunk, v = Any().decode(
4088 decode_path=decode_path + (str(chunk_i),),
4092 vlen += chunk.tlvlen
4093 sub_offset += chunk.tlvlen
4095 tlvlen = tlen + llen + vlen + EOC_LEN
4096 obj = self.__class__(
4097 value=tlv[:tlvlen].tobytes(),
4099 optional=self.optional,
4100 _decoded=(offset, 0, tlvlen),
4104 return obj, v[EOC_LEN:]
4105 except DecodeError as err:
4106 raise err.__class__(
4108 klass=self.__class__,
4109 decode_path=decode_path,
4113 raise NotEnoughData(
4114 "encoded length is longer than data",
4115 klass=self.__class__,
4116 decode_path=decode_path,
4119 tlvlen = tlen + llen + l
4120 v, tail = tlv[:tlvlen], v[l:]
4121 obj = self.__class__(
4124 optional=self.optional,
4125 _decoded=(offset, 0, tlvlen),
4131 return pp_console_row(next(self.pps()))
4133 def pps(self, decode_path=()):
4135 asn1_type_name=self.asn1_type_name,
4136 obj_name=self.__class__.__name__,
4137 decode_path=decode_path,
4138 blob=self._value if self.ready else None,
4139 optional=self.optional,
4140 default=self == self.default,
4141 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4142 expl=None if self._expl is None else tag_decode(self._expl),
4147 expl_offset=self.expl_offset if self.expled else None,
4148 expl_tlen=self.expl_tlen if self.expled else None,
4149 expl_llen=self.expl_llen if self.expled else None,
4150 expl_vlen=self.expl_vlen if self.expled else None,
4151 expl_lenindef=self.expl_lenindef,
4152 lenindef=self.lenindef,
4154 defined_by, defined = self.defined or (None, None)
4155 if defined_by is not None:
4157 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4159 for pp in self.pps_lenindef(decode_path):
4163 ########################################################################
4164 # ASN.1 constructed types
4165 ########################################################################
4167 def get_def_by_path(defines_by_path, sub_decode_path):
4168 """Get define by decode path
4170 for path, define in defines_by_path:
4171 if len(path) != len(sub_decode_path):
4173 for p1, p2 in zip(path, sub_decode_path):
4174 if (p1 != any) and (p1 != p2):
4180 def abs_decode_path(decode_path, rel_path):
4181 """Create an absolute decode path from current and relative ones
4183 :param decode_path: current decode path, starting point.
4185 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4186 If first tuple's element is "/", then treat it as
4187 an absolute path, ignoring ``decode_path`` as
4188 starting point. Also this tuple can contain ".."
4189 elements, stripping the leading element from
4192 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4193 ("foo", "bar", "baz", "whatever")
4194 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4196 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4199 if rel_path[0] == "/":
4201 if rel_path[0] == "..":
4202 return abs_decode_path(decode_path[:-1], rel_path[1:])
4203 return decode_path + rel_path
4206 class Sequence(Obj):
4207 """``SEQUENCE`` structure type
4209 You have to make specification of sequence::
4211 class Extension(Sequence):
4213 ("extnID", ObjectIdentifier()),
4214 ("critical", Boolean(default=False)),
4215 ("extnValue", OctetString()),
4218 Then, you can work with it as with dictionary.
4220 >>> ext = Extension()
4221 >>> Extension().specs
4223 ('extnID', OBJECT IDENTIFIER),
4224 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4225 ('extnValue', OCTET STRING),
4227 >>> ext["extnID"] = "1.2.3"
4228 Traceback (most recent call last):
4229 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4230 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4232 You can determine if sequence is ready to be encoded:
4237 Traceback (most recent call last):
4238 pyderasn.ObjNotReady: object is not ready: extnValue
4239 >>> ext["extnValue"] = OctetString(b"foobar")
4243 Value you want to assign, must have the same **type** as in
4244 corresponding specification, but it can have different tags,
4245 optional/default attributes -- they will be taken from specification
4248 class TBSCertificate(Sequence):
4250 ("version", Version(expl=tag_ctxc(0), default="v1")),
4253 >>> tbs = TBSCertificate()
4254 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4256 Assign ``None`` to remove value from sequence.
4258 You can set values in Sequence during its initialization:
4260 >>> AlgorithmIdentifier((
4261 ("algorithm", ObjectIdentifier("1.2.3")),
4262 ("parameters", Any(Null()))
4264 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4266 You can determine if value exists/set in the sequence and take its value:
4268 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4271 OBJECT IDENTIFIER 1.2.3
4273 But pay attention that if value has default, then it won't be (not
4274 in) in the sequence (because ``DEFAULT`` must not be encoded in
4275 DER), but you can read its value:
4277 >>> "critical" in ext, ext["critical"]
4278 (False, BOOLEAN False)
4279 >>> ext["critical"] = Boolean(True)
4280 >>> "critical" in ext, ext["critical"]
4281 (True, BOOLEAN True)
4283 All defaulted values are always optional.
4285 .. _strict_default_existence_ctx:
4289 When decoded DER contains defaulted value inside, then
4290 technically this is not valid DER encoding. But we allow and pass
4291 it **by default**. Of course reencoding of that kind of DER will
4292 result in different binary representation (validly without
4293 defaulted value inside). You can enable strict defaulted values
4294 existence validation by setting ``"strict_default_existence":
4295 True`` :ref:`context <ctx>` option -- decoding process will raise
4296 an exception if defaulted value is met.
4298 Two sequences are equal if they have equal specification (schema),
4299 implicit/explicit tagging and the same values.
4301 __slots__ = ("specs",)
4302 tag_default = tag_encode(form=TagFormConstructed, num=16)
4303 asn1_type_name = "SEQUENCE"
4315 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4317 schema = getattr(self, "schema", ())
4319 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4322 if value is not None:
4323 if issubclass(value.__class__, Sequence):
4324 self._value = value._value
4325 elif hasattr(value, "__iter__"):
4326 for seq_key, seq_value in value:
4327 self[seq_key] = seq_value
4329 raise InvalidValueType((Sequence,))
4330 if default is not None:
4331 if not issubclass(default.__class__, Sequence):
4332 raise InvalidValueType((Sequence,))
4333 default_value = default._value
4334 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4335 default_obj.specs = self.specs
4336 default_obj._value = default_value
4337 self.default = default_obj
4339 self._value = default_obj.copy()._value
4343 for name, spec in self.specs.items():
4344 value = self._value.get(name)
4355 obj = self.__class__(schema=self.specs)
4357 obj._expl = self._expl
4358 obj.default = self.default
4359 obj.optional = self.optional
4360 obj.offset = self.offset
4361 obj.llen = self.llen
4362 obj.vlen = self.vlen
4363 obj._value = {k: v.copy() for k, v in self._value.items()}
4366 def __eq__(self, their):
4367 if not isinstance(their, self.__class__):
4370 self.specs == their.specs and
4371 self.tag == their.tag and
4372 self._expl == their._expl and
4373 self._value == their._value
4384 return self.__class__(
4387 impl=self.tag if impl is None else impl,
4388 expl=self._expl if expl is None else expl,
4389 default=self.default if default is None else default,
4390 optional=self.optional if optional is None else optional,
4393 def __contains__(self, key):
4394 return key in self._value
4396 def __setitem__(self, key, value):
4397 spec = self.specs.get(key)
4399 raise ObjUnknown(key)
4401 self._value.pop(key, None)
4403 if not isinstance(value, spec.__class__):
4404 raise InvalidValueType((spec.__class__,))
4405 value = spec(value=value)
4406 if spec.default is not None and value == spec.default:
4407 self._value.pop(key, None)
4409 self._value[key] = value
4411 def __getitem__(self, key):
4412 value = self._value.get(key)
4413 if value is not None:
4415 spec = self.specs.get(key)
4417 raise ObjUnknown(key)
4418 if spec.default is not None:
4422 def _encoded_values(self):
4424 for name, spec in self.specs.items():
4425 value = self._value.get(name)
4429 raise ObjNotReady(name)
4430 raws.append(value.encode())
4434 v = b"".join(self._encoded_values())
4435 return b"".join((self.tag, len_encode(len(v)), v))
4437 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4439 t, tlen, lv = tag_strip(tlv)
4440 except DecodeError as err:
4441 raise err.__class__(
4443 klass=self.__class__,
4444 decode_path=decode_path,
4449 klass=self.__class__,
4450 decode_path=decode_path,
4457 l, llen, v = len_decode(lv)
4458 except LenIndefForm as err:
4459 if not ctx.get("bered", False):
4460 raise err.__class__(
4462 klass=self.__class__,
4463 decode_path=decode_path,
4466 l, llen, v = 0, 1, lv[1:]
4468 except DecodeError as err:
4469 raise err.__class__(
4471 klass=self.__class__,
4472 decode_path=decode_path,
4476 raise NotEnoughData(
4477 "encoded length is longer than data",
4478 klass=self.__class__,
4479 decode_path=decode_path,
4483 v, tail = v[:l], v[l:]
4485 sub_offset = offset + tlen + llen
4487 for name, spec in self.specs.items():
4488 if spec.optional and (
4489 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4493 sub_decode_path = decode_path + (name,)
4495 value, v_tail = spec.decode(
4499 decode_path=sub_decode_path,
4507 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4508 if defined is not None:
4509 defined_by, defined_spec = defined
4510 if issubclass(value.__class__, SequenceOf):
4511 for i, _value in enumerate(value):
4512 sub_sub_decode_path = sub_decode_path + (
4514 DecodePathDefBy(defined_by),
4516 defined_value, defined_tail = defined_spec.decode(
4517 memoryview(bytes(_value)),
4519 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4520 if value.expled else (value.tlen + value.llen)
4523 decode_path=sub_sub_decode_path,
4526 if len(defined_tail) > 0:
4529 klass=self.__class__,
4530 decode_path=sub_sub_decode_path,
4533 _value.defined = (defined_by, defined_value)
4535 defined_value, defined_tail = defined_spec.decode(
4536 memoryview(bytes(value)),
4538 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4539 if value.expled else (value.tlen + value.llen)
4542 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4545 if len(defined_tail) > 0:
4548 klass=self.__class__,
4549 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4552 value.defined = (defined_by, defined_value)
4554 value_len = value.fulllen
4556 sub_offset += value_len
4558 if spec.default is not None and value == spec.default:
4559 if ctx.get("strict_default_existence", False):
4561 "DEFAULT value met",
4562 klass=self.__class__,
4563 decode_path=sub_decode_path,
4568 values[name] = value
4570 spec_defines = getattr(spec, "defines", ())
4571 if len(spec_defines) == 0:
4572 defines_by_path = ctx.get("defines_by_path", ())
4573 if len(defines_by_path) > 0:
4574 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4575 if spec_defines is not None and len(spec_defines) > 0:
4576 for rel_path, schema in spec_defines:
4577 defined = schema.get(value, None)
4578 if defined is not None:
4579 ctx.setdefault("_defines", []).append((
4580 abs_decode_path(sub_decode_path[:-1], rel_path),
4584 if v[:EOC_LEN].tobytes() != EOC:
4587 klass=self.__class__,
4588 decode_path=decode_path,
4596 klass=self.__class__,
4597 decode_path=decode_path,
4600 obj = self.__class__(
4604 default=self.default,
4605 optional=self.optional,
4606 _decoded=(offset, llen, vlen),
4609 obj.lenindef = lenindef
4613 value = pp_console_row(next(self.pps()))
4615 for name in self.specs:
4616 _value = self._value.get(name)
4619 cols.append("%s: %s" % (name, repr(_value)))
4620 return "%s[%s]" % (value, "; ".join(cols))
4622 def pps(self, decode_path=()):
4624 asn1_type_name=self.asn1_type_name,
4625 obj_name=self.__class__.__name__,
4626 decode_path=decode_path,
4627 optional=self.optional,
4628 default=self == self.default,
4629 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4630 expl=None if self._expl is None else tag_decode(self._expl),
4635 expl_offset=self.expl_offset if self.expled else None,
4636 expl_tlen=self.expl_tlen if self.expled else None,
4637 expl_llen=self.expl_llen if self.expled else None,
4638 expl_vlen=self.expl_vlen if self.expled else None,
4639 expl_lenindef=self.expl_lenindef,
4640 lenindef=self.lenindef,
4642 for name in self.specs:
4643 value = self._value.get(name)
4646 yield value.pps(decode_path=decode_path + (name,))
4647 for pp in self.pps_lenindef(decode_path):
4651 class Set(Sequence):
4652 """``SET`` structure type
4654 Its usage is identical to :py:class:`pyderasn.Sequence`.
4657 tag_default = tag_encode(form=TagFormConstructed, num=17)
4658 asn1_type_name = "SET"
4661 raws = self._encoded_values()
4664 return b"".join((self.tag, len_encode(len(v)), v))
4666 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4668 t, tlen, lv = tag_strip(tlv)
4669 except DecodeError as err:
4670 raise err.__class__(
4672 klass=self.__class__,
4673 decode_path=decode_path,
4678 klass=self.__class__,
4679 decode_path=decode_path,
4686 l, llen, v = len_decode(lv)
4687 except LenIndefForm as err:
4688 if not ctx.get("bered", False):
4689 raise err.__class__(
4691 klass=self.__class__,
4692 decode_path=decode_path,
4695 l, llen, v = 0, 1, lv[1:]
4697 except DecodeError as err:
4698 raise err.__class__(
4700 klass=self.__class__,
4701 decode_path=decode_path,
4705 raise NotEnoughData(
4706 "encoded length is longer than data",
4707 klass=self.__class__,
4711 v, tail = v[:l], v[l:]
4713 sub_offset = offset + tlen + llen
4715 specs_items = self.specs.items
4717 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4719 for name, spec in specs_items():
4720 sub_decode_path = decode_path + (name,)
4726 decode_path=sub_decode_path,
4735 klass=self.__class__,
4736 decode_path=decode_path,
4739 value, v_tail = spec.decode(
4743 decode_path=sub_decode_path,
4746 value_len = value.fulllen
4747 sub_offset += value_len
4750 if spec.default is None or value != spec.default: # pragma: no cover
4751 # SeqMixing.test_encoded_default_accepted covers that place
4752 values[name] = value
4753 obj = self.__class__(
4757 default=self.default,
4758 optional=self.optional,
4759 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4763 if v[:EOC_LEN].tobytes() != EOC:
4766 klass=self.__class__,
4767 decode_path=decode_path,
4774 "not all values are ready",
4775 klass=self.__class__,
4776 decode_path=decode_path,
4782 class SequenceOf(Obj):
4783 """``SEQUENCE OF`` sequence type
4785 For that kind of type you must specify the object it will carry on
4786 (bounds are for example here, not required)::
4788 class Ints(SequenceOf):
4793 >>> ints.append(Integer(123))
4794 >>> ints.append(Integer(234))
4796 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4797 >>> [int(i) for i in ints]
4799 >>> ints.append(Integer(345))
4800 Traceback (most recent call last):
4801 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4804 >>> ints[1] = Integer(345)
4806 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4808 Also you can initialize sequence with preinitialized values:
4810 >>> ints = Ints([Integer(123), Integer(234)])
4812 __slots__ = ("spec", "_bound_min", "_bound_max")
4813 tag_default = tag_encode(form=TagFormConstructed, num=16)
4814 asn1_type_name = "SEQUENCE OF"
4827 super(SequenceOf, self).__init__(
4835 schema = getattr(self, "schema", None)
4837 raise ValueError("schema must be specified")
4839 self._bound_min, self._bound_max = getattr(
4843 ) if bounds is None else bounds
4845 if value is not None:
4846 self._value = self._value_sanitize(value)
4847 if default is not None:
4848 default_value = self._value_sanitize(default)
4849 default_obj = self.__class__(
4854 default_obj._value = default_value
4855 self.default = default_obj
4857 self._value = default_obj.copy()._value
4859 def _value_sanitize(self, value):
4860 if issubclass(value.__class__, SequenceOf):
4861 value = value._value
4862 elif hasattr(value, "__iter__"):
4865 raise InvalidValueType((self.__class__, iter))
4866 if not self._bound_min <= len(value) <= self._bound_max:
4867 raise BoundsError(self._bound_min, len(value), self._bound_max)
4869 if not isinstance(v, self.spec.__class__):
4870 raise InvalidValueType((self.spec.__class__,))
4875 return all(v.ready for v in self._value)
4878 obj = self.__class__(schema=self.spec)
4879 obj._bound_min = self._bound_min
4880 obj._bound_max = self._bound_max
4882 obj._expl = self._expl
4883 obj.default = self.default
4884 obj.optional = self.optional
4885 obj.offset = self.offset
4886 obj.llen = self.llen
4887 obj.vlen = self.vlen
4888 obj._value = [v.copy() for v in self._value]
4891 def __eq__(self, their):
4892 if isinstance(their, self.__class__):
4894 self.spec == their.spec and
4895 self.tag == their.tag and
4896 self._expl == their._expl and
4897 self._value == their._value
4899 if hasattr(their, "__iter__"):
4900 return self._value == list(their)
4912 return self.__class__(
4916 (self._bound_min, self._bound_max)
4917 if bounds is None else bounds
4919 impl=self.tag if impl is None else impl,
4920 expl=self._expl if expl is None else expl,
4921 default=self.default if default is None else default,
4922 optional=self.optional if optional is None else optional,
4925 def __contains__(self, key):
4926 return key in self._value
4928 def append(self, value):
4929 if not isinstance(value, self.spec.__class__):
4930 raise InvalidValueType((self.spec.__class__,))
4931 if len(self._value) + 1 > self._bound_max:
4934 len(self._value) + 1,
4937 self._value.append(value)
4940 self._assert_ready()
4941 return iter(self._value)
4944 self._assert_ready()
4945 return len(self._value)
4947 def __setitem__(self, key, value):
4948 if not isinstance(value, self.spec.__class__):
4949 raise InvalidValueType((self.spec.__class__,))
4950 self._value[key] = self.spec(value=value)
4952 def __getitem__(self, key):
4953 return self._value[key]
4955 def _encoded_values(self):
4956 return [v.encode() for v in self._value]
4959 v = b"".join(self._encoded_values())
4960 return b"".join((self.tag, len_encode(len(v)), v))
4962 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4964 t, tlen, lv = tag_strip(tlv)
4965 except DecodeError as err:
4966 raise err.__class__(
4968 klass=self.__class__,
4969 decode_path=decode_path,
4974 klass=self.__class__,
4975 decode_path=decode_path,
4982 l, llen, v = len_decode(lv)
4983 except LenIndefForm as err:
4984 if not ctx.get("bered", False):
4985 raise err.__class__(
4987 klass=self.__class__,
4988 decode_path=decode_path,
4991 l, llen, v = 0, 1, lv[1:]
4993 except DecodeError as err:
4994 raise err.__class__(
4996 klass=self.__class__,
4997 decode_path=decode_path,
5001 raise NotEnoughData(
5002 "encoded length is longer than data",
5003 klass=self.__class__,
5004 decode_path=decode_path,
5008 v, tail = v[:l], v[l:]
5010 sub_offset = offset + tlen + llen
5014 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5016 value, v_tail = spec.decode(
5020 decode_path=decode_path + (str(len(_value)),),
5023 value_len = value.fulllen
5024 sub_offset += value_len
5027 _value.append(value)
5029 obj = self.__class__(
5032 bounds=(self._bound_min, self._bound_max),
5035 default=self.default,
5036 optional=self.optional,
5037 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5039 except BoundsError as err:
5042 klass=self.__class__,
5043 decode_path=decode_path,
5047 if v[:EOC_LEN].tobytes() != EOC:
5050 klass=self.__class__,
5051 decode_path=decode_path,
5060 pp_console_row(next(self.pps())),
5061 ", ".join(repr(v) for v in self._value),
5064 def pps(self, decode_path=()):
5066 asn1_type_name=self.asn1_type_name,
5067 obj_name=self.__class__.__name__,
5068 decode_path=decode_path,
5069 optional=self.optional,
5070 default=self == self.default,
5071 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5072 expl=None if self._expl is None else tag_decode(self._expl),
5077 expl_offset=self.expl_offset if self.expled else None,
5078 expl_tlen=self.expl_tlen if self.expled else None,
5079 expl_llen=self.expl_llen if self.expled else None,
5080 expl_vlen=self.expl_vlen if self.expled else None,
5081 expl_lenindef=self.expl_lenindef,
5082 lenindef=self.lenindef,
5084 for i, value in enumerate(self._value):
5085 yield value.pps(decode_path=decode_path + (str(i),))
5086 for pp in self.pps_lenindef(decode_path):
5090 class SetOf(SequenceOf):
5091 """``SET OF`` sequence type
5093 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5096 tag_default = tag_encode(form=TagFormConstructed, num=17)
5097 asn1_type_name = "SET OF"
5100 raws = self._encoded_values()
5103 return b"".join((self.tag, len_encode(len(v)), v))
5106 def obj_by_path(pypath): # pragma: no cover
5107 """Import object specified as string Python path
5109 Modules must be separated from classes/functions with ``:``.
5111 >>> obj_by_path("foo.bar:Baz")
5112 <class 'foo.bar.Baz'>
5113 >>> obj_by_path("foo.bar:Baz.boo")
5114 <classmethod 'foo.bar.Baz.boo'>
5116 mod, objs = pypath.rsplit(":", 1)
5117 from importlib import import_module
5118 obj = import_module(mod)
5119 for obj_name in objs.split("."):
5120 obj = getattr(obj, obj_name)
5124 def generic_decoder(): # pragma: no cover
5125 # All of this below is a big hack with self references
5126 choice = PrimitiveTypes()
5127 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5128 choice.specs["SetOf"] = SetOf(schema=choice)
5130 choice.specs["SequenceOf%d" % i] = SequenceOf(
5134 choice.specs["Any"] = Any()
5136 # Class name equals to type name, to omit it from output
5137 class SEQUENCEOF(SequenceOf):
5141 def pprint_any(obj, oids=None, with_colours=False):
5142 def _pprint_pps(pps):
5144 if hasattr(pp, "_fields"):
5145 if pp.asn1_type_name == Choice.asn1_type_name:
5147 pp_kwargs = pp._asdict()
5148 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5149 pp = _pp(**pp_kwargs)
5150 yield pp_console_row(
5155 with_colours=with_colours,
5157 for row in pp_console_blob(pp):
5160 for row in _pprint_pps(pp):
5162 return "\n".join(_pprint_pps(obj.pps()))
5163 return SEQUENCEOF(), pprint_any
5166 def main(): # pragma: no cover
5168 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5169 parser.add_argument(
5173 help="Skip that number of bytes from the beginning",
5175 parser.add_argument(
5177 help="Python path to dictionary with OIDs",
5179 parser.add_argument(
5181 help="Python path to schema definition to use",
5183 parser.add_argument(
5184 "--defines-by-path",
5185 help="Python path to decoder's defines_by_path",
5187 parser.add_argument(
5189 action='store_true',
5190 help="Disallow BER encoding",
5192 parser.add_argument(
5194 type=argparse.FileType("rb"),
5195 help="Path to DER file you want to decode",
5197 args = parser.parse_args()
5198 args.DERFile.seek(args.skip)
5199 der = memoryview(args.DERFile.read())
5200 args.DERFile.close()
5201 oids = obj_by_path(args.oids) if args.oids else {}
5203 schema = obj_by_path(args.schema)
5204 from functools import partial
5205 pprinter = partial(pprint, big_blobs=True)
5207 schema, pprinter = generic_decoder()
5208 ctx = {"bered": not args.nobered}
5209 if args.defines_by_path is not None:
5210 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5211 obj, tail = schema().decode(der, ctx=ctx)
5215 with_colours=True if environ.get("NO_COLOR") is None else False,
5218 print("\nTrailing data: %s" % hexenc(tail))
5221 if __name__ == "__main__":