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:
192 ``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
193 lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
194 (that actually equals to ordinary ``tlvlen``).
196 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
203 You can specify so called context keyword argument during ``decode()``
204 invocation. It is dictionary containing various options governing
207 Currently available context options:
209 * :ref:`bered <bered_ctx>`
210 * :ref:`defines_by_path <defines_by_path_ctx>`
211 * :ref:`strict_default_existence <strict_default_existence_ctx>`
218 All objects have ``pps()`` method, that is a generator of
219 :py:class:`pyderasn.PP` namedtuple, holding various raw information
220 about the object. If ``pps`` is called on sequences, then all underlying
221 ``PP`` will be yielded.
223 You can use :py:func:`pyderasn.pp_console_row` function, converting
224 those ``PP`` to human readable string. Actually exactly it is used for
225 all object ``repr``. But it is easy to write custom formatters.
227 >>> from pyderasn import pprint
228 >>> encoded = Integer(-12345).encode()
229 >>> obj, tail = Integer().decode(encoded)
230 >>> print(pprint(obj))
231 0 [1,1, 2] INTEGER -12345
238 ASN.1 structures often have ANY and OCTET STRING fields, that are
239 DEFINED BY some previously met ObjectIdentifier. This library provides
240 ability to specify mapping between some OID and field that must be
241 decoded with specific specification.
246 :py:class:`pyderasn.ObjectIdentifier` field inside
247 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
248 necessary for decoding structures. For example, CMS (:rfc:`5652`)
251 class ContentInfo(Sequence):
253 ("contentType", ContentType(defines=((("content",), {
254 id_digestedData: DigestedData(),
255 id_signedData: SignedData(),
257 ("content", Any(expl=tag_ctxc(0))),
260 ``contentType`` field tells that it defines that ``content`` must be
261 decoded with ``SignedData`` specification, if ``contentType`` equals to
262 ``id-signedData``. The same applies to ``DigestedData``. If
263 ``contentType`` contains unknown OID, then no automatic decoding is
266 You can specify multiple fields, that will be autodecoded -- that is why
267 ``defines`` kwarg is a sequence. You can specify defined field
268 relatively or absolutely to current decode path. For example ``defines``
269 for AlgorithmIdentifier of X.509's
270 ``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
274 id_ecPublicKey: ECParameters(),
275 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
277 (("..", "subjectPublicKey"), {
278 id_rsaEncryption: RSAPublicKey(),
279 id_GostR3410_2001: OctetString(),
283 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
284 autodecode its parameters inside SPKI's algorithm and its public key
287 Following types can be automatically decoded (DEFINED BY):
289 * :py:class:`pyderasn.Any`
290 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
291 * :py:class:`pyderasn.OctetString`
292 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
293 ``Any``/``BitString``/``OctetString``-s
295 When any of those fields is automatically decoded, then ``.defined``
296 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
297 was defined, ``value`` contains corresponding decoded value. For example
298 above, ``content_info["content"].defined == (id_signedData,
301 .. _defines_by_path_ctx:
303 defines_by_path context option
304 ______________________________
306 Sometimes you either can not or do not want to explicitly set *defines*
307 in the scheme. You can dynamically apply those definitions when calling
308 ``.decode()`` method.
310 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
311 value must be sequence of following tuples::
313 (decode_path, defines)
315 where ``decode_path`` is a tuple holding so-called decode path to the
316 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
317 ``defines``, holding exactly the same value as accepted in its keyword
320 For example, again for CMS, you want to automatically decode
321 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
322 structures it may hold. Also, automatically decode ``controlSequence``
325 content_info, tail = ContentInfo().decode(data, defines_by_path=(
328 ((("content",), {id_signedData: SignedData()}),),
333 DecodePathDefBy(id_signedData),
338 id_cct_PKIData: PKIData(),
339 id_cct_PKIResponse: PKIResponse(),
345 DecodePathDefBy(id_signedData),
348 DecodePathDefBy(id_cct_PKIResponse),
354 id_cmc_recipientNonce: RecipientNonce(),
355 id_cmc_senderNonce: SenderNonce(),
356 id_cmc_statusInfoV2: CMCStatusInfoV2(),
357 id_cmc_transactionId: TransactionId(),
362 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
363 First function is useful for path construction when some automatic
364 decoding is already done. ``any`` means literally any value it meet --
365 useful for SEQUENCE/SET OF-s.
372 By default PyDERASN accepts only DER encoded data. It always encodes to
373 DER. But you can optionally enable BER decoding with setting ``bered``
374 :ref:`context <ctx>` argument to True. Indefinite lengths and
375 constructed primitive types should be parsed successfully.
377 * If object is encoded in BER form (not the DER one), then ``bered``
378 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
379 STRING`` can contain it.
380 * If object has an indefinite length encoding, then its ``lenindef``
381 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
382 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
384 * If object has an indefinite length encoded explicit tag, then
385 ``expl_lenindef`` is set to True.
387 EOC (end-of-contents) token's length is taken in advance in object's
395 .. autoclass:: pyderasn.Boolean
400 .. autoclass:: pyderasn.Integer
405 .. autoclass:: pyderasn.BitString
410 .. autoclass:: pyderasn.OctetString
415 .. autoclass:: pyderasn.Null
420 .. autoclass:: pyderasn.ObjectIdentifier
425 .. autoclass:: pyderasn.Enumerated
429 .. autoclass:: pyderasn.CommonString
433 .. autoclass:: pyderasn.NumericString
437 .. autoclass:: pyderasn.UTCTime
438 :members: __init__, todatetime
442 .. autoclass:: pyderasn.GeneralizedTime
449 .. autoclass:: pyderasn.Choice
454 .. autoclass:: PrimitiveTypes
458 .. autoclass:: pyderasn.Any
466 .. autoclass:: pyderasn.Sequence
471 .. autoclass:: pyderasn.Set
476 .. autoclass:: pyderasn.SequenceOf
481 .. autoclass:: pyderasn.SetOf
487 .. autofunction:: pyderasn.abs_decode_path
488 .. autofunction:: pyderasn.hexenc
489 .. autofunction:: pyderasn.hexdec
490 .. autofunction:: pyderasn.tag_encode
491 .. autofunction:: pyderasn.tag_decode
492 .. autofunction:: pyderasn.tag_ctxp
493 .. autofunction:: pyderasn.tag_ctxc
494 .. autoclass:: pyderasn.Obj
495 .. autoclass:: pyderasn.DecodeError
497 .. autoclass:: pyderasn.NotEnoughData
498 .. autoclass:: pyderasn.LenIndefForm
499 .. autoclass:: pyderasn.TagMismatch
500 .. autoclass:: pyderasn.InvalidLength
501 .. autoclass:: pyderasn.InvalidOID
502 .. autoclass:: pyderasn.ObjUnknown
503 .. autoclass:: pyderasn.ObjNotReady
504 .. autoclass:: pyderasn.InvalidValueType
505 .. autoclass:: pyderasn.BoundsError
508 from codecs import getdecoder
509 from codecs import getencoder
510 from collections import namedtuple
511 from collections import OrderedDict
512 from datetime import datetime
513 from math import ceil
514 from os import environ
515 from string import digits
517 from six import add_metaclass
518 from six import binary_type
519 from six import byte2int
520 from six import indexbytes
521 from six import int2byte
522 from six import integer_types
523 from six import iterbytes
525 from six import string_types
526 from six import text_type
527 from six.moves import xrange as six_xrange
531 from termcolor import colored
533 def colored(what, *args):
577 "TagClassApplication",
581 "TagFormConstructed",
592 TagClassUniversal = 0
593 TagClassApplication = 1 << 6
594 TagClassContext = 1 << 7
595 TagClassPrivate = 1 << 6 | 1 << 7
597 TagFormConstructed = 1 << 5
600 TagClassApplication: "APPLICATION ",
601 TagClassPrivate: "PRIVATE ",
602 TagClassUniversal: "UNIV ",
606 LENINDEF = b"\x80" # length indefinite mark
609 ########################################################################
611 ########################################################################
613 class DecodeError(Exception):
614 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
616 :param str msg: reason of decode failing
617 :param klass: optional exact DecodeError inherited class (like
618 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
619 :py:exc:`InvalidLength`)
620 :param decode_path: tuple of strings. It contains human
621 readable names of the fields through which
622 decoding process has passed
623 :param int offset: binary offset where failure happened
625 super(DecodeError, self).__init__()
628 self.decode_path = decode_path
634 "" if self.klass is None else self.klass.__name__,
636 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
637 if len(self.decode_path) > 0 else ""
639 ("(at %d)" % self.offset) if self.offset > 0 else "",
645 return "%s(%s)" % (self.__class__.__name__, self)
648 class NotEnoughData(DecodeError):
652 class LenIndefForm(DecodeError):
656 class TagMismatch(DecodeError):
660 class InvalidLength(DecodeError):
664 class InvalidOID(DecodeError):
668 class ObjUnknown(ValueError):
669 def __init__(self, name):
670 super(ObjUnknown, self).__init__()
674 return "object is unknown: %s" % self.name
677 return "%s(%s)" % (self.__class__.__name__, self)
680 class ObjNotReady(ValueError):
681 def __init__(self, name):
682 super(ObjNotReady, self).__init__()
686 return "object is not ready: %s" % self.name
689 return "%s(%s)" % (self.__class__.__name__, self)
692 class InvalidValueType(ValueError):
693 def __init__(self, expected_types):
694 super(InvalidValueType, self).__init__()
695 self.expected_types = expected_types
698 return "invalid value type, expected: %s" % ", ".join(
699 [repr(t) for t in self.expected_types]
703 return "%s(%s)" % (self.__class__.__name__, self)
706 class BoundsError(ValueError):
707 def __init__(self, bound_min, value, bound_max):
708 super(BoundsError, self).__init__()
709 self.bound_min = bound_min
711 self.bound_max = bound_max
714 return "unsatisfied bounds: %s <= %s <= %s" % (
721 return "%s(%s)" % (self.__class__.__name__, self)
724 ########################################################################
726 ########################################################################
728 _hexdecoder = getdecoder("hex")
729 _hexencoder = getencoder("hex")
733 """Binary data to hexadecimal string convert
735 return _hexdecoder(data)[0]
739 """Hexadecimal string to binary data convert
741 return _hexencoder(data)[0].decode("ascii")
744 def int_bytes_len(num, byte_len=8):
747 return int(ceil(float(num.bit_length()) / byte_len))
750 def zero_ended_encode(num):
751 octets = bytearray(int_bytes_len(num, 7))
753 octets[i] = num & 0x7F
757 octets[i] = 0x80 | (num & 0x7F)
763 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
764 """Encode tag to binary form
766 :param int num: tag's number
767 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
768 :py:data:`pyderasn.TagClassContext`,
769 :py:data:`pyderasn.TagClassApplication`,
770 :py:data:`pyderasn.TagClassPrivate`)
771 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
772 :py:data:`pyderasn.TagFormConstructed`)
776 return int2byte(klass | form | num)
777 # [XX|X|11111][1.......][1.......] ... [0.......]
778 return int2byte(klass | form | 31) + zero_ended_encode(num)
782 """Decode tag from binary form
786 No validation is performed, assuming that it has already passed.
788 It returns tuple with three integers, as
789 :py:func:`pyderasn.tag_encode` accepts.
791 first_octet = byte2int(tag)
792 klass = first_octet & 0xC0
793 form = first_octet & 0x20
794 if first_octet & 0x1F < 0x1F:
795 return (klass, form, first_octet & 0x1F)
797 for octet in iterbytes(tag[1:]):
800 return (klass, form, num)
804 """Create CONTEXT PRIMITIVE tag
806 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
810 """Create CONTEXT CONSTRUCTED tag
812 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
816 """Take off tag from the data
818 :returns: (encoded tag, tag length, remaining data)
821 raise NotEnoughData("no data at all")
822 if byte2int(data) & 0x1F < 31:
823 return data[:1], 1, data[1:]
828 raise DecodeError("unfinished tag")
829 if indexbytes(data, i) & 0x80 == 0:
832 return data[:i], i, data[i:]
838 octets = bytearray(int_bytes_len(l) + 1)
839 octets[0] = 0x80 | (len(octets) - 1)
840 for i in six_xrange(len(octets) - 1, 0, -1):
846 def len_decode(data):
849 :returns: (decoded length, length's length, remaining data)
850 :raises LenIndefForm: if indefinite form encoding is met
853 raise NotEnoughData("no data at all")
854 first_octet = byte2int(data)
855 if first_octet & 0x80 == 0:
856 return first_octet, 1, data[1:]
857 octets_num = first_octet & 0x7F
858 if octets_num + 1 > len(data):
859 raise NotEnoughData("encoded length is longer than data")
862 if byte2int(data[1:]) == 0:
863 raise DecodeError("leading zeros")
865 for v in iterbytes(data[1:1 + octets_num]):
868 raise DecodeError("long form instead of short one")
869 return l, 1 + octets_num, data[1 + octets_num:]
872 ########################################################################
874 ########################################################################
876 class AutoAddSlots(type):
877 def __new__(mcs, name, bases, _dict):
878 _dict["__slots__"] = _dict.get("__slots__", ())
879 return type.__new__(mcs, name, bases, _dict)
882 @add_metaclass(AutoAddSlots)
884 """Common ASN.1 object class
886 All ASN.1 types are inherited from it. It has metaclass that
887 automatically adds ``__slots__`` to all inherited classes.
911 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
912 self._expl = getattr(self, "expl", None) if expl is None else expl
913 if self.tag != self.tag_default and self._expl is not None:
914 raise ValueError("implicit and explicit tags can not be set simultaneously")
915 if default is not None:
917 self.optional = optional
918 self.offset, self.llen, self.vlen = _decoded
920 self.expl_lenindef = False
921 self.lenindef = False
925 def ready(self): # pragma: no cover
926 """Is object ready to be encoded?
928 raise NotImplementedError()
930 def _assert_ready(self):
932 raise ObjNotReady(self.__class__.__name__)
936 """Is object decoded?
938 return (self.llen + self.vlen) > 0
940 def copy(self): # pragma: no cover
941 """Make a copy of object, safe to be mutated
943 raise NotImplementedError()
951 return self.tlen + self.llen + self.vlen
953 def __str__(self): # pragma: no cover
954 return self.__bytes__() if PY2 else self.__unicode__()
956 def __ne__(self, their):
957 return not(self == their)
959 def __gt__(self, their): # pragma: no cover
960 return not(self < their)
962 def __le__(self, their): # pragma: no cover
963 return (self == their) or (self < their)
965 def __ge__(self, their): # pragma: no cover
966 return (self == their) or (self > their)
968 def _encode(self): # pragma: no cover
969 raise NotImplementedError()
971 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
972 raise NotImplementedError()
976 if self._expl is None:
978 return b"".join((self._expl, len_encode(len(raw)), raw))
991 :param data: either binary or memoryview
992 :param int offset: initial data's offset
993 :param bool leavemm: do we need to leave memoryview of remaining
994 data as is, or convert it to bytes otherwise
995 :param ctx: optional :ref:`context <ctx>` governing decoding process.
996 :param tag_only: decode only the tag, without length and contents
997 (used only in Choice and Set structures, trying to
998 determine if tag satisfies the scheme)
999 :returns: (Obj, remaining data)
1003 tlv = memoryview(data)
1004 if self._expl is None:
1005 result = self._decode(
1008 decode_path=decode_path,
1017 t, tlen, lv = tag_strip(tlv)
1018 except DecodeError as err:
1019 raise err.__class__(
1021 klass=self.__class__,
1022 decode_path=decode_path,
1027 klass=self.__class__,
1028 decode_path=decode_path,
1032 l, llen, v = len_decode(lv)
1033 except LenIndefForm as err:
1034 if not ctx.get("bered", False):
1035 raise err.__class__(
1037 klass=self.__class__,
1038 decode_path=decode_path,
1042 offset += tlen + llen
1043 result = self._decode(
1046 decode_path=decode_path,
1053 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1054 if eoc_expected.tobytes() != EOC:
1057 klass=self.__class__,
1058 decode_path=decode_path,
1062 obj.expl_lenindef = True
1063 except DecodeError as err:
1064 raise err.__class__(
1066 klass=self.__class__,
1067 decode_path=decode_path,
1072 raise NotEnoughData(
1073 "encoded length is longer than data",
1074 klass=self.__class__,
1075 decode_path=decode_path,
1078 result = self._decode(
1080 offset=offset + tlen + llen,
1081 decode_path=decode_path,
1088 return obj, (tail if leavemm else tail.tobytes())
1092 return self._expl is not None
1099 def expl_tlen(self):
1100 return len(self._expl)
1103 def expl_llen(self):
1104 if self.expl_lenindef:
1106 return len(len_encode(self.tlvlen))
1109 def expl_offset(self):
1110 return self.offset - self.expl_tlen - self.expl_llen
1113 def expl_vlen(self):
1117 def expl_tlvlen(self):
1118 return self.expl_tlen + self.expl_llen + self.expl_vlen
1121 class DecodePathDefBy(object):
1122 """DEFINED BY representation inside decode path
1124 __slots__ = ("defined_by",)
1126 def __init__(self, defined_by):
1127 self.defined_by = defined_by
1129 def __ne__(self, their):
1130 return not(self == their)
1132 def __eq__(self, their):
1133 if not isinstance(their, self.__class__):
1135 return self.defined_by == their.defined_by
1138 return "DEFINED BY " + str(self.defined_by)
1141 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1144 ########################################################################
1146 ########################################################################
1148 PP = namedtuple("PP", (
1173 asn1_type_name="unknown",
1190 expl_lenindef=False,
1218 def _colorize(what, colour, with_colours, attrs=("bold",)):
1219 return colored(what, colour, attrs=attrs) if with_colours else what
1234 " " if pp.expl_offset is None else
1235 ("-%d" % (pp.offset - pp.expl_offset))
1238 cols.append(_colorize(col, "red", with_colours, ()))
1239 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1240 col = _colorize(col, "green", with_colours, ())
1242 if pp.expl_lenindef:
1247 " " if ber_deoffset == 0 else
1248 _colorize(("-%d" % ber_deoffset), "red", with_colours)
1251 if len(pp.decode_path) > 0:
1252 cols.append(" ." * (len(pp.decode_path)))
1253 ent = pp.decode_path[-1]
1254 if isinstance(ent, DecodePathDefBy):
1255 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1256 value = str(ent.defined_by)
1258 oids is not None and
1259 ent.defined_by.asn1_type_name ==
1260 ObjectIdentifier.asn1_type_name and
1263 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1265 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1267 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1268 if pp.expl is not None:
1269 klass, _, num = pp.expl
1270 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1271 cols.append(_colorize(col, "blue", with_colours))
1272 if pp.impl is not None:
1273 klass, _, num = pp.impl
1274 col = "[%s%d]" % (TagClassReprs[klass], num)
1275 cols.append(_colorize(col, "blue", with_colours))
1276 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1277 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1279 cols.append(_colorize("BER", "red", with_colours))
1280 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1281 if pp.value is not None:
1283 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1285 oids is not None and
1286 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1289 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1291 if isinstance(pp.blob, binary_type):
1292 cols.append(hexenc(pp.blob))
1293 elif isinstance(pp.blob, tuple):
1294 cols.append(", ".join(pp.blob))
1296 cols.append(_colorize("OPTIONAL", "red", with_colours))
1298 cols.append(_colorize("DEFAULT", "red", with_colours))
1299 return " ".join(cols)
1302 def pp_console_blob(pp):
1303 cols = [" " * len("XXXXXYY [X,X,XXXX]YY")]
1304 if len(pp.decode_path) > 0:
1305 cols.append(" ." * (len(pp.decode_path) + 1))
1306 if isinstance(pp.blob, binary_type):
1307 blob = hexenc(pp.blob).upper()
1308 for i in range(0, len(blob), 32):
1309 chunk = blob[i:i + 32]
1310 yield " ".join(cols + [":".join(
1311 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1313 elif isinstance(pp.blob, tuple):
1314 yield " ".join(cols + [", ".join(pp.blob)])
1317 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1318 """Pretty print object
1320 :param Obj obj: object you want to pretty print
1321 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1322 from it is met, then its humand readable form is printed
1323 :param big_blobs: if large binary objects are met (like OctetString
1324 values), do we need to print them too, on separate
1326 :param with_colours: colourize output, if ``termcolor`` library
1329 def _pprint_pps(pps):
1331 if hasattr(pp, "_fields"):
1333 yield pp_console_row(
1338 with_colours=with_colours,
1340 for row in pp_console_blob(pp):
1343 yield pp_console_row(
1348 with_colours=with_colours,
1351 for row in _pprint_pps(pp):
1353 return "\n".join(_pprint_pps(obj.pps()))
1356 ########################################################################
1357 # ASN.1 primitive types
1358 ########################################################################
1361 """``BOOLEAN`` boolean type
1363 >>> b = Boolean(True)
1365 >>> b == Boolean(True)
1371 tag_default = tag_encode(1)
1372 asn1_type_name = "BOOLEAN"
1384 :param value: set the value. Either boolean type, or
1385 :py:class:`pyderasn.Boolean` object
1386 :param bytes impl: override default tag with ``IMPLICIT`` one
1387 :param bytes expl: override default tag with ``EXPLICIT`` one
1388 :param default: set default value. Type same as in ``value``
1389 :param bool optional: is object ``OPTIONAL`` in sequence
1391 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1392 self._value = None if value is None else self._value_sanitize(value)
1393 if default is not None:
1394 default = self._value_sanitize(default)
1395 self.default = self.__class__(
1401 self._value = default
1403 def _value_sanitize(self, value):
1404 if issubclass(value.__class__, Boolean):
1406 if isinstance(value, bool):
1408 raise InvalidValueType((self.__class__, bool))
1412 return self._value is not None
1415 obj = self.__class__()
1416 obj._value = self._value
1418 obj._expl = self._expl
1419 obj.default = self.default
1420 obj.optional = self.optional
1421 obj.offset = self.offset
1422 obj.llen = self.llen
1423 obj.vlen = self.vlen
1426 def __nonzero__(self):
1427 self._assert_ready()
1431 self._assert_ready()
1434 def __eq__(self, their):
1435 if isinstance(their, bool):
1436 return self._value == their
1437 if not issubclass(their.__class__, Boolean):
1440 self._value == their._value and
1441 self.tag == their.tag and
1442 self._expl == their._expl
1453 return self.__class__(
1455 impl=self.tag if impl is None else impl,
1456 expl=self._expl if expl is None else expl,
1457 default=self.default if default is None else default,
1458 optional=self.optional if optional is None else optional,
1462 self._assert_ready()
1466 (b"\xFF" if self._value else b"\x00"),
1469 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1471 t, _, lv = tag_strip(tlv)
1472 except DecodeError as err:
1473 raise err.__class__(
1475 klass=self.__class__,
1476 decode_path=decode_path,
1481 klass=self.__class__,
1482 decode_path=decode_path,
1488 l, _, v = len_decode(lv)
1489 except DecodeError as err:
1490 raise err.__class__(
1492 klass=self.__class__,
1493 decode_path=decode_path,
1497 raise InvalidLength(
1498 "Boolean's length must be equal to 1",
1499 klass=self.__class__,
1500 decode_path=decode_path,
1504 raise NotEnoughData(
1505 "encoded length is longer than data",
1506 klass=self.__class__,
1507 decode_path=decode_path,
1510 first_octet = byte2int(v)
1512 if first_octet == 0:
1514 elif first_octet == 0xFF:
1516 elif ctx.get("bered", False):
1521 "unacceptable Boolean value",
1522 klass=self.__class__,
1523 decode_path=decode_path,
1526 obj = self.__class__(
1530 default=self.default,
1531 optional=self.optional,
1532 _decoded=(offset, 1, 1),
1538 return pp_console_row(next(self.pps()))
1540 def pps(self, decode_path=()):
1542 asn1_type_name=self.asn1_type_name,
1543 obj_name=self.__class__.__name__,
1544 decode_path=decode_path,
1545 value=str(self._value) if self.ready else None,
1546 optional=self.optional,
1547 default=self == self.default,
1548 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1549 expl=None if self._expl is None else tag_decode(self._expl),
1554 expl_offset=self.expl_offset if self.expled else None,
1555 expl_tlen=self.expl_tlen if self.expled else None,
1556 expl_llen=self.expl_llen if self.expled else None,
1557 expl_vlen=self.expl_vlen if self.expled else None,
1558 expl_lenindef=self.expl_lenindef,
1564 """``INTEGER`` integer type
1566 >>> b = Integer(-123)
1568 >>> b == Integer(-123)
1573 >>> Integer(2, bounds=(1, 3))
1575 >>> Integer(5, bounds=(1, 3))
1576 Traceback (most recent call last):
1577 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1581 class Version(Integer):
1588 >>> v = Version("v1")
1595 {'v3': 2, 'v1': 0, 'v2': 1}
1597 __slots__ = ("specs", "_bound_min", "_bound_max")
1598 tag_default = tag_encode(2)
1599 asn1_type_name = "INTEGER"
1613 :param value: set the value. Either integer type, named value
1614 (if ``schema`` is specified in the class), or
1615 :py:class:`pyderasn.Integer` object
1616 :param bounds: set ``(MIN, MAX)`` value constraint.
1617 (-inf, +inf) by default
1618 :param bytes impl: override default tag with ``IMPLICIT`` one
1619 :param bytes expl: override default tag with ``EXPLICIT`` one
1620 :param default: set default value. Type same as in ``value``
1621 :param bool optional: is object ``OPTIONAL`` in sequence
1623 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1625 specs = getattr(self, "schema", {}) if _specs is None else _specs
1626 self.specs = specs if isinstance(specs, dict) else dict(specs)
1627 self._bound_min, self._bound_max = getattr(
1630 (float("-inf"), float("+inf")),
1631 ) if bounds is None else bounds
1632 if value is not None:
1633 self._value = self._value_sanitize(value)
1634 if default is not None:
1635 default = self._value_sanitize(default)
1636 self.default = self.__class__(
1642 if self._value is None:
1643 self._value = default
1645 def _value_sanitize(self, value):
1646 if issubclass(value.__class__, Integer):
1647 value = value._value
1648 elif isinstance(value, integer_types):
1650 elif isinstance(value, str):
1651 value = self.specs.get(value)
1653 raise ObjUnknown("integer value: %s" % value)
1655 raise InvalidValueType((self.__class__, int, str))
1656 if not self._bound_min <= value <= self._bound_max:
1657 raise BoundsError(self._bound_min, value, self._bound_max)
1662 return self._value is not None
1665 obj = self.__class__(_specs=self.specs)
1666 obj._value = self._value
1667 obj._bound_min = self._bound_min
1668 obj._bound_max = self._bound_max
1670 obj._expl = self._expl
1671 obj.default = self.default
1672 obj.optional = self.optional
1673 obj.offset = self.offset
1674 obj.llen = self.llen
1675 obj.vlen = self.vlen
1679 self._assert_ready()
1680 return int(self._value)
1683 self._assert_ready()
1686 bytes(self._expl or b"") +
1687 str(self._value).encode("ascii"),
1690 def __eq__(self, their):
1691 if isinstance(their, integer_types):
1692 return self._value == their
1693 if not issubclass(their.__class__, Integer):
1696 self._value == their._value and
1697 self.tag == their.tag and
1698 self._expl == their._expl
1701 def __lt__(self, their):
1702 return self._value < their._value
1706 for name, value in self.specs.items():
1707 if value == self._value:
1719 return self.__class__(
1722 (self._bound_min, self._bound_max)
1723 if bounds is None else bounds
1725 impl=self.tag if impl is None else impl,
1726 expl=self._expl if expl is None else expl,
1727 default=self.default if default is None else default,
1728 optional=self.optional if optional is None else optional,
1733 self._assert_ready()
1737 octets = bytearray([0])
1741 octets = bytearray()
1743 octets.append((value & 0xFF) ^ 0xFF)
1745 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1748 octets = bytearray()
1750 octets.append(value & 0xFF)
1752 if octets[-1] & 0x80 > 0:
1755 octets = bytes(octets)
1757 bytes_len = ceil(value.bit_length() / 8) or 1
1760 octets = value.to_bytes(
1765 except OverflowError:
1769 return b"".join((self.tag, len_encode(len(octets)), octets))
1771 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1773 t, _, lv = tag_strip(tlv)
1774 except DecodeError as err:
1775 raise err.__class__(
1777 klass=self.__class__,
1778 decode_path=decode_path,
1783 klass=self.__class__,
1784 decode_path=decode_path,
1790 l, llen, v = len_decode(lv)
1791 except DecodeError as err:
1792 raise err.__class__(
1794 klass=self.__class__,
1795 decode_path=decode_path,
1799 raise NotEnoughData(
1800 "encoded length is longer than data",
1801 klass=self.__class__,
1802 decode_path=decode_path,
1806 raise NotEnoughData(
1808 klass=self.__class__,
1809 decode_path=decode_path,
1812 v, tail = v[:l], v[l:]
1813 first_octet = byte2int(v)
1815 second_octet = byte2int(v[1:])
1817 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1818 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1821 "non normalized integer",
1822 klass=self.__class__,
1823 decode_path=decode_path,
1828 if first_octet & 0x80 > 0:
1829 octets = bytearray()
1830 for octet in bytearray(v):
1831 octets.append(octet ^ 0xFF)
1832 for octet in octets:
1833 value = (value << 8) | octet
1837 for octet in bytearray(v):
1838 value = (value << 8) | octet
1840 value = int.from_bytes(v, byteorder="big", signed=True)
1842 obj = self.__class__(
1844 bounds=(self._bound_min, self._bound_max),
1847 default=self.default,
1848 optional=self.optional,
1850 _decoded=(offset, llen, l),
1852 except BoundsError as err:
1855 klass=self.__class__,
1856 decode_path=decode_path,
1862 return pp_console_row(next(self.pps()))
1864 def pps(self, decode_path=()):
1866 asn1_type_name=self.asn1_type_name,
1867 obj_name=self.__class__.__name__,
1868 decode_path=decode_path,
1869 value=(self.named or str(self._value)) if self.ready else None,
1870 optional=self.optional,
1871 default=self == self.default,
1872 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1873 expl=None if self._expl is None else tag_decode(self._expl),
1878 expl_offset=self.expl_offset if self.expled else None,
1879 expl_tlen=self.expl_tlen if self.expled else None,
1880 expl_llen=self.expl_llen if self.expled else None,
1881 expl_vlen=self.expl_vlen if self.expled else None,
1882 expl_lenindef=self.expl_lenindef,
1886 class BitString(Obj):
1887 """``BIT STRING`` bit string type
1889 >>> BitString(b"hello world")
1890 BIT STRING 88 bits 68656c6c6f20776f726c64
1893 >>> b == b"hello world"
1898 >>> BitString("'0A3B5F291CD'H")
1899 BIT STRING 44 bits 0a3b5f291cd0
1900 >>> b = BitString("'010110000000'B")
1901 BIT STRING 12 bits 5800
1904 >>> b[0], b[1], b[2], b[3]
1905 (False, True, False, True)
1909 [False, True, False, True, True, False, False, False, False, False, False, False]
1913 class KeyUsage(BitString):
1915 ("digitalSignature", 0),
1916 ("nonRepudiation", 1),
1917 ("keyEncipherment", 2),
1920 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1921 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1923 ['nonRepudiation', 'keyEncipherment']
1925 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1929 Pay attention that BIT STRING can be encoded both in primitive
1930 and constructed forms. Decoder always checks constructed form tag
1931 additionally to specified primitive one. If BER decoding is
1932 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
1933 of DER restrictions.
1935 __slots__ = ("tag_constructed", "specs", "defined")
1936 tag_default = tag_encode(3)
1937 asn1_type_name = "BIT STRING"
1950 :param value: set the value. Either binary type, tuple of named
1951 values (if ``schema`` is specified in the class),
1952 string in ``'XXX...'B`` form, or
1953 :py:class:`pyderasn.BitString` object
1954 :param bytes impl: override default tag with ``IMPLICIT`` one
1955 :param bytes expl: override default tag with ``EXPLICIT`` one
1956 :param default: set default value. Type same as in ``value``
1957 :param bool optional: is object ``OPTIONAL`` in sequence
1959 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1960 specs = getattr(self, "schema", {}) if _specs is None else _specs
1961 self.specs = specs if isinstance(specs, dict) else dict(specs)
1962 self._value = None if value is None else self._value_sanitize(value)
1963 if default is not None:
1964 default = self._value_sanitize(default)
1965 self.default = self.__class__(
1971 self._value = default
1973 tag_klass, _, tag_num = tag_decode(self.tag)
1974 self.tag_constructed = tag_encode(
1976 form=TagFormConstructed,
1980 def _bits2octets(self, bits):
1981 if len(self.specs) > 0:
1982 bits = bits.rstrip("0")
1984 bits += "0" * ((8 - (bit_len % 8)) % 8)
1985 octets = bytearray(len(bits) // 8)
1986 for i in six_xrange(len(octets)):
1987 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1988 return bit_len, bytes(octets)
1990 def _value_sanitize(self, value):
1991 if issubclass(value.__class__, BitString):
1993 if isinstance(value, (string_types, binary_type)):
1995 isinstance(value, string_types) and
1996 value.startswith("'")
1998 if value.endswith("'B"):
2000 if not set(value) <= set(("0", "1")):
2001 raise ValueError("B's coding contains unacceptable chars")
2002 return self._bits2octets(value)
2003 elif value.endswith("'H"):
2007 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2009 if isinstance(value, binary_type):
2010 return (len(value) * 8, value)
2012 raise InvalidValueType((self.__class__, string_types, binary_type))
2013 if isinstance(value, tuple):
2016 isinstance(value[0], integer_types) and
2017 isinstance(value[1], binary_type)
2022 bit = self.specs.get(name)
2024 raise ObjUnknown("BitString value: %s" % name)
2027 return self._bits2octets("")
2029 return self._bits2octets("".join(
2030 ("1" if bit in bits else "0")
2031 for bit in six_xrange(max(bits) + 1)
2033 raise InvalidValueType((self.__class__, binary_type, string_types))
2037 return self._value is not None
2040 obj = self.__class__(_specs=self.specs)
2042 if value is not None:
2043 value = (value[0], value[1])
2046 obj._expl = self._expl
2047 obj.default = self.default
2048 obj.optional = self.optional
2049 obj.offset = self.offset
2050 obj.llen = self.llen
2051 obj.vlen = self.vlen
2055 self._assert_ready()
2056 for i in six_xrange(self._value[0]):
2061 self._assert_ready()
2062 return self._value[0]
2064 def __bytes__(self):
2065 self._assert_ready()
2066 return self._value[1]
2068 def __eq__(self, their):
2069 if isinstance(their, bytes):
2070 return self._value[1] == their
2071 if not issubclass(their.__class__, BitString):
2074 self._value == their._value and
2075 self.tag == their.tag and
2076 self._expl == their._expl
2081 return [name for name, bit in self.specs.items() if self[bit]]
2091 return self.__class__(
2093 impl=self.tag if impl is None else impl,
2094 expl=self._expl if expl is None else expl,
2095 default=self.default if default is None else default,
2096 optional=self.optional if optional is None else optional,
2100 def __getitem__(self, key):
2101 if isinstance(key, int):
2102 bit_len, octets = self._value
2106 byte2int(memoryview(octets)[key // 8:]) >>
2109 if isinstance(key, string_types):
2110 value = self.specs.get(key)
2112 raise ObjUnknown("BitString value: %s" % key)
2114 raise InvalidValueType((int, str))
2117 self._assert_ready()
2118 bit_len, octets = self._value
2121 len_encode(len(octets) + 1),
2122 int2byte((8 - bit_len % 8) % 8),
2126 def _decode_chunk(self, lv, offset, decode_path, ctx):
2128 l, llen, v = len_decode(lv)
2129 except DecodeError as err:
2130 raise err.__class__(
2132 klass=self.__class__,
2133 decode_path=decode_path,
2137 raise NotEnoughData(
2138 "encoded length is longer than data",
2139 klass=self.__class__,
2140 decode_path=decode_path,
2144 raise NotEnoughData(
2146 klass=self.__class__,
2147 decode_path=decode_path,
2150 pad_size = byte2int(v)
2151 if l == 1 and pad_size != 0:
2153 "invalid empty value",
2154 klass=self.__class__,
2155 decode_path=decode_path,
2161 klass=self.__class__,
2162 decode_path=decode_path,
2165 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2168 klass=self.__class__,
2169 decode_path=decode_path,
2172 v, tail = v[:l], v[l:]
2173 obj = self.__class__(
2174 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2177 default=self.default,
2178 optional=self.optional,
2180 _decoded=(offset, llen, l),
2184 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2186 t, tlen, lv = tag_strip(tlv)
2187 except DecodeError as err:
2188 raise err.__class__(
2190 klass=self.__class__,
2191 decode_path=decode_path,
2197 return self._decode_chunk(lv, offset, decode_path, ctx)
2198 if t == self.tag_constructed:
2199 if not ctx.get("bered", False):
2201 "unallowed BER constructed encoding",
2202 klass=self.__class__,
2203 decode_path=decode_path,
2210 l, llen, v = len_decode(lv)
2211 except LenIndefForm:
2212 llen, l, v = 1, 0, lv[1:]
2214 except DecodeError as err:
2215 raise err.__class__(
2217 klass=self.__class__,
2218 decode_path=decode_path,
2222 raise NotEnoughData(
2223 "encoded length is longer than data",
2224 klass=self.__class__,
2225 decode_path=decode_path,
2228 if not lenindef and l == 0:
2229 raise NotEnoughData(
2231 klass=self.__class__,
2232 decode_path=decode_path,
2236 sub_offset = offset + tlen + llen
2240 if v[:EOC_LEN].tobytes() == EOC:
2247 "chunk out of bounds",
2248 klass=self.__class__,
2249 decode_path=decode_path + (str(len(chunks) - 1),),
2250 offset=chunks[-1].offset,
2252 sub_decode_path = decode_path + (str(len(chunks)),)
2254 chunk, v_tail = BitString().decode(
2257 decode_path=sub_decode_path,
2263 "expected BitString encoded chunk",
2264 klass=self.__class__,
2265 decode_path=sub_decode_path,
2268 chunks.append(chunk)
2269 sub_offset += chunk.tlvlen
2270 vlen += chunk.tlvlen
2272 if len(chunks) == 0:
2275 klass=self.__class__,
2276 decode_path=decode_path,
2281 for chunk_i, chunk in enumerate(chunks[:-1]):
2282 if chunk.bit_len % 8 != 0:
2284 "BitString chunk is not multiple of 8 bits",
2285 klass=self.__class__,
2286 decode_path=decode_path + (str(chunk_i),),
2287 offset=chunk.offset,
2289 values.append(bytes(chunk))
2290 bit_len += chunk.bit_len
2291 chunk_last = chunks[-1]
2292 values.append(bytes(chunk_last))
2293 bit_len += chunk_last.bit_len
2294 obj = self.__class__(
2295 value=(bit_len, b"".join(values)),
2298 default=self.default,
2299 optional=self.optional,
2301 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2303 obj.lenindef = lenindef
2305 return obj, (v[EOC_LEN:] if lenindef else v)
2307 klass=self.__class__,
2308 decode_path=decode_path,
2313 return pp_console_row(next(self.pps()))
2315 def pps(self, decode_path=()):
2319 bit_len, blob = self._value
2320 value = "%d bits" % bit_len
2321 if len(self.specs) > 0:
2322 blob = tuple(self.named)
2324 asn1_type_name=self.asn1_type_name,
2325 obj_name=self.__class__.__name__,
2326 decode_path=decode_path,
2329 optional=self.optional,
2330 default=self == self.default,
2331 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2332 expl=None if self._expl is None else tag_decode(self._expl),
2337 expl_offset=self.expl_offset if self.expled else None,
2338 expl_tlen=self.expl_tlen if self.expled else None,
2339 expl_llen=self.expl_llen if self.expled else None,
2340 expl_vlen=self.expl_vlen if self.expled else None,
2341 expl_lenindef=self.expl_lenindef,
2342 lenindef=self.lenindef,
2345 defined_by, defined = self.defined or (None, None)
2346 if defined_by is not None:
2348 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2352 class OctetString(Obj):
2353 """``OCTET STRING`` binary string type
2355 >>> s = OctetString(b"hello world")
2356 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2357 >>> s == OctetString(b"hello world")
2362 >>> OctetString(b"hello", bounds=(4, 4))
2363 Traceback (most recent call last):
2364 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2365 >>> OctetString(b"hell", bounds=(4, 4))
2366 OCTET STRING 4 bytes 68656c6c
2370 Pay attention that OCTET STRING can be encoded both in primitive
2371 and constructed forms. Decoder always checks constructed form tag
2372 additionally to specified primitive one. If BER decoding is
2373 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2374 of DER restrictions.
2376 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2377 tag_default = tag_encode(4)
2378 asn1_type_name = "OCTET STRING"
2391 :param value: set the value. Either binary type, or
2392 :py:class:`pyderasn.OctetString` object
2393 :param bounds: set ``(MIN, MAX)`` value size constraint.
2394 (-inf, +inf) by default
2395 :param bytes impl: override default tag with ``IMPLICIT`` one
2396 :param bytes expl: override default tag with ``EXPLICIT`` one
2397 :param default: set default value. Type same as in ``value``
2398 :param bool optional: is object ``OPTIONAL`` in sequence
2400 super(OctetString, self).__init__(
2408 self._bound_min, self._bound_max = getattr(
2412 ) if bounds is None else bounds
2413 if value is not None:
2414 self._value = self._value_sanitize(value)
2415 if default is not None:
2416 default = self._value_sanitize(default)
2417 self.default = self.__class__(
2422 if self._value is None:
2423 self._value = default
2425 tag_klass, _, tag_num = tag_decode(self.tag)
2426 self.tag_constructed = tag_encode(
2428 form=TagFormConstructed,
2432 def _value_sanitize(self, value):
2433 if issubclass(value.__class__, OctetString):
2434 value = value._value
2435 elif isinstance(value, binary_type):
2438 raise InvalidValueType((self.__class__, bytes))
2439 if not self._bound_min <= len(value) <= self._bound_max:
2440 raise BoundsError(self._bound_min, len(value), self._bound_max)
2445 return self._value is not None
2448 obj = self.__class__()
2449 obj._value = self._value
2450 obj._bound_min = self._bound_min
2451 obj._bound_max = self._bound_max
2453 obj._expl = self._expl
2454 obj.default = self.default
2455 obj.optional = self.optional
2456 obj.offset = self.offset
2457 obj.llen = self.llen
2458 obj.vlen = self.vlen
2461 def __bytes__(self):
2462 self._assert_ready()
2465 def __eq__(self, their):
2466 if isinstance(their, binary_type):
2467 return self._value == their
2468 if not issubclass(their.__class__, OctetString):
2471 self._value == their._value and
2472 self.tag == their.tag and
2473 self._expl == their._expl
2476 def __lt__(self, their):
2477 return self._value < their._value
2488 return self.__class__(
2491 (self._bound_min, self._bound_max)
2492 if bounds is None else bounds
2494 impl=self.tag if impl is None else impl,
2495 expl=self._expl if expl is None else expl,
2496 default=self.default if default is None else default,
2497 optional=self.optional if optional is None else optional,
2501 self._assert_ready()
2504 len_encode(len(self._value)),
2508 def _decode_chunk(self, lv, offset, decode_path, ctx):
2510 l, llen, v = len_decode(lv)
2511 except DecodeError as err:
2512 raise err.__class__(
2514 klass=self.__class__,
2515 decode_path=decode_path,
2519 raise NotEnoughData(
2520 "encoded length is longer than data",
2521 klass=self.__class__,
2522 decode_path=decode_path,
2525 v, tail = v[:l], v[l:]
2527 obj = self.__class__(
2529 bounds=(self._bound_min, self._bound_max),
2532 default=self.default,
2533 optional=self.optional,
2534 _decoded=(offset, llen, l),
2536 except DecodeError as err:
2539 klass=self.__class__,
2540 decode_path=decode_path,
2543 except BoundsError as err:
2546 klass=self.__class__,
2547 decode_path=decode_path,
2552 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2554 t, tlen, lv = tag_strip(tlv)
2555 except DecodeError as err:
2556 raise err.__class__(
2558 klass=self.__class__,
2559 decode_path=decode_path,
2565 return self._decode_chunk(lv, offset, decode_path, ctx)
2566 if t == self.tag_constructed:
2567 if not ctx.get("bered", False):
2569 "unallowed BER constructed encoding",
2570 klass=self.__class__,
2571 decode_path=decode_path,
2578 l, llen, v = len_decode(lv)
2579 except LenIndefForm:
2580 llen, l, v = 1, 0, lv[1:]
2582 except DecodeError as err:
2583 raise err.__class__(
2585 klass=self.__class__,
2586 decode_path=decode_path,
2590 raise NotEnoughData(
2591 "encoded length is longer than data",
2592 klass=self.__class__,
2593 decode_path=decode_path,
2597 sub_offset = offset + tlen + llen
2601 if v[:EOC_LEN].tobytes() == EOC:
2608 "chunk out of bounds",
2609 klass=self.__class__,
2610 decode_path=decode_path + (str(len(chunks) - 1),),
2611 offset=chunks[-1].offset,
2613 sub_decode_path = decode_path + (str(len(chunks)),)
2615 chunk, v_tail = OctetString().decode(
2618 decode_path=sub_decode_path,
2624 "expected OctetString encoded chunk",
2625 klass=self.__class__,
2626 decode_path=sub_decode_path,
2629 chunks.append(chunk)
2630 sub_offset += chunk.tlvlen
2631 vlen += chunk.tlvlen
2634 obj = self.__class__(
2635 value=b"".join(bytes(chunk) for chunk in chunks),
2636 bounds=(self._bound_min, self._bound_max),
2639 default=self.default,
2640 optional=self.optional,
2641 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2643 except DecodeError as err:
2646 klass=self.__class__,
2647 decode_path=decode_path,
2650 except BoundsError as err:
2653 klass=self.__class__,
2654 decode_path=decode_path,
2657 obj.lenindef = lenindef
2659 return obj, (v[EOC_LEN:] if lenindef else v)
2661 klass=self.__class__,
2662 decode_path=decode_path,
2667 return pp_console_row(next(self.pps()))
2669 def pps(self, decode_path=()):
2671 asn1_type_name=self.asn1_type_name,
2672 obj_name=self.__class__.__name__,
2673 decode_path=decode_path,
2674 value=("%d bytes" % len(self._value)) if self.ready else None,
2675 blob=self._value if self.ready else None,
2676 optional=self.optional,
2677 default=self == self.default,
2678 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2679 expl=None if self._expl is None else tag_decode(self._expl),
2684 expl_offset=self.expl_offset if self.expled else None,
2685 expl_tlen=self.expl_tlen if self.expled else None,
2686 expl_llen=self.expl_llen if self.expled else None,
2687 expl_vlen=self.expl_vlen if self.expled else None,
2688 expl_lenindef=self.expl_lenindef,
2689 lenindef=self.lenindef,
2692 defined_by, defined = self.defined or (None, None)
2693 if defined_by is not None:
2695 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2700 """``NULL`` null object
2708 tag_default = tag_encode(5)
2709 asn1_type_name = "NULL"
2713 value=None, # unused, but Sequence passes it
2720 :param bytes impl: override default tag with ``IMPLICIT`` one
2721 :param bytes expl: override default tag with ``EXPLICIT`` one
2722 :param bool optional: is object ``OPTIONAL`` in sequence
2724 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2732 obj = self.__class__()
2734 obj._expl = self._expl
2735 obj.default = self.default
2736 obj.optional = self.optional
2737 obj.offset = self.offset
2738 obj.llen = self.llen
2739 obj.vlen = self.vlen
2742 def __eq__(self, their):
2743 if not issubclass(their.__class__, Null):
2746 self.tag == their.tag and
2747 self._expl == their._expl
2757 return self.__class__(
2758 impl=self.tag if impl is None else impl,
2759 expl=self._expl if expl is None else expl,
2760 optional=self.optional if optional is None else optional,
2764 return self.tag + len_encode(0)
2766 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2768 t, _, lv = tag_strip(tlv)
2769 except DecodeError as err:
2770 raise err.__class__(
2772 klass=self.__class__,
2773 decode_path=decode_path,
2778 klass=self.__class__,
2779 decode_path=decode_path,
2785 l, _, v = len_decode(lv)
2786 except DecodeError as err:
2787 raise err.__class__(
2789 klass=self.__class__,
2790 decode_path=decode_path,
2794 raise InvalidLength(
2795 "Null must have zero length",
2796 klass=self.__class__,
2797 decode_path=decode_path,
2800 obj = self.__class__(
2803 optional=self.optional,
2804 _decoded=(offset, 1, 0),
2809 return pp_console_row(next(self.pps()))
2811 def pps(self, decode_path=()):
2813 asn1_type_name=self.asn1_type_name,
2814 obj_name=self.__class__.__name__,
2815 decode_path=decode_path,
2816 optional=self.optional,
2817 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2818 expl=None if self._expl is None else tag_decode(self._expl),
2823 expl_offset=self.expl_offset if self.expled else None,
2824 expl_tlen=self.expl_tlen if self.expled else None,
2825 expl_llen=self.expl_llen if self.expled else None,
2826 expl_vlen=self.expl_vlen if self.expled else None,
2827 expl_lenindef=self.expl_lenindef,
2831 class ObjectIdentifier(Obj):
2832 """``OBJECT IDENTIFIER`` OID type
2834 >>> oid = ObjectIdentifier((1, 2, 3))
2835 OBJECT IDENTIFIER 1.2.3
2836 >>> oid == ObjectIdentifier("1.2.3")
2842 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2843 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2845 >>> str(ObjectIdentifier((3, 1)))
2846 Traceback (most recent call last):
2847 pyderasn.InvalidOID: unacceptable first arc value
2849 __slots__ = ("defines",)
2850 tag_default = tag_encode(6)
2851 asn1_type_name = "OBJECT IDENTIFIER"
2864 :param value: set the value. Either tuples of integers,
2865 string of "."-concatenated integers, or
2866 :py:class:`pyderasn.ObjectIdentifier` object
2867 :param defines: sequence of tuples. Each tuple has two elements.
2868 First one is relative to current one decode
2869 path, aiming to the field defined by that OID.
2870 Read about relative path in
2871 :py:func:`pyderasn.abs_decode_path`. Second
2872 tuple element is ``{OID: pyderasn.Obj()}``
2873 dictionary, mapping between current OID value
2874 and structure applied to defined field.
2875 :ref:`Read about DEFINED BY <definedby>`
2876 :param bytes impl: override default tag with ``IMPLICIT`` one
2877 :param bytes expl: override default tag with ``EXPLICIT`` one
2878 :param default: set default value. Type same as in ``value``
2879 :param bool optional: is object ``OPTIONAL`` in sequence
2881 super(ObjectIdentifier, self).__init__(
2889 if value is not None:
2890 self._value = self._value_sanitize(value)
2891 if default is not None:
2892 default = self._value_sanitize(default)
2893 self.default = self.__class__(
2898 if self._value is None:
2899 self._value = default
2900 self.defines = defines
2902 def __add__(self, their):
2903 if isinstance(their, self.__class__):
2904 return self.__class__(self._value + their._value)
2905 if isinstance(their, tuple):
2906 return self.__class__(self._value + their)
2907 raise InvalidValueType((self.__class__, tuple))
2909 def _value_sanitize(self, value):
2910 if issubclass(value.__class__, ObjectIdentifier):
2912 if isinstance(value, string_types):
2914 value = tuple(int(arc) for arc in value.split("."))
2916 raise InvalidOID("unacceptable arcs values")
2917 if isinstance(value, tuple):
2919 raise InvalidOID("less than 2 arcs")
2920 first_arc = value[0]
2921 if first_arc in (0, 1):
2922 if not (0 <= value[1] <= 39):
2923 raise InvalidOID("second arc is too wide")
2924 elif first_arc == 2:
2927 raise InvalidOID("unacceptable first arc value")
2929 raise InvalidValueType((self.__class__, str, tuple))
2933 return self._value is not None
2936 obj = self.__class__()
2937 obj._value = self._value
2938 obj.defines = self.defines
2940 obj._expl = self._expl
2941 obj.default = self.default
2942 obj.optional = self.optional
2943 obj.offset = self.offset
2944 obj.llen = self.llen
2945 obj.vlen = self.vlen
2949 self._assert_ready()
2950 return iter(self._value)
2953 return ".".join(str(arc) for arc in self._value or ())
2956 self._assert_ready()
2959 bytes(self._expl or b"") +
2960 str(self._value).encode("ascii"),
2963 def __eq__(self, their):
2964 if isinstance(their, tuple):
2965 return self._value == their
2966 if not issubclass(their.__class__, ObjectIdentifier):
2969 self.tag == their.tag and
2970 self._expl == their._expl and
2971 self._value == their._value
2974 def __lt__(self, their):
2975 return self._value < their._value
2986 return self.__class__(
2988 defines=self.defines if defines is None else defines,
2989 impl=self.tag if impl is None else impl,
2990 expl=self._expl if expl is None else expl,
2991 default=self.default if default is None else default,
2992 optional=self.optional if optional is None else optional,
2996 self._assert_ready()
2998 first_value = value[1]
2999 first_arc = value[0]
3002 elif first_arc == 1:
3004 elif first_arc == 2:
3006 else: # pragma: no cover
3007 raise RuntimeError("invalid arc is stored")
3008 octets = [zero_ended_encode(first_value)]
3009 for arc in value[2:]:
3010 octets.append(zero_ended_encode(arc))
3011 v = b"".join(octets)
3012 return b"".join((self.tag, len_encode(len(v)), v))
3014 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3016 t, _, lv = tag_strip(tlv)
3017 except DecodeError as err:
3018 raise err.__class__(
3020 klass=self.__class__,
3021 decode_path=decode_path,
3026 klass=self.__class__,
3027 decode_path=decode_path,
3033 l, llen, v = len_decode(lv)
3034 except DecodeError as err:
3035 raise err.__class__(
3037 klass=self.__class__,
3038 decode_path=decode_path,
3042 raise NotEnoughData(
3043 "encoded length is longer than data",
3044 klass=self.__class__,
3045 decode_path=decode_path,
3049 raise NotEnoughData(
3051 klass=self.__class__,
3052 decode_path=decode_path,
3055 v, tail = v[:l], v[l:]
3061 octet = indexbytes(v, i)
3062 arc = (arc << 7) | (octet & 0x7F)
3063 if octet & 0x80 == 0:
3071 klass=self.__class__,
3072 decode_path=decode_path,
3076 second_arc = arcs[0]
3077 if 0 <= second_arc <= 39:
3079 elif 40 <= second_arc <= 79:
3085 obj = self.__class__(
3086 value=tuple([first_arc, second_arc] + arcs[1:]),
3089 default=self.default,
3090 optional=self.optional,
3091 _decoded=(offset, llen, l),
3096 return pp_console_row(next(self.pps()))
3098 def pps(self, decode_path=()):
3100 asn1_type_name=self.asn1_type_name,
3101 obj_name=self.__class__.__name__,
3102 decode_path=decode_path,
3103 value=str(self) if self.ready else None,
3104 optional=self.optional,
3105 default=self == self.default,
3106 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3107 expl=None if self._expl is None else tag_decode(self._expl),
3112 expl_offset=self.expl_offset if self.expled else None,
3113 expl_tlen=self.expl_tlen if self.expled else None,
3114 expl_llen=self.expl_llen if self.expled else None,
3115 expl_vlen=self.expl_vlen if self.expled else None,
3116 expl_lenindef=self.expl_lenindef,
3120 class Enumerated(Integer):
3121 """``ENUMERATED`` integer type
3123 This type is identical to :py:class:`pyderasn.Integer`, but requires
3124 schema to be specified and does not accept values missing from it.
3127 tag_default = tag_encode(10)
3128 asn1_type_name = "ENUMERATED"
3139 bounds=None, # dummy argument, workability for Integer.decode
3141 super(Enumerated, self).__init__(
3150 if len(self.specs) == 0:
3151 raise ValueError("schema must be specified")
3153 def _value_sanitize(self, value):
3154 if isinstance(value, self.__class__):
3155 value = value._value
3156 elif isinstance(value, integer_types):
3157 if value not in list(self.specs.values()):
3159 "unknown integer value: %s" % value,
3160 klass=self.__class__,
3162 elif isinstance(value, string_types):
3163 value = self.specs.get(value)
3165 raise ObjUnknown("integer value: %s" % value)
3167 raise InvalidValueType((self.__class__, int, str))
3171 obj = self.__class__(_specs=self.specs)
3172 obj._value = self._value
3173 obj._bound_min = self._bound_min
3174 obj._bound_max = self._bound_max
3176 obj._expl = self._expl
3177 obj.default = self.default
3178 obj.optional = self.optional
3179 obj.offset = self.offset
3180 obj.llen = self.llen
3181 obj.vlen = self.vlen
3193 return self.__class__(
3195 impl=self.tag if impl is None else impl,
3196 expl=self._expl if expl is None else expl,
3197 default=self.default if default is None else default,
3198 optional=self.optional if optional is None else optional,
3203 class CommonString(OctetString):
3204 """Common class for all strings
3206 Everything resembles :py:class:`pyderasn.OctetString`, except
3207 ability to deal with unicode text strings.
3209 >>> hexenc("привет мир".encode("utf-8"))
3210 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3211 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3213 >>> s = UTF8String("привет мир")
3214 UTF8String UTF8String привет мир
3216 'привет мир'
3217 >>> hexenc(bytes(s))
3218 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3220 >>> PrintableString("привет мир")
3221 Traceback (most recent call last):
3222 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3224 >>> BMPString("ада", bounds=(2, 2))
3225 Traceback (most recent call last):
3226 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3227 >>> s = BMPString("ад", bounds=(2, 2))
3230 >>> hexenc(bytes(s))
3238 * - :py:class:`pyderasn.UTF8String`
3240 * - :py:class:`pyderasn.NumericString`
3242 * - :py:class:`pyderasn.PrintableString`
3244 * - :py:class:`pyderasn.TeletexString`
3246 * - :py:class:`pyderasn.T61String`
3248 * - :py:class:`pyderasn.VideotexString`
3250 * - :py:class:`pyderasn.IA5String`
3252 * - :py:class:`pyderasn.GraphicString`
3254 * - :py:class:`pyderasn.VisibleString`
3256 * - :py:class:`pyderasn.ISO646String`
3258 * - :py:class:`pyderasn.GeneralString`
3260 * - :py:class:`pyderasn.UniversalString`
3262 * - :py:class:`pyderasn.BMPString`
3265 __slots__ = ("encoding",)
3267 def _value_sanitize(self, value):
3269 value_decoded = None
3270 if isinstance(value, self.__class__):
3271 value_raw = value._value
3272 elif isinstance(value, text_type):
3273 value_decoded = value
3274 elif isinstance(value, binary_type):
3277 raise InvalidValueType((self.__class__, text_type, binary_type))
3280 value_decoded.encode(self.encoding)
3281 if value_raw is None else value_raw
3284 value_raw.decode(self.encoding)
3285 if value_decoded is None else value_decoded
3287 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3288 raise DecodeError(str(err))
3289 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3297 def __eq__(self, their):
3298 if isinstance(their, binary_type):
3299 return self._value == their
3300 if isinstance(their, text_type):
3301 return self._value == their.encode(self.encoding)
3302 if not isinstance(their, self.__class__):
3305 self._value == their._value and
3306 self.tag == their.tag and
3307 self._expl == their._expl
3310 def __unicode__(self):
3312 return self._value.decode(self.encoding)
3313 return text_type(self._value)
3316 return pp_console_row(next(self.pps(no_unicode=PY2)))
3318 def pps(self, decode_path=(), no_unicode=False):
3321 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3323 asn1_type_name=self.asn1_type_name,
3324 obj_name=self.__class__.__name__,
3325 decode_path=decode_path,
3327 optional=self.optional,
3328 default=self == self.default,
3329 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3330 expl=None if self._expl is None else tag_decode(self._expl),
3335 expl_offset=self.expl_offset if self.expled else None,
3336 expl_tlen=self.expl_tlen if self.expled else None,
3337 expl_llen=self.expl_llen if self.expled else None,
3338 expl_vlen=self.expl_vlen if self.expled else None,
3339 expl_lenindef=self.expl_lenindef,
3343 class UTF8String(CommonString):
3345 tag_default = tag_encode(12)
3347 asn1_type_name = "UTF8String"
3350 class NumericString(CommonString):
3353 Its value is properly sanitized: only ASCII digits can be stored.
3356 tag_default = tag_encode(18)
3358 asn1_type_name = "NumericString"
3359 allowable_chars = set(digits.encode("ascii"))
3361 def _value_sanitize(self, value):
3362 value = super(NumericString, self)._value_sanitize(value)
3363 if not set(value) <= self.allowable_chars:
3364 raise DecodeError("non-numeric value")
3368 class PrintableString(CommonString):
3370 tag_default = tag_encode(19)
3372 asn1_type_name = "PrintableString"
3375 class TeletexString(CommonString):
3377 tag_default = tag_encode(20)
3379 asn1_type_name = "TeletexString"
3382 class T61String(TeletexString):
3384 asn1_type_name = "T61String"
3387 class VideotexString(CommonString):
3389 tag_default = tag_encode(21)
3390 encoding = "iso-8859-1"
3391 asn1_type_name = "VideotexString"
3394 class IA5String(CommonString):
3396 tag_default = tag_encode(22)
3398 asn1_type_name = "IA5"
3401 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3402 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3403 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3406 class UTCTime(CommonString):
3407 """``UTCTime`` datetime type
3409 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3410 UTCTime UTCTime 2017-09-30T22:07:50
3416 datetime.datetime(2017, 9, 30, 22, 7, 50)
3417 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3418 datetime.datetime(1957, 9, 30, 22, 7, 50)
3421 tag_default = tag_encode(23)
3423 asn1_type_name = "UTCTime"
3425 fmt = "%y%m%d%H%M%SZ"
3435 bounds=None, # dummy argument, workability for OctetString.decode
3438 :param value: set the value. Either datetime type, or
3439 :py:class:`pyderasn.UTCTime` object
3440 :param bytes impl: override default tag with ``IMPLICIT`` one
3441 :param bytes expl: override default tag with ``EXPLICIT`` one
3442 :param default: set default value. Type same as in ``value``
3443 :param bool optional: is object ``OPTIONAL`` in sequence
3445 super(UTCTime, self).__init__(
3453 if value is not None:
3454 self._value = self._value_sanitize(value)
3455 if default is not None:
3456 default = self._value_sanitize(default)
3457 self.default = self.__class__(
3462 if self._value is None:
3463 self._value = default
3465 def _value_sanitize(self, value):
3466 if isinstance(value, self.__class__):
3468 if isinstance(value, datetime):
3469 return value.strftime(self.fmt).encode("ascii")
3470 if isinstance(value, binary_type):
3471 value_decoded = value.decode("ascii")
3472 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3474 datetime.strptime(value_decoded, self.fmt)
3476 raise DecodeError("invalid UTCTime format")
3479 raise DecodeError("invalid UTCTime length")
3480 raise InvalidValueType((self.__class__, datetime))
3482 def __eq__(self, their):
3483 if isinstance(their, binary_type):
3484 return self._value == their
3485 if isinstance(their, datetime):
3486 return self.todatetime() == their
3487 if not isinstance(their, self.__class__):
3490 self._value == their._value and
3491 self.tag == their.tag and
3492 self._expl == their._expl
3495 def todatetime(self):
3496 """Convert to datetime
3500 Pay attention that UTCTime can not hold full year, so all years
3501 having < 50 years are treated as 20xx, 19xx otherwise, according
3502 to X.509 recomendation.
3504 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3505 year = value.year % 100
3507 year=(2000 + year) if year < 50 else (1900 + year),
3511 minute=value.minute,
3512 second=value.second,
3516 return pp_console_row(next(self.pps()))
3518 def pps(self, decode_path=()):
3520 asn1_type_name=self.asn1_type_name,
3521 obj_name=self.__class__.__name__,
3522 decode_path=decode_path,
3523 value=self.todatetime().isoformat() if self.ready else None,
3524 optional=self.optional,
3525 default=self == self.default,
3526 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3527 expl=None if self._expl is None else tag_decode(self._expl),
3532 expl_offset=self.expl_offset if self.expled else None,
3533 expl_tlen=self.expl_tlen if self.expled else None,
3534 expl_llen=self.expl_llen if self.expled else None,
3535 expl_vlen=self.expl_vlen if self.expled else None,
3536 expl_lenindef=self.expl_lenindef,
3540 class GeneralizedTime(UTCTime):
3541 """``GeneralizedTime`` datetime type
3543 This type is similar to :py:class:`pyderasn.UTCTime`.
3545 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3546 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3548 '20170930220750.000123Z'
3549 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3550 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3553 tag_default = tag_encode(24)
3554 asn1_type_name = "GeneralizedTime"
3556 fmt = "%Y%m%d%H%M%SZ"
3557 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3559 def _value_sanitize(self, value):
3560 if isinstance(value, self.__class__):
3562 if isinstance(value, datetime):
3563 return value.strftime(
3564 self.fmt_ms if value.microsecond > 0 else self.fmt
3566 if isinstance(value, binary_type):
3567 value_decoded = value.decode("ascii")
3568 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3570 datetime.strptime(value_decoded, self.fmt)
3573 "invalid GeneralizedTime (without ms) format",
3576 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3578 datetime.strptime(value_decoded, self.fmt_ms)
3581 "invalid GeneralizedTime (with ms) format",
3586 "invalid GeneralizedTime length",
3587 klass=self.__class__,
3589 raise InvalidValueType((self.__class__, datetime))
3591 def todatetime(self):
3592 value = self._value.decode("ascii")
3593 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3594 return datetime.strptime(value, self.fmt)
3595 return datetime.strptime(value, self.fmt_ms)
3598 class GraphicString(CommonString):
3600 tag_default = tag_encode(25)
3601 encoding = "iso-8859-1"
3602 asn1_type_name = "GraphicString"
3605 class VisibleString(CommonString):
3607 tag_default = tag_encode(26)
3609 asn1_type_name = "VisibleString"
3612 class ISO646String(VisibleString):
3614 asn1_type_name = "ISO646String"
3617 class GeneralString(CommonString):
3619 tag_default = tag_encode(27)
3620 encoding = "iso-8859-1"
3621 asn1_type_name = "GeneralString"
3624 class UniversalString(CommonString):
3626 tag_default = tag_encode(28)
3627 encoding = "utf-32-be"
3628 asn1_type_name = "UniversalString"
3631 class BMPString(CommonString):
3633 tag_default = tag_encode(30)
3634 encoding = "utf-16-be"
3635 asn1_type_name = "BMPString"
3639 """``CHOICE`` special type
3643 class GeneralName(Choice):
3645 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3646 ("dNSName", IA5String(impl=tag_ctxp(2))),
3649 >>> gn = GeneralName()
3651 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3652 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3653 >>> gn["dNSName"] = IA5String("bar.baz")
3654 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3655 >>> gn["rfc822Name"]
3658 [2] IA5String IA5 bar.baz
3661 >>> gn.value == gn["dNSName"]
3664 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3666 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3667 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3669 __slots__ = ("specs",)
3671 asn1_type_name = "CHOICE"
3684 :param value: set the value. Either ``(choice, value)`` tuple, or
3685 :py:class:`pyderasn.Choice` object
3686 :param bytes impl: can not be set, do **not** use it
3687 :param bytes expl: override default tag with ``EXPLICIT`` one
3688 :param default: set default value. Type same as in ``value``
3689 :param bool optional: is object ``OPTIONAL`` in sequence
3691 if impl is not None:
3692 raise ValueError("no implicit tag allowed for CHOICE")
3693 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3695 schema = getattr(self, "schema", ())
3696 if len(schema) == 0:
3697 raise ValueError("schema must be specified")
3699 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3702 if value is not None:
3703 self._value = self._value_sanitize(value)
3704 if default is not None:
3705 default_value = self._value_sanitize(default)
3706 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3707 default_obj.specs = self.specs
3708 default_obj._value = default_value
3709 self.default = default_obj
3711 self._value = default_obj.copy()._value
3713 def _value_sanitize(self, value):
3714 if isinstance(value, self.__class__):
3716 if isinstance(value, tuple) and len(value) == 2:
3718 spec = self.specs.get(choice)
3720 raise ObjUnknown(choice)
3721 if not isinstance(obj, spec.__class__):
3722 raise InvalidValueType((spec,))
3723 return (choice, spec(obj))
3724 raise InvalidValueType((self.__class__, tuple))
3728 return self._value is not None and self._value[1].ready
3731 obj = self.__class__(schema=self.specs)
3732 obj._expl = self._expl
3733 obj.default = self.default
3734 obj.optional = self.optional
3735 obj.offset = self.offset
3736 obj.llen = self.llen
3737 obj.vlen = self.vlen
3739 if value is not None:
3740 obj._value = (value[0], value[1].copy())
3743 def __eq__(self, their):
3744 if isinstance(their, tuple) and len(their) == 2:
3745 return self._value == their
3746 if not isinstance(their, self.__class__):
3749 self.specs == their.specs and
3750 self._value == their._value
3760 return self.__class__(
3763 expl=self._expl if expl is None else expl,
3764 default=self.default if default is None else default,
3765 optional=self.optional if optional is None else optional,
3770 self._assert_ready()
3771 return self._value[0]
3775 self._assert_ready()
3776 return self._value[1]
3778 def __getitem__(self, key):
3779 if key not in self.specs:
3780 raise ObjUnknown(key)
3781 if self._value is None:
3783 choice, value = self._value
3788 def __setitem__(self, key, value):
3789 spec = self.specs.get(key)
3791 raise ObjUnknown(key)
3792 if not isinstance(value, spec.__class__):
3793 raise InvalidValueType((spec.__class__,))
3794 self._value = (key, spec(value))
3802 return self._value[1].decoded if self.ready else False
3805 self._assert_ready()
3806 return self._value[1].encode()
3808 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3809 for choice, spec in self.specs.items():
3810 sub_decode_path = decode_path + (choice,)
3816 decode_path=sub_decode_path,
3825 klass=self.__class__,
3826 decode_path=decode_path,
3831 value, tail = spec.decode(
3835 decode_path=sub_decode_path,
3838 obj = self.__class__(
3841 default=self.default,
3842 optional=self.optional,
3843 _decoded=(offset, 0, value.tlvlen),
3845 obj._value = (choice, value)
3849 value = pp_console_row(next(self.pps()))
3851 value = "%s[%r]" % (value, self.value)
3854 def pps(self, decode_path=()):
3856 asn1_type_name=self.asn1_type_name,
3857 obj_name=self.__class__.__name__,
3858 decode_path=decode_path,
3859 value=self.choice if self.ready else None,
3860 optional=self.optional,
3861 default=self == self.default,
3862 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3863 expl=None if self._expl is None else tag_decode(self._expl),
3868 expl_lenindef=self.expl_lenindef,
3871 yield self.value.pps(decode_path=decode_path + (self.choice,))
3874 class PrimitiveTypes(Choice):
3875 """Predefined ``CHOICE`` for all generic primitive types
3877 It could be useful for general decoding of some unspecified values:
3879 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3880 OCTET STRING 3 bytes 666f6f
3881 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3885 schema = tuple((klass.__name__, klass()) for klass in (
3910 """``ANY`` special type
3912 >>> Any(Integer(-123))
3914 >>> a = Any(OctetString(b"hello world").encode())
3915 ANY 040b68656c6c6f20776f726c64
3916 >>> hexenc(bytes(a))
3917 b'0x040x0bhello world'
3919 __slots__ = ("defined",)
3920 tag_default = tag_encode(0)
3921 asn1_type_name = "ANY"
3931 :param value: set the value. Either any kind of pyderasn's
3932 **ready** object, or bytes. Pay attention that
3933 **no** validation is performed is raw binary value
3935 :param bytes expl: override default tag with ``EXPLICIT`` one
3936 :param bool optional: is object ``OPTIONAL`` in sequence
3938 super(Any, self).__init__(None, expl, None, optional, _decoded)
3939 self._value = None if value is None else self._value_sanitize(value)
3942 def _value_sanitize(self, value):
3943 if isinstance(value, self.__class__):
3945 if isinstance(value, Obj):
3946 return value.encode()
3947 if isinstance(value, binary_type):
3949 raise InvalidValueType((self.__class__, Obj, binary_type))
3953 return self._value is not None
3956 obj = self.__class__()
3957 obj._value = self._value
3959 obj._expl = self._expl
3960 obj.optional = self.optional
3961 obj.offset = self.offset
3962 obj.llen = self.llen
3963 obj.vlen = self.vlen
3966 def __eq__(self, their):
3967 if isinstance(their, binary_type):
3968 return self._value == their
3969 if issubclass(their.__class__, Any):
3970 return self._value == their._value
3979 return self.__class__(
3981 expl=self._expl if expl is None else expl,
3982 optional=self.optional if optional is None else optional,
3985 def __bytes__(self):
3986 self._assert_ready()
3994 self._assert_ready()
3997 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3999 t, tlen, lv = tag_strip(tlv)
4000 except DecodeError as err:
4001 raise err.__class__(
4003 klass=self.__class__,
4004 decode_path=decode_path,
4008 l, llen, v = len_decode(lv)
4009 except LenIndefForm as err:
4010 if not ctx.get("bered", False):
4011 raise err.__class__(
4013 klass=self.__class__,
4014 decode_path=decode_path,
4017 llen, vlen, v = 1, 0, lv[1:]
4018 sub_offset = offset + tlen + llen
4020 while v[:EOC_LEN].tobytes() != EOC:
4021 chunk, v = Any().decode(
4024 decode_path=decode_path + (str(chunk_i),),
4028 vlen += chunk.tlvlen
4029 sub_offset += chunk.tlvlen
4031 tlvlen = tlen + llen + vlen + EOC_LEN
4032 obj = self.__class__(
4033 value=tlv[:tlvlen].tobytes(),
4035 optional=self.optional,
4036 _decoded=(offset, 0, tlvlen),
4040 return obj, v[EOC_LEN:]
4041 except DecodeError as err:
4042 raise err.__class__(
4044 klass=self.__class__,
4045 decode_path=decode_path,
4049 raise NotEnoughData(
4050 "encoded length is longer than data",
4051 klass=self.__class__,
4052 decode_path=decode_path,
4055 tlvlen = tlen + llen + l
4056 v, tail = tlv[:tlvlen], v[l:]
4057 obj = self.__class__(
4060 optional=self.optional,
4061 _decoded=(offset, 0, tlvlen),
4067 return pp_console_row(next(self.pps()))
4069 def pps(self, decode_path=()):
4071 asn1_type_name=self.asn1_type_name,
4072 obj_name=self.__class__.__name__,
4073 decode_path=decode_path,
4074 blob=self._value if self.ready else None,
4075 optional=self.optional,
4076 default=self == self.default,
4077 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4078 expl=None if self._expl is None else tag_decode(self._expl),
4083 expl_offset=self.expl_offset if self.expled else None,
4084 expl_tlen=self.expl_tlen if self.expled else None,
4085 expl_llen=self.expl_llen if self.expled else None,
4086 expl_vlen=self.expl_vlen if self.expled else None,
4087 expl_lenindef=self.expl_lenindef,
4088 lenindef=self.lenindef,
4090 defined_by, defined = self.defined or (None, None)
4091 if defined_by is not None:
4093 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4097 ########################################################################
4098 # ASN.1 constructed types
4099 ########################################################################
4101 def get_def_by_path(defines_by_path, sub_decode_path):
4102 """Get define by decode path
4104 for path, define in defines_by_path:
4105 if len(path) != len(sub_decode_path):
4107 for p1, p2 in zip(path, sub_decode_path):
4108 if (p1 != any) and (p1 != p2):
4114 def abs_decode_path(decode_path, rel_path):
4115 """Create an absolute decode path from current and relative ones
4117 :param decode_path: current decode path, starting point.
4119 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4120 If first tuple's element is "/", then treat it as
4121 an absolute path, ignoring ``decode_path`` as
4122 starting point. Also this tuple can contain ".."
4123 elements, stripping the leading element from
4126 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4127 ("foo", "bar", "baz", "whatever")
4128 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4130 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4133 if rel_path[0] == "/":
4135 if rel_path[0] == "..":
4136 return abs_decode_path(decode_path[:-1], rel_path[1:])
4137 return decode_path + rel_path
4140 class Sequence(Obj):
4141 """``SEQUENCE`` structure type
4143 You have to make specification of sequence::
4145 class Extension(Sequence):
4147 ("extnID", ObjectIdentifier()),
4148 ("critical", Boolean(default=False)),
4149 ("extnValue", OctetString()),
4152 Then, you can work with it as with dictionary.
4154 >>> ext = Extension()
4155 >>> Extension().specs
4157 ('extnID', OBJECT IDENTIFIER),
4158 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4159 ('extnValue', OCTET STRING),
4161 >>> ext["extnID"] = "1.2.3"
4162 Traceback (most recent call last):
4163 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4164 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4166 You can determine if sequence is ready to be encoded:
4171 Traceback (most recent call last):
4172 pyderasn.ObjNotReady: object is not ready: extnValue
4173 >>> ext["extnValue"] = OctetString(b"foobar")
4177 Value you want to assign, must have the same **type** as in
4178 corresponding specification, but it can have different tags,
4179 optional/default attributes -- they will be taken from specification
4182 class TBSCertificate(Sequence):
4184 ("version", Version(expl=tag_ctxc(0), default="v1")),
4187 >>> tbs = TBSCertificate()
4188 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4190 Assign ``None`` to remove value from sequence.
4192 You can set values in Sequence during its initialization:
4194 >>> AlgorithmIdentifier((
4195 ("algorithm", ObjectIdentifier("1.2.3")),
4196 ("parameters", Any(Null()))
4198 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4200 You can determine if value exists/set in the sequence and take its value:
4202 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4205 OBJECT IDENTIFIER 1.2.3
4207 But pay attention that if value has default, then it won't be (not
4208 in) in the sequence (because ``DEFAULT`` must not be encoded in
4209 DER), but you can read its value:
4211 >>> "critical" in ext, ext["critical"]
4212 (False, BOOLEAN False)
4213 >>> ext["critical"] = Boolean(True)
4214 >>> "critical" in ext, ext["critical"]
4215 (True, BOOLEAN True)
4217 All defaulted values are always optional.
4219 .. _strict_default_existence_ctx:
4223 When decoded DER contains defaulted value inside, then
4224 technically this is not valid DER encoding. But we allow and pass
4225 it **by default**. Of course reencoding of that kind of DER will
4226 result in different binary representation (validly without
4227 defaulted value inside). You can enable strict defaulted values
4228 existence validation by setting ``"strict_default_existence":
4229 True`` :ref:`context <ctx>` option -- decoding process will raise
4230 an exception if defaulted value is met.
4232 Two sequences are equal if they have equal specification (schema),
4233 implicit/explicit tagging and the same values.
4235 __slots__ = ("specs",)
4236 tag_default = tag_encode(form=TagFormConstructed, num=16)
4237 asn1_type_name = "SEQUENCE"
4249 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4251 schema = getattr(self, "schema", ())
4253 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4256 if value is not None:
4257 if issubclass(value.__class__, Sequence):
4258 self._value = value._value
4259 elif hasattr(value, "__iter__"):
4260 for seq_key, seq_value in value:
4261 self[seq_key] = seq_value
4263 raise InvalidValueType((Sequence,))
4264 if default is not None:
4265 if not issubclass(default.__class__, Sequence):
4266 raise InvalidValueType((Sequence,))
4267 default_value = default._value
4268 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4269 default_obj.specs = self.specs
4270 default_obj._value = default_value
4271 self.default = default_obj
4273 self._value = default_obj.copy()._value
4277 for name, spec in self.specs.items():
4278 value = self._value.get(name)
4289 obj = self.__class__(schema=self.specs)
4291 obj._expl = self._expl
4292 obj.default = self.default
4293 obj.optional = self.optional
4294 obj.offset = self.offset
4295 obj.llen = self.llen
4296 obj.vlen = self.vlen
4297 obj._value = {k: v.copy() for k, v in self._value.items()}
4300 def __eq__(self, their):
4301 if not isinstance(their, self.__class__):
4304 self.specs == their.specs and
4305 self.tag == their.tag and
4306 self._expl == their._expl and
4307 self._value == their._value
4318 return self.__class__(
4321 impl=self.tag if impl is None else impl,
4322 expl=self._expl if expl is None else expl,
4323 default=self.default if default is None else default,
4324 optional=self.optional if optional is None else optional,
4327 def __contains__(self, key):
4328 return key in self._value
4330 def __setitem__(self, key, value):
4331 spec = self.specs.get(key)
4333 raise ObjUnknown(key)
4335 self._value.pop(key, None)
4337 if not isinstance(value, spec.__class__):
4338 raise InvalidValueType((spec.__class__,))
4339 value = spec(value=value)
4340 if spec.default is not None and value == spec.default:
4341 self._value.pop(key, None)
4343 self._value[key] = value
4345 def __getitem__(self, key):
4346 value = self._value.get(key)
4347 if value is not None:
4349 spec = self.specs.get(key)
4351 raise ObjUnknown(key)
4352 if spec.default is not None:
4356 def _encoded_values(self):
4358 for name, spec in self.specs.items():
4359 value = self._value.get(name)
4363 raise ObjNotReady(name)
4364 raws.append(value.encode())
4368 v = b"".join(self._encoded_values())
4369 return b"".join((self.tag, len_encode(len(v)), v))
4371 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4373 t, tlen, lv = tag_strip(tlv)
4374 except DecodeError as err:
4375 raise err.__class__(
4377 klass=self.__class__,
4378 decode_path=decode_path,
4383 klass=self.__class__,
4384 decode_path=decode_path,
4391 l, llen, v = len_decode(lv)
4392 except LenIndefForm as err:
4393 if not ctx.get("bered", False):
4394 raise err.__class__(
4396 klass=self.__class__,
4397 decode_path=decode_path,
4400 l, llen, v = 0, 1, lv[1:]
4402 except DecodeError as err:
4403 raise err.__class__(
4405 klass=self.__class__,
4406 decode_path=decode_path,
4410 raise NotEnoughData(
4411 "encoded length is longer than data",
4412 klass=self.__class__,
4413 decode_path=decode_path,
4417 v, tail = v[:l], v[l:]
4419 sub_offset = offset + tlen + llen
4421 for name, spec in self.specs.items():
4422 if spec.optional and (
4423 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4427 sub_decode_path = decode_path + (name,)
4429 value, v_tail = spec.decode(
4433 decode_path=sub_decode_path,
4441 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4442 if defined is not None:
4443 defined_by, defined_spec = defined
4444 if issubclass(value.__class__, SequenceOf):
4445 for i, _value in enumerate(value):
4446 sub_sub_decode_path = sub_decode_path + (
4448 DecodePathDefBy(defined_by),
4450 defined_value, defined_tail = defined_spec.decode(
4451 memoryview(bytes(_value)),
4453 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4454 if value.expled else (value.tlen + value.llen)
4457 decode_path=sub_sub_decode_path,
4460 if len(defined_tail) > 0:
4463 klass=self.__class__,
4464 decode_path=sub_sub_decode_path,
4467 _value.defined = (defined_by, defined_value)
4469 defined_value, defined_tail = defined_spec.decode(
4470 memoryview(bytes(value)),
4472 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4473 if value.expled else (value.tlen + value.llen)
4476 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4479 if len(defined_tail) > 0:
4482 klass=self.__class__,
4483 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4486 value.defined = (defined_by, defined_value)
4488 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4490 sub_offset += value_len
4492 if spec.default is not None and value == spec.default:
4493 if ctx.get("strict_default_existence", False):
4495 "DEFAULT value met",
4496 klass=self.__class__,
4497 decode_path=sub_decode_path,
4502 values[name] = value
4504 spec_defines = getattr(spec, "defines", ())
4505 if len(spec_defines) == 0:
4506 defines_by_path = ctx.get("defines_by_path", ())
4507 if len(defines_by_path) > 0:
4508 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4509 if spec_defines is not None and len(spec_defines) > 0:
4510 for rel_path, schema in spec_defines:
4511 defined = schema.get(value, None)
4512 if defined is not None:
4513 ctx.setdefault("defines", []).append((
4514 abs_decode_path(sub_decode_path[:-1], rel_path),
4518 if v[:EOC_LEN].tobytes() != EOC:
4521 klass=self.__class__,
4522 decode_path=decode_path,
4530 klass=self.__class__,
4531 decode_path=decode_path,
4534 obj = self.__class__(
4538 default=self.default,
4539 optional=self.optional,
4540 _decoded=(offset, llen, vlen),
4543 obj.lenindef = lenindef
4547 value = pp_console_row(next(self.pps()))
4549 for name in self.specs:
4550 _value = self._value.get(name)
4553 cols.append(repr(_value))
4554 return "%s[%s]" % (value, ", ".join(cols))
4556 def pps(self, decode_path=()):
4558 asn1_type_name=self.asn1_type_name,
4559 obj_name=self.__class__.__name__,
4560 decode_path=decode_path,
4561 optional=self.optional,
4562 default=self == self.default,
4563 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4564 expl=None if self._expl is None else tag_decode(self._expl),
4569 expl_offset=self.expl_offset if self.expled else None,
4570 expl_tlen=self.expl_tlen if self.expled else None,
4571 expl_llen=self.expl_llen if self.expled else None,
4572 expl_vlen=self.expl_vlen if self.expled else None,
4573 expl_lenindef=self.expl_lenindef,
4574 lenindef=self.lenindef,
4576 for name in self.specs:
4577 value = self._value.get(name)
4580 yield value.pps(decode_path=decode_path + (name,))
4583 class Set(Sequence):
4584 """``SET`` structure type
4586 Its usage is identical to :py:class:`pyderasn.Sequence`.
4589 tag_default = tag_encode(form=TagFormConstructed, num=17)
4590 asn1_type_name = "SET"
4593 raws = self._encoded_values()
4596 return b"".join((self.tag, len_encode(len(v)), v))
4598 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4600 t, tlen, lv = tag_strip(tlv)
4601 except DecodeError as err:
4602 raise err.__class__(
4604 klass=self.__class__,
4605 decode_path=decode_path,
4610 klass=self.__class__,
4611 decode_path=decode_path,
4618 l, llen, v = len_decode(lv)
4619 except LenIndefForm as err:
4620 if not ctx.get("bered", False):
4621 raise err.__class__(
4623 klass=self.__class__,
4624 decode_path=decode_path,
4627 l, llen, v = 0, 1, lv[1:]
4629 except DecodeError as err:
4630 raise err.__class__(
4632 klass=self.__class__,
4633 decode_path=decode_path,
4637 raise NotEnoughData(
4638 "encoded length is longer than data",
4639 klass=self.__class__,
4643 v, tail = v[:l], v[l:]
4645 sub_offset = offset + tlen + llen
4647 specs_items = self.specs.items
4649 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4651 for name, spec in specs_items():
4652 sub_decode_path = decode_path + (name,)
4658 decode_path=sub_decode_path,
4667 klass=self.__class__,
4668 decode_path=decode_path,
4671 value, v_tail = spec.decode(
4675 decode_path=sub_decode_path,
4678 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4679 sub_offset += value_len
4682 if spec.default is None or value != spec.default: # pragma: no cover
4683 # SeqMixing.test_encoded_default_accepted covers that place
4684 values[name] = value
4685 obj = self.__class__(
4689 default=self.default,
4690 optional=self.optional,
4691 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4695 if v[:EOC_LEN].tobytes() != EOC:
4698 klass=self.__class__,
4699 decode_path=decode_path,
4706 "not all values are ready",
4707 klass=self.__class__,
4708 decode_path=decode_path,
4714 class SequenceOf(Obj):
4715 """``SEQUENCE OF`` sequence type
4717 For that kind of type you must specify the object it will carry on
4718 (bounds are for example here, not required)::
4720 class Ints(SequenceOf):
4725 >>> ints.append(Integer(123))
4726 >>> ints.append(Integer(234))
4728 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4729 >>> [int(i) for i in ints]
4731 >>> ints.append(Integer(345))
4732 Traceback (most recent call last):
4733 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4736 >>> ints[1] = Integer(345)
4738 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4740 Also you can initialize sequence with preinitialized values:
4742 >>> ints = Ints([Integer(123), Integer(234)])
4744 __slots__ = ("spec", "_bound_min", "_bound_max")
4745 tag_default = tag_encode(form=TagFormConstructed, num=16)
4746 asn1_type_name = "SEQUENCE OF"
4759 super(SequenceOf, self).__init__(
4767 schema = getattr(self, "schema", None)
4769 raise ValueError("schema must be specified")
4771 self._bound_min, self._bound_max = getattr(
4775 ) if bounds is None else bounds
4777 if value is not None:
4778 self._value = self._value_sanitize(value)
4779 if default is not None:
4780 default_value = self._value_sanitize(default)
4781 default_obj = self.__class__(
4786 default_obj._value = default_value
4787 self.default = default_obj
4789 self._value = default_obj.copy()._value
4791 def _value_sanitize(self, value):
4792 if issubclass(value.__class__, SequenceOf):
4793 value = value._value
4794 elif hasattr(value, "__iter__"):
4797 raise InvalidValueType((self.__class__, iter))
4798 if not self._bound_min <= len(value) <= self._bound_max:
4799 raise BoundsError(self._bound_min, len(value), self._bound_max)
4801 if not isinstance(v, self.spec.__class__):
4802 raise InvalidValueType((self.spec.__class__,))
4807 return all(v.ready for v in self._value)
4810 obj = self.__class__(schema=self.spec)
4811 obj._bound_min = self._bound_min
4812 obj._bound_max = self._bound_max
4814 obj._expl = self._expl
4815 obj.default = self.default
4816 obj.optional = self.optional
4817 obj.offset = self.offset
4818 obj.llen = self.llen
4819 obj.vlen = self.vlen
4820 obj._value = [v.copy() for v in self._value]
4823 def __eq__(self, their):
4824 if isinstance(their, self.__class__):
4826 self.spec == their.spec and
4827 self.tag == their.tag and
4828 self._expl == their._expl and
4829 self._value == their._value
4831 if hasattr(their, "__iter__"):
4832 return self._value == list(their)
4844 return self.__class__(
4848 (self._bound_min, self._bound_max)
4849 if bounds is None else bounds
4851 impl=self.tag if impl is None else impl,
4852 expl=self._expl if expl is None else expl,
4853 default=self.default if default is None else default,
4854 optional=self.optional if optional is None else optional,
4857 def __contains__(self, key):
4858 return key in self._value
4860 def append(self, value):
4861 if not isinstance(value, self.spec.__class__):
4862 raise InvalidValueType((self.spec.__class__,))
4863 if len(self._value) + 1 > self._bound_max:
4866 len(self._value) + 1,
4869 self._value.append(value)
4872 self._assert_ready()
4873 return iter(self._value)
4876 self._assert_ready()
4877 return len(self._value)
4879 def __setitem__(self, key, value):
4880 if not isinstance(value, self.spec.__class__):
4881 raise InvalidValueType((self.spec.__class__,))
4882 self._value[key] = self.spec(value=value)
4884 def __getitem__(self, key):
4885 return self._value[key]
4887 def _encoded_values(self):
4888 return [v.encode() for v in self._value]
4891 v = b"".join(self._encoded_values())
4892 return b"".join((self.tag, len_encode(len(v)), v))
4894 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4896 t, tlen, lv = tag_strip(tlv)
4897 except DecodeError as err:
4898 raise err.__class__(
4900 klass=self.__class__,
4901 decode_path=decode_path,
4906 klass=self.__class__,
4907 decode_path=decode_path,
4914 l, llen, v = len_decode(lv)
4915 except LenIndefForm as err:
4916 if not ctx.get("bered", False):
4917 raise err.__class__(
4919 klass=self.__class__,
4920 decode_path=decode_path,
4923 l, llen, v = 0, 1, lv[1:]
4925 except DecodeError as err:
4926 raise err.__class__(
4928 klass=self.__class__,
4929 decode_path=decode_path,
4933 raise NotEnoughData(
4934 "encoded length is longer than data",
4935 klass=self.__class__,
4936 decode_path=decode_path,
4940 v, tail = v[:l], v[l:]
4942 sub_offset = offset + tlen + llen
4946 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4948 value, v_tail = spec.decode(
4952 decode_path=decode_path + (str(len(_value)),),
4955 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4956 sub_offset += value_len
4959 _value.append(value)
4960 obj = self.__class__(
4963 bounds=(self._bound_min, self._bound_max),
4966 default=self.default,
4967 optional=self.optional,
4968 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4971 if v[:EOC_LEN].tobytes() != EOC:
4974 klass=self.__class__,
4975 decode_path=decode_path,
4984 pp_console_row(next(self.pps())),
4985 ", ".join(repr(v) for v in self._value),
4988 def pps(self, decode_path=()):
4990 asn1_type_name=self.asn1_type_name,
4991 obj_name=self.__class__.__name__,
4992 decode_path=decode_path,
4993 optional=self.optional,
4994 default=self == self.default,
4995 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4996 expl=None if self._expl is None else tag_decode(self._expl),
5001 expl_offset=self.expl_offset if self.expled else None,
5002 expl_tlen=self.expl_tlen if self.expled else None,
5003 expl_llen=self.expl_llen if self.expled else None,
5004 expl_vlen=self.expl_vlen if self.expled else None,
5005 expl_lenindef=self.expl_lenindef,
5006 lenindef=self.lenindef,
5008 for i, value in enumerate(self._value):
5009 yield value.pps(decode_path=decode_path + (str(i),))
5012 class SetOf(SequenceOf):
5013 """``SET OF`` sequence type
5015 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5018 tag_default = tag_encode(form=TagFormConstructed, num=17)
5019 asn1_type_name = "SET OF"
5022 raws = self._encoded_values()
5025 return b"".join((self.tag, len_encode(len(v)), v))
5028 def obj_by_path(pypath): # pragma: no cover
5029 """Import object specified as string Python path
5031 Modules must be separated from classes/functions with ``:``.
5033 >>> obj_by_path("foo.bar:Baz")
5034 <class 'foo.bar.Baz'>
5035 >>> obj_by_path("foo.bar:Baz.boo")
5036 <classmethod 'foo.bar.Baz.boo'>
5038 mod, objs = pypath.rsplit(":", 1)
5039 from importlib import import_module
5040 obj = import_module(mod)
5041 for obj_name in objs.split("."):
5042 obj = getattr(obj, obj_name)
5046 def generic_decoder(): # pragma: no cover
5047 # All of this below is a big hack with self references
5048 choice = PrimitiveTypes()
5049 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5050 choice.specs["SetOf"] = SetOf(schema=choice)
5052 choice.specs["SequenceOf%d" % i] = SequenceOf(
5056 choice.specs["Any"] = Any()
5058 # Class name equals to type name, to omit it from output
5059 class SEQUENCEOF(SequenceOf):
5063 def pprint_any(obj, oids=None, with_colours=False):
5064 def _pprint_pps(pps):
5066 if hasattr(pp, "_fields"):
5067 if pp.asn1_type_name == Choice.asn1_type_name:
5069 pp_kwargs = pp._asdict()
5070 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5071 pp = _pp(**pp_kwargs)
5072 yield pp_console_row(
5077 with_colours=with_colours,
5079 for row in pp_console_blob(pp):
5082 for row in _pprint_pps(pp):
5084 return "\n".join(_pprint_pps(obj.pps()))
5085 return SEQUENCEOF(), pprint_any
5088 def main(): # pragma: no cover
5090 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5091 parser.add_argument(
5095 help="Skip that number of bytes from the beginning",
5097 parser.add_argument(
5099 help="Python path to dictionary with OIDs",
5101 parser.add_argument(
5103 help="Python path to schema definition to use",
5105 parser.add_argument(
5106 "--defines-by-path",
5107 help="Python path to decoder's defines_by_path",
5109 parser.add_argument(
5111 action='store_true',
5112 help="Disallow BER encoding",
5114 parser.add_argument(
5116 type=argparse.FileType("rb"),
5117 help="Path to DER file you want to decode",
5119 args = parser.parse_args()
5120 args.DERFile.seek(args.skip)
5121 der = memoryview(args.DERFile.read())
5122 args.DERFile.close()
5123 oids = obj_by_path(args.oids) if args.oids else {}
5125 schema = obj_by_path(args.schema)
5126 from functools import partial
5127 pprinter = partial(pprint, big_blobs=True)
5129 schema, pprinter = generic_decoder()
5130 ctx = {"bered": not args.nobered}
5131 if args.defines_by_path is not None:
5132 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5133 obj, tail = schema().decode(der, ctx=ctx)
5137 with_colours=True if environ.get("NO_COLOR") is None else False,
5140 print("\nTrailing data: %s" % hexenc(tail))
5143 if __name__ == "__main__":