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):
3525 value_decoded = value.decode("ascii")
3526 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3528 datetime.strptime(value_decoded, self.fmt)
3530 raise DecodeError("invalid UTCTime format")
3533 raise DecodeError("invalid UTCTime length")
3534 raise InvalidValueType((self.__class__, datetime))
3536 def __eq__(self, their):
3537 if isinstance(their, binary_type):
3538 return self._value == their
3539 if isinstance(their, datetime):
3540 return self.todatetime() == their
3541 if not isinstance(their, self.__class__):
3544 self._value == their._value and
3545 self.tag == their.tag and
3546 self._expl == their._expl
3549 def todatetime(self):
3550 """Convert to datetime
3554 Pay attention that UTCTime can not hold full year, so all years
3555 having < 50 years are treated as 20xx, 19xx otherwise, according
3556 to X.509 recomendation.
3558 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3559 year = value.year % 100
3561 year=(2000 + year) if year < 50 else (1900 + year),
3565 minute=value.minute,
3566 second=value.second,
3570 return pp_console_row(next(self.pps()))
3572 def pps(self, decode_path=()):
3574 asn1_type_name=self.asn1_type_name,
3575 obj_name=self.__class__.__name__,
3576 decode_path=decode_path,
3577 value=self.todatetime().isoformat() if self.ready else None,
3578 optional=self.optional,
3579 default=self == self.default,
3580 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3581 expl=None if self._expl is None else tag_decode(self._expl),
3586 expl_offset=self.expl_offset if self.expled else None,
3587 expl_tlen=self.expl_tlen if self.expled else None,
3588 expl_llen=self.expl_llen if self.expled else None,
3589 expl_vlen=self.expl_vlen if self.expled else None,
3590 expl_lenindef=self.expl_lenindef,
3592 for pp in self.pps_lenindef(decode_path):
3596 class GeneralizedTime(UTCTime):
3597 """``GeneralizedTime`` datetime type
3599 This type is similar to :py:class:`pyderasn.UTCTime`.
3601 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3602 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3604 '20170930220750.000123Z'
3605 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3606 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3609 tag_default = tag_encode(24)
3610 asn1_type_name = "GeneralizedTime"
3612 fmt = "%Y%m%d%H%M%SZ"
3613 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3615 def _value_sanitize(self, value):
3616 if isinstance(value, self.__class__):
3618 if isinstance(value, datetime):
3619 return value.strftime(
3620 self.fmt_ms if value.microsecond > 0 else self.fmt
3622 if isinstance(value, binary_type):
3623 value_decoded = value.decode("ascii")
3624 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3626 datetime.strptime(value_decoded, self.fmt)
3629 "invalid GeneralizedTime (without ms) format",
3632 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3634 datetime.strptime(value_decoded, self.fmt_ms)
3637 "invalid GeneralizedTime (with ms) format",
3642 "invalid GeneralizedTime length",
3643 klass=self.__class__,
3645 raise InvalidValueType((self.__class__, datetime))
3647 def todatetime(self):
3648 value = self._value.decode("ascii")
3649 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3650 return datetime.strptime(value, self.fmt)
3651 return datetime.strptime(value, self.fmt_ms)
3654 class GraphicString(CommonString):
3656 tag_default = tag_encode(25)
3657 encoding = "iso-8859-1"
3658 asn1_type_name = "GraphicString"
3661 class VisibleString(CommonString):
3663 tag_default = tag_encode(26)
3665 asn1_type_name = "VisibleString"
3668 class ISO646String(VisibleString):
3670 asn1_type_name = "ISO646String"
3673 class GeneralString(CommonString):
3675 tag_default = tag_encode(27)
3676 encoding = "iso-8859-1"
3677 asn1_type_name = "GeneralString"
3680 class UniversalString(CommonString):
3682 tag_default = tag_encode(28)
3683 encoding = "utf-32-be"
3684 asn1_type_name = "UniversalString"
3687 class BMPString(CommonString):
3689 tag_default = tag_encode(30)
3690 encoding = "utf-16-be"
3691 asn1_type_name = "BMPString"
3695 """``CHOICE`` special type
3699 class GeneralName(Choice):
3701 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3702 ("dNSName", IA5String(impl=tag_ctxp(2))),
3705 >>> gn = GeneralName()
3707 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3708 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3709 >>> gn["dNSName"] = IA5String("bar.baz")
3710 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3711 >>> gn["rfc822Name"]
3714 [2] IA5String IA5 bar.baz
3717 >>> gn.value == gn["dNSName"]
3720 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3722 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3723 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3725 __slots__ = ("specs",)
3727 asn1_type_name = "CHOICE"
3740 :param value: set the value. Either ``(choice, value)`` tuple, or
3741 :py:class:`pyderasn.Choice` object
3742 :param bytes impl: can not be set, do **not** use it
3743 :param bytes expl: override default tag with ``EXPLICIT`` one
3744 :param default: set default value. Type same as in ``value``
3745 :param bool optional: is object ``OPTIONAL`` in sequence
3747 if impl is not None:
3748 raise ValueError("no implicit tag allowed for CHOICE")
3749 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3751 schema = getattr(self, "schema", ())
3752 if len(schema) == 0:
3753 raise ValueError("schema must be specified")
3755 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3758 if value is not None:
3759 self._value = self._value_sanitize(value)
3760 if default is not None:
3761 default_value = self._value_sanitize(default)
3762 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3763 default_obj.specs = self.specs
3764 default_obj._value = default_value
3765 self.default = default_obj
3767 self._value = default_obj.copy()._value
3769 def _value_sanitize(self, value):
3770 if isinstance(value, self.__class__):
3772 if isinstance(value, tuple) and len(value) == 2:
3774 spec = self.specs.get(choice)
3776 raise ObjUnknown(choice)
3777 if not isinstance(obj, spec.__class__):
3778 raise InvalidValueType((spec,))
3779 return (choice, spec(obj))
3780 raise InvalidValueType((self.__class__, tuple))
3784 return self._value is not None and self._value[1].ready
3787 obj = self.__class__(schema=self.specs)
3788 obj._expl = self._expl
3789 obj.default = self.default
3790 obj.optional = self.optional
3791 obj.offset = self.offset
3792 obj.llen = self.llen
3793 obj.vlen = self.vlen
3795 if value is not None:
3796 obj._value = (value[0], value[1].copy())
3799 def __eq__(self, their):
3800 if isinstance(their, tuple) and len(their) == 2:
3801 return self._value == their
3802 if not isinstance(their, self.__class__):
3805 self.specs == their.specs and
3806 self._value == their._value
3816 return self.__class__(
3819 expl=self._expl if expl is None else expl,
3820 default=self.default if default is None else default,
3821 optional=self.optional if optional is None else optional,
3826 self._assert_ready()
3827 return self._value[0]
3831 self._assert_ready()
3832 return self._value[1]
3834 def __getitem__(self, key):
3835 if key not in self.specs:
3836 raise ObjUnknown(key)
3837 if self._value is None:
3839 choice, value = self._value
3844 def __setitem__(self, key, value):
3845 spec = self.specs.get(key)
3847 raise ObjUnknown(key)
3848 if not isinstance(value, spec.__class__):
3849 raise InvalidValueType((spec.__class__,))
3850 self._value = (key, spec(value))
3858 return self._value[1].decoded if self.ready else False
3861 self._assert_ready()
3862 return self._value[1].encode()
3864 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3865 for choice, spec in self.specs.items():
3866 sub_decode_path = decode_path + (choice,)
3872 decode_path=sub_decode_path,
3881 klass=self.__class__,
3882 decode_path=decode_path,
3887 value, tail = spec.decode(
3891 decode_path=sub_decode_path,
3894 obj = self.__class__(
3897 default=self.default,
3898 optional=self.optional,
3899 _decoded=(offset, 0, value.tlvlen),
3901 obj._value = (choice, value)
3905 value = pp_console_row(next(self.pps()))
3907 value = "%s[%r]" % (value, self.value)
3910 def pps(self, decode_path=()):
3912 asn1_type_name=self.asn1_type_name,
3913 obj_name=self.__class__.__name__,
3914 decode_path=decode_path,
3915 value=self.choice if self.ready else None,
3916 optional=self.optional,
3917 default=self == self.default,
3918 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3919 expl=None if self._expl is None else tag_decode(self._expl),
3924 expl_lenindef=self.expl_lenindef,
3927 yield self.value.pps(decode_path=decode_path + (self.choice,))
3928 for pp in self.pps_lenindef(decode_path):
3932 class PrimitiveTypes(Choice):
3933 """Predefined ``CHOICE`` for all generic primitive types
3935 It could be useful for general decoding of some unspecified values:
3937 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3938 OCTET STRING 3 bytes 666f6f
3939 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3943 schema = tuple((klass.__name__, klass()) for klass in (
3968 """``ANY`` special type
3970 >>> Any(Integer(-123))
3972 >>> a = Any(OctetString(b"hello world").encode())
3973 ANY 040b68656c6c6f20776f726c64
3974 >>> hexenc(bytes(a))
3975 b'0x040x0bhello world'
3977 __slots__ = ("defined",)
3978 tag_default = tag_encode(0)
3979 asn1_type_name = "ANY"
3989 :param value: set the value. Either any kind of pyderasn's
3990 **ready** object, or bytes. Pay attention that
3991 **no** validation is performed is raw binary value
3993 :param bytes expl: override default tag with ``EXPLICIT`` one
3994 :param bool optional: is object ``OPTIONAL`` in sequence
3996 super(Any, self).__init__(None, expl, None, optional, _decoded)
3997 self._value = None if value is None else self._value_sanitize(value)
4000 def _value_sanitize(self, value):
4001 if isinstance(value, self.__class__):
4003 if isinstance(value, Obj):
4004 return value.encode()
4005 if isinstance(value, binary_type):
4007 raise InvalidValueType((self.__class__, Obj, binary_type))
4011 return self._value is not None
4014 obj = self.__class__()
4015 obj._value = self._value
4017 obj._expl = self._expl
4018 obj.optional = self.optional
4019 obj.offset = self.offset
4020 obj.llen = self.llen
4021 obj.vlen = self.vlen
4024 def __eq__(self, their):
4025 if isinstance(their, binary_type):
4026 return self._value == their
4027 if issubclass(their.__class__, Any):
4028 return self._value == their._value
4037 return self.__class__(
4039 expl=self._expl if expl is None else expl,
4040 optional=self.optional if optional is None else optional,
4043 def __bytes__(self):
4044 self._assert_ready()
4052 self._assert_ready()
4055 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4057 t, tlen, lv = tag_strip(tlv)
4058 except DecodeError as err:
4059 raise err.__class__(
4061 klass=self.__class__,
4062 decode_path=decode_path,
4066 l, llen, v = len_decode(lv)
4067 except LenIndefForm as err:
4068 if not ctx.get("bered", False):
4069 raise err.__class__(
4071 klass=self.__class__,
4072 decode_path=decode_path,
4075 llen, vlen, v = 1, 0, lv[1:]
4076 sub_offset = offset + tlen + llen
4078 while v[:EOC_LEN].tobytes() != EOC:
4079 chunk, v = Any().decode(
4082 decode_path=decode_path + (str(chunk_i),),
4086 vlen += chunk.tlvlen
4087 sub_offset += chunk.tlvlen
4089 tlvlen = tlen + llen + vlen + EOC_LEN
4090 obj = self.__class__(
4091 value=tlv[:tlvlen].tobytes(),
4093 optional=self.optional,
4094 _decoded=(offset, 0, tlvlen),
4098 return obj, v[EOC_LEN:]
4099 except DecodeError as err:
4100 raise err.__class__(
4102 klass=self.__class__,
4103 decode_path=decode_path,
4107 raise NotEnoughData(
4108 "encoded length is longer than data",
4109 klass=self.__class__,
4110 decode_path=decode_path,
4113 tlvlen = tlen + llen + l
4114 v, tail = tlv[:tlvlen], v[l:]
4115 obj = self.__class__(
4118 optional=self.optional,
4119 _decoded=(offset, 0, tlvlen),
4125 return pp_console_row(next(self.pps()))
4127 def pps(self, decode_path=()):
4129 asn1_type_name=self.asn1_type_name,
4130 obj_name=self.__class__.__name__,
4131 decode_path=decode_path,
4132 blob=self._value if self.ready else None,
4133 optional=self.optional,
4134 default=self == self.default,
4135 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4136 expl=None if self._expl is None else tag_decode(self._expl),
4141 expl_offset=self.expl_offset if self.expled else None,
4142 expl_tlen=self.expl_tlen if self.expled else None,
4143 expl_llen=self.expl_llen if self.expled else None,
4144 expl_vlen=self.expl_vlen if self.expled else None,
4145 expl_lenindef=self.expl_lenindef,
4146 lenindef=self.lenindef,
4148 defined_by, defined = self.defined or (None, None)
4149 if defined_by is not None:
4151 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4153 for pp in self.pps_lenindef(decode_path):
4157 ########################################################################
4158 # ASN.1 constructed types
4159 ########################################################################
4161 def get_def_by_path(defines_by_path, sub_decode_path):
4162 """Get define by decode path
4164 for path, define in defines_by_path:
4165 if len(path) != len(sub_decode_path):
4167 for p1, p2 in zip(path, sub_decode_path):
4168 if (p1 != any) and (p1 != p2):
4174 def abs_decode_path(decode_path, rel_path):
4175 """Create an absolute decode path from current and relative ones
4177 :param decode_path: current decode path, starting point.
4179 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4180 If first tuple's element is "/", then treat it as
4181 an absolute path, ignoring ``decode_path`` as
4182 starting point. Also this tuple can contain ".."
4183 elements, stripping the leading element from
4186 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4187 ("foo", "bar", "baz", "whatever")
4188 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4190 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4193 if rel_path[0] == "/":
4195 if rel_path[0] == "..":
4196 return abs_decode_path(decode_path[:-1], rel_path[1:])
4197 return decode_path + rel_path
4200 class Sequence(Obj):
4201 """``SEQUENCE`` structure type
4203 You have to make specification of sequence::
4205 class Extension(Sequence):
4207 ("extnID", ObjectIdentifier()),
4208 ("critical", Boolean(default=False)),
4209 ("extnValue", OctetString()),
4212 Then, you can work with it as with dictionary.
4214 >>> ext = Extension()
4215 >>> Extension().specs
4217 ('extnID', OBJECT IDENTIFIER),
4218 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4219 ('extnValue', OCTET STRING),
4221 >>> ext["extnID"] = "1.2.3"
4222 Traceback (most recent call last):
4223 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4224 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4226 You can determine if sequence is ready to be encoded:
4231 Traceback (most recent call last):
4232 pyderasn.ObjNotReady: object is not ready: extnValue
4233 >>> ext["extnValue"] = OctetString(b"foobar")
4237 Value you want to assign, must have the same **type** as in
4238 corresponding specification, but it can have different tags,
4239 optional/default attributes -- they will be taken from specification
4242 class TBSCertificate(Sequence):
4244 ("version", Version(expl=tag_ctxc(0), default="v1")),
4247 >>> tbs = TBSCertificate()
4248 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4250 Assign ``None`` to remove value from sequence.
4252 You can set values in Sequence during its initialization:
4254 >>> AlgorithmIdentifier((
4255 ("algorithm", ObjectIdentifier("1.2.3")),
4256 ("parameters", Any(Null()))
4258 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4260 You can determine if value exists/set in the sequence and take its value:
4262 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4265 OBJECT IDENTIFIER 1.2.3
4267 But pay attention that if value has default, then it won't be (not
4268 in) in the sequence (because ``DEFAULT`` must not be encoded in
4269 DER), but you can read its value:
4271 >>> "critical" in ext, ext["critical"]
4272 (False, BOOLEAN False)
4273 >>> ext["critical"] = Boolean(True)
4274 >>> "critical" in ext, ext["critical"]
4275 (True, BOOLEAN True)
4277 All defaulted values are always optional.
4279 .. _strict_default_existence_ctx:
4283 When decoded DER contains defaulted value inside, then
4284 technically this is not valid DER encoding. But we allow and pass
4285 it **by default**. Of course reencoding of that kind of DER will
4286 result in different binary representation (validly without
4287 defaulted value inside). You can enable strict defaulted values
4288 existence validation by setting ``"strict_default_existence":
4289 True`` :ref:`context <ctx>` option -- decoding process will raise
4290 an exception if defaulted value is met.
4292 Two sequences are equal if they have equal specification (schema),
4293 implicit/explicit tagging and the same values.
4295 __slots__ = ("specs",)
4296 tag_default = tag_encode(form=TagFormConstructed, num=16)
4297 asn1_type_name = "SEQUENCE"
4309 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4311 schema = getattr(self, "schema", ())
4313 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4316 if value is not None:
4317 if issubclass(value.__class__, Sequence):
4318 self._value = value._value
4319 elif hasattr(value, "__iter__"):
4320 for seq_key, seq_value in value:
4321 self[seq_key] = seq_value
4323 raise InvalidValueType((Sequence,))
4324 if default is not None:
4325 if not issubclass(default.__class__, Sequence):
4326 raise InvalidValueType((Sequence,))
4327 default_value = default._value
4328 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4329 default_obj.specs = self.specs
4330 default_obj._value = default_value
4331 self.default = default_obj
4333 self._value = default_obj.copy()._value
4337 for name, spec in self.specs.items():
4338 value = self._value.get(name)
4349 obj = self.__class__(schema=self.specs)
4351 obj._expl = self._expl
4352 obj.default = self.default
4353 obj.optional = self.optional
4354 obj.offset = self.offset
4355 obj.llen = self.llen
4356 obj.vlen = self.vlen
4357 obj._value = {k: v.copy() for k, v in self._value.items()}
4360 def __eq__(self, their):
4361 if not isinstance(their, self.__class__):
4364 self.specs == their.specs and
4365 self.tag == their.tag and
4366 self._expl == their._expl and
4367 self._value == their._value
4378 return self.__class__(
4381 impl=self.tag if impl is None else impl,
4382 expl=self._expl if expl is None else expl,
4383 default=self.default if default is None else default,
4384 optional=self.optional if optional is None else optional,
4387 def __contains__(self, key):
4388 return key in self._value
4390 def __setitem__(self, key, value):
4391 spec = self.specs.get(key)
4393 raise ObjUnknown(key)
4395 self._value.pop(key, None)
4397 if not isinstance(value, spec.__class__):
4398 raise InvalidValueType((spec.__class__,))
4399 value = spec(value=value)
4400 if spec.default is not None and value == spec.default:
4401 self._value.pop(key, None)
4403 self._value[key] = value
4405 def __getitem__(self, key):
4406 value = self._value.get(key)
4407 if value is not None:
4409 spec = self.specs.get(key)
4411 raise ObjUnknown(key)
4412 if spec.default is not None:
4416 def _encoded_values(self):
4418 for name, spec in self.specs.items():
4419 value = self._value.get(name)
4423 raise ObjNotReady(name)
4424 raws.append(value.encode())
4428 v = b"".join(self._encoded_values())
4429 return b"".join((self.tag, len_encode(len(v)), v))
4431 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4433 t, tlen, lv = tag_strip(tlv)
4434 except DecodeError as err:
4435 raise err.__class__(
4437 klass=self.__class__,
4438 decode_path=decode_path,
4443 klass=self.__class__,
4444 decode_path=decode_path,
4451 l, llen, v = len_decode(lv)
4452 except LenIndefForm as err:
4453 if not ctx.get("bered", False):
4454 raise err.__class__(
4456 klass=self.__class__,
4457 decode_path=decode_path,
4460 l, llen, v = 0, 1, lv[1:]
4462 except DecodeError as err:
4463 raise err.__class__(
4465 klass=self.__class__,
4466 decode_path=decode_path,
4470 raise NotEnoughData(
4471 "encoded length is longer than data",
4472 klass=self.__class__,
4473 decode_path=decode_path,
4477 v, tail = v[:l], v[l:]
4479 sub_offset = offset + tlen + llen
4481 for name, spec in self.specs.items():
4482 if spec.optional and (
4483 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4487 sub_decode_path = decode_path + (name,)
4489 value, v_tail = spec.decode(
4493 decode_path=sub_decode_path,
4501 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4502 if defined is not None:
4503 defined_by, defined_spec = defined
4504 if issubclass(value.__class__, SequenceOf):
4505 for i, _value in enumerate(value):
4506 sub_sub_decode_path = sub_decode_path + (
4508 DecodePathDefBy(defined_by),
4510 defined_value, defined_tail = defined_spec.decode(
4511 memoryview(bytes(_value)),
4513 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4514 if value.expled else (value.tlen + value.llen)
4517 decode_path=sub_sub_decode_path,
4520 if len(defined_tail) > 0:
4523 klass=self.__class__,
4524 decode_path=sub_sub_decode_path,
4527 _value.defined = (defined_by, defined_value)
4529 defined_value, defined_tail = defined_spec.decode(
4530 memoryview(bytes(value)),
4532 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4533 if value.expled else (value.tlen + value.llen)
4536 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4539 if len(defined_tail) > 0:
4542 klass=self.__class__,
4543 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4546 value.defined = (defined_by, defined_value)
4548 value_len = value.fulllen
4550 sub_offset += value_len
4552 if spec.default is not None and value == spec.default:
4553 if ctx.get("strict_default_existence", False):
4555 "DEFAULT value met",
4556 klass=self.__class__,
4557 decode_path=sub_decode_path,
4562 values[name] = value
4564 spec_defines = getattr(spec, "defines", ())
4565 if len(spec_defines) == 0:
4566 defines_by_path = ctx.get("defines_by_path", ())
4567 if len(defines_by_path) > 0:
4568 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4569 if spec_defines is not None and len(spec_defines) > 0:
4570 for rel_path, schema in spec_defines:
4571 defined = schema.get(value, None)
4572 if defined is not None:
4573 ctx.setdefault("defines", []).append((
4574 abs_decode_path(sub_decode_path[:-1], rel_path),
4578 if v[:EOC_LEN].tobytes() != EOC:
4581 klass=self.__class__,
4582 decode_path=decode_path,
4590 klass=self.__class__,
4591 decode_path=decode_path,
4594 obj = self.__class__(
4598 default=self.default,
4599 optional=self.optional,
4600 _decoded=(offset, llen, vlen),
4603 obj.lenindef = lenindef
4607 value = pp_console_row(next(self.pps()))
4609 for name in self.specs:
4610 _value = self._value.get(name)
4613 cols.append("%s: %s" % (name, repr(_value)))
4614 return "%s[%s]" % (value, "; ".join(cols))
4616 def pps(self, decode_path=()):
4618 asn1_type_name=self.asn1_type_name,
4619 obj_name=self.__class__.__name__,
4620 decode_path=decode_path,
4621 optional=self.optional,
4622 default=self == self.default,
4623 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4624 expl=None if self._expl is None else tag_decode(self._expl),
4629 expl_offset=self.expl_offset if self.expled else None,
4630 expl_tlen=self.expl_tlen if self.expled else None,
4631 expl_llen=self.expl_llen if self.expled else None,
4632 expl_vlen=self.expl_vlen if self.expled else None,
4633 expl_lenindef=self.expl_lenindef,
4634 lenindef=self.lenindef,
4636 for name in self.specs:
4637 value = self._value.get(name)
4640 yield value.pps(decode_path=decode_path + (name,))
4641 for pp in self.pps_lenindef(decode_path):
4645 class Set(Sequence):
4646 """``SET`` structure type
4648 Its usage is identical to :py:class:`pyderasn.Sequence`.
4651 tag_default = tag_encode(form=TagFormConstructed, num=17)
4652 asn1_type_name = "SET"
4655 raws = self._encoded_values()
4658 return b"".join((self.tag, len_encode(len(v)), v))
4660 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4662 t, tlen, lv = tag_strip(tlv)
4663 except DecodeError as err:
4664 raise err.__class__(
4666 klass=self.__class__,
4667 decode_path=decode_path,
4672 klass=self.__class__,
4673 decode_path=decode_path,
4680 l, llen, v = len_decode(lv)
4681 except LenIndefForm as err:
4682 if not ctx.get("bered", False):
4683 raise err.__class__(
4685 klass=self.__class__,
4686 decode_path=decode_path,
4689 l, llen, v = 0, 1, lv[1:]
4691 except DecodeError as err:
4692 raise err.__class__(
4694 klass=self.__class__,
4695 decode_path=decode_path,
4699 raise NotEnoughData(
4700 "encoded length is longer than data",
4701 klass=self.__class__,
4705 v, tail = v[:l], v[l:]
4707 sub_offset = offset + tlen + llen
4709 specs_items = self.specs.items
4711 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4713 for name, spec in specs_items():
4714 sub_decode_path = decode_path + (name,)
4720 decode_path=sub_decode_path,
4729 klass=self.__class__,
4730 decode_path=decode_path,
4733 value, v_tail = spec.decode(
4737 decode_path=sub_decode_path,
4740 value_len = value.fulllen
4741 sub_offset += value_len
4744 if spec.default is None or value != spec.default: # pragma: no cover
4745 # SeqMixing.test_encoded_default_accepted covers that place
4746 values[name] = value
4747 obj = self.__class__(
4751 default=self.default,
4752 optional=self.optional,
4753 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4757 if v[:EOC_LEN].tobytes() != EOC:
4760 klass=self.__class__,
4761 decode_path=decode_path,
4768 "not all values are ready",
4769 klass=self.__class__,
4770 decode_path=decode_path,
4776 class SequenceOf(Obj):
4777 """``SEQUENCE OF`` sequence type
4779 For that kind of type you must specify the object it will carry on
4780 (bounds are for example here, not required)::
4782 class Ints(SequenceOf):
4787 >>> ints.append(Integer(123))
4788 >>> ints.append(Integer(234))
4790 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4791 >>> [int(i) for i in ints]
4793 >>> ints.append(Integer(345))
4794 Traceback (most recent call last):
4795 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4798 >>> ints[1] = Integer(345)
4800 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4802 Also you can initialize sequence with preinitialized values:
4804 >>> ints = Ints([Integer(123), Integer(234)])
4806 __slots__ = ("spec", "_bound_min", "_bound_max")
4807 tag_default = tag_encode(form=TagFormConstructed, num=16)
4808 asn1_type_name = "SEQUENCE OF"
4821 super(SequenceOf, self).__init__(
4829 schema = getattr(self, "schema", None)
4831 raise ValueError("schema must be specified")
4833 self._bound_min, self._bound_max = getattr(
4837 ) if bounds is None else bounds
4839 if value is not None:
4840 self._value = self._value_sanitize(value)
4841 if default is not None:
4842 default_value = self._value_sanitize(default)
4843 default_obj = self.__class__(
4848 default_obj._value = default_value
4849 self.default = default_obj
4851 self._value = default_obj.copy()._value
4853 def _value_sanitize(self, value):
4854 if issubclass(value.__class__, SequenceOf):
4855 value = value._value
4856 elif hasattr(value, "__iter__"):
4859 raise InvalidValueType((self.__class__, iter))
4860 if not self._bound_min <= len(value) <= self._bound_max:
4861 raise BoundsError(self._bound_min, len(value), self._bound_max)
4863 if not isinstance(v, self.spec.__class__):
4864 raise InvalidValueType((self.spec.__class__,))
4869 return all(v.ready for v in self._value)
4872 obj = self.__class__(schema=self.spec)
4873 obj._bound_min = self._bound_min
4874 obj._bound_max = self._bound_max
4876 obj._expl = self._expl
4877 obj.default = self.default
4878 obj.optional = self.optional
4879 obj.offset = self.offset
4880 obj.llen = self.llen
4881 obj.vlen = self.vlen
4882 obj._value = [v.copy() for v in self._value]
4885 def __eq__(self, their):
4886 if isinstance(their, self.__class__):
4888 self.spec == their.spec and
4889 self.tag == their.tag and
4890 self._expl == their._expl and
4891 self._value == their._value
4893 if hasattr(their, "__iter__"):
4894 return self._value == list(their)
4906 return self.__class__(
4910 (self._bound_min, self._bound_max)
4911 if bounds is None else bounds
4913 impl=self.tag if impl is None else impl,
4914 expl=self._expl if expl is None else expl,
4915 default=self.default if default is None else default,
4916 optional=self.optional if optional is None else optional,
4919 def __contains__(self, key):
4920 return key in self._value
4922 def append(self, value):
4923 if not isinstance(value, self.spec.__class__):
4924 raise InvalidValueType((self.spec.__class__,))
4925 if len(self._value) + 1 > self._bound_max:
4928 len(self._value) + 1,
4931 self._value.append(value)
4934 self._assert_ready()
4935 return iter(self._value)
4938 self._assert_ready()
4939 return len(self._value)
4941 def __setitem__(self, key, value):
4942 if not isinstance(value, self.spec.__class__):
4943 raise InvalidValueType((self.spec.__class__,))
4944 self._value[key] = self.spec(value=value)
4946 def __getitem__(self, key):
4947 return self._value[key]
4949 def _encoded_values(self):
4950 return [v.encode() for v in self._value]
4953 v = b"".join(self._encoded_values())
4954 return b"".join((self.tag, len_encode(len(v)), v))
4956 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4958 t, tlen, lv = tag_strip(tlv)
4959 except DecodeError as err:
4960 raise err.__class__(
4962 klass=self.__class__,
4963 decode_path=decode_path,
4968 klass=self.__class__,
4969 decode_path=decode_path,
4976 l, llen, v = len_decode(lv)
4977 except LenIndefForm as err:
4978 if not ctx.get("bered", False):
4979 raise err.__class__(
4981 klass=self.__class__,
4982 decode_path=decode_path,
4985 l, llen, v = 0, 1, lv[1:]
4987 except DecodeError as err:
4988 raise err.__class__(
4990 klass=self.__class__,
4991 decode_path=decode_path,
4995 raise NotEnoughData(
4996 "encoded length is longer than data",
4997 klass=self.__class__,
4998 decode_path=decode_path,
5002 v, tail = v[:l], v[l:]
5004 sub_offset = offset + tlen + llen
5008 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5010 value, v_tail = spec.decode(
5014 decode_path=decode_path + (str(len(_value)),),
5017 value_len = value.fulllen
5018 sub_offset += value_len
5021 _value.append(value)
5022 obj = self.__class__(
5025 bounds=(self._bound_min, self._bound_max),
5028 default=self.default,
5029 optional=self.optional,
5030 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5033 if v[:EOC_LEN].tobytes() != EOC:
5036 klass=self.__class__,
5037 decode_path=decode_path,
5046 pp_console_row(next(self.pps())),
5047 ", ".join(repr(v) for v in self._value),
5050 def pps(self, decode_path=()):
5052 asn1_type_name=self.asn1_type_name,
5053 obj_name=self.__class__.__name__,
5054 decode_path=decode_path,
5055 optional=self.optional,
5056 default=self == self.default,
5057 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5058 expl=None if self._expl is None else tag_decode(self._expl),
5063 expl_offset=self.expl_offset if self.expled else None,
5064 expl_tlen=self.expl_tlen if self.expled else None,
5065 expl_llen=self.expl_llen if self.expled else None,
5066 expl_vlen=self.expl_vlen if self.expled else None,
5067 expl_lenindef=self.expl_lenindef,
5068 lenindef=self.lenindef,
5070 for i, value in enumerate(self._value):
5071 yield value.pps(decode_path=decode_path + (str(i),))
5072 for pp in self.pps_lenindef(decode_path):
5076 class SetOf(SequenceOf):
5077 """``SET OF`` sequence type
5079 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5082 tag_default = tag_encode(form=TagFormConstructed, num=17)
5083 asn1_type_name = "SET OF"
5086 raws = self._encoded_values()
5089 return b"".join((self.tag, len_encode(len(v)), v))
5092 def obj_by_path(pypath): # pragma: no cover
5093 """Import object specified as string Python path
5095 Modules must be separated from classes/functions with ``:``.
5097 >>> obj_by_path("foo.bar:Baz")
5098 <class 'foo.bar.Baz'>
5099 >>> obj_by_path("foo.bar:Baz.boo")
5100 <classmethod 'foo.bar.Baz.boo'>
5102 mod, objs = pypath.rsplit(":", 1)
5103 from importlib import import_module
5104 obj = import_module(mod)
5105 for obj_name in objs.split("."):
5106 obj = getattr(obj, obj_name)
5110 def generic_decoder(): # pragma: no cover
5111 # All of this below is a big hack with self references
5112 choice = PrimitiveTypes()
5113 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5114 choice.specs["SetOf"] = SetOf(schema=choice)
5116 choice.specs["SequenceOf%d" % i] = SequenceOf(
5120 choice.specs["Any"] = Any()
5122 # Class name equals to type name, to omit it from output
5123 class SEQUENCEOF(SequenceOf):
5127 def pprint_any(obj, oids=None, with_colours=False):
5128 def _pprint_pps(pps):
5130 if hasattr(pp, "_fields"):
5131 if pp.asn1_type_name == Choice.asn1_type_name:
5133 pp_kwargs = pp._asdict()
5134 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5135 pp = _pp(**pp_kwargs)
5136 yield pp_console_row(
5141 with_colours=with_colours,
5143 for row in pp_console_blob(pp):
5146 for row in _pprint_pps(pp):
5148 return "\n".join(_pprint_pps(obj.pps()))
5149 return SEQUENCEOF(), pprint_any
5152 def main(): # pragma: no cover
5154 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5155 parser.add_argument(
5159 help="Skip that number of bytes from the beginning",
5161 parser.add_argument(
5163 help="Python path to dictionary with OIDs",
5165 parser.add_argument(
5167 help="Python path to schema definition to use",
5169 parser.add_argument(
5170 "--defines-by-path",
5171 help="Python path to decoder's defines_by_path",
5173 parser.add_argument(
5175 action='store_true',
5176 help="Disallow BER encoding",
5178 parser.add_argument(
5180 type=argparse.FileType("rb"),
5181 help="Path to DER file you want to decode",
5183 args = parser.parse_args()
5184 args.DERFile.seek(args.skip)
5185 der = memoryview(args.DERFile.read())
5186 args.DERFile.close()
5187 oids = obj_by_path(args.oids) if args.oids else {}
5189 schema = obj_by_path(args.schema)
5190 from functools import partial
5191 pprinter = partial(pprint, big_blobs=True)
5193 schema, pprinter = generic_decoder()
5194 ctx = {"bered": not args.nobered}
5195 if args.defines_by_path is not None:
5196 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5197 obj, tail = schema().decode(der, ctx=ctx)
5201 with_colours=True if environ.get("NO_COLOR") is None else False,
5204 print("\nTrailing data: %s" % hexenc(tail))
5207 if __name__ == "__main__":