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 ",
608 ########################################################################
610 ########################################################################
612 class DecodeError(Exception):
613 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
615 :param str msg: reason of decode failing
616 :param klass: optional exact DecodeError inherited class (like
617 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
618 :py:exc:`InvalidLength`)
619 :param decode_path: tuple of strings. It contains human
620 readable names of the fields through which
621 decoding process has passed
622 :param int offset: binary offset where failure happened
624 super(DecodeError, self).__init__()
627 self.decode_path = decode_path
633 "" if self.klass is None else self.klass.__name__,
635 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
636 if len(self.decode_path) > 0 else ""
638 ("(at %d)" % self.offset) if self.offset > 0 else "",
644 return "%s(%s)" % (self.__class__.__name__, self)
647 class NotEnoughData(DecodeError):
651 class LenIndefForm(DecodeError):
655 class TagMismatch(DecodeError):
659 class InvalidLength(DecodeError):
663 class InvalidOID(DecodeError):
667 class ObjUnknown(ValueError):
668 def __init__(self, name):
669 super(ObjUnknown, self).__init__()
673 return "object is unknown: %s" % self.name
676 return "%s(%s)" % (self.__class__.__name__, self)
679 class ObjNotReady(ValueError):
680 def __init__(self, name):
681 super(ObjNotReady, self).__init__()
685 return "object is not ready: %s" % self.name
688 return "%s(%s)" % (self.__class__.__name__, self)
691 class InvalidValueType(ValueError):
692 def __init__(self, expected_types):
693 super(InvalidValueType, self).__init__()
694 self.expected_types = expected_types
697 return "invalid value type, expected: %s" % ", ".join(
698 [repr(t) for t in self.expected_types]
702 return "%s(%s)" % (self.__class__.__name__, self)
705 class BoundsError(ValueError):
706 def __init__(self, bound_min, value, bound_max):
707 super(BoundsError, self).__init__()
708 self.bound_min = bound_min
710 self.bound_max = bound_max
713 return "unsatisfied bounds: %s <= %s <= %s" % (
720 return "%s(%s)" % (self.__class__.__name__, self)
723 ########################################################################
725 ########################################################################
727 _hexdecoder = getdecoder("hex")
728 _hexencoder = getencoder("hex")
732 """Binary data to hexadecimal string convert
734 return _hexdecoder(data)[0]
738 """Hexadecimal string to binary data convert
740 return _hexencoder(data)[0].decode("ascii")
743 def int_bytes_len(num, byte_len=8):
746 return int(ceil(float(num.bit_length()) / byte_len))
749 def zero_ended_encode(num):
750 octets = bytearray(int_bytes_len(num, 7))
752 octets[i] = num & 0x7F
756 octets[i] = 0x80 | (num & 0x7F)
762 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
763 """Encode tag to binary form
765 :param int num: tag's number
766 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
767 :py:data:`pyderasn.TagClassContext`,
768 :py:data:`pyderasn.TagClassApplication`,
769 :py:data:`pyderasn.TagClassPrivate`)
770 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
771 :py:data:`pyderasn.TagFormConstructed`)
775 return int2byte(klass | form | num)
776 # [XX|X|11111][1.......][1.......] ... [0.......]
777 return int2byte(klass | form | 31) + zero_ended_encode(num)
781 """Decode tag from binary form
785 No validation is performed, assuming that it has already passed.
787 It returns tuple with three integers, as
788 :py:func:`pyderasn.tag_encode` accepts.
790 first_octet = byte2int(tag)
791 klass = first_octet & 0xC0
792 form = first_octet & 0x20
793 if first_octet & 0x1F < 0x1F:
794 return (klass, form, first_octet & 0x1F)
796 for octet in iterbytes(tag[1:]):
799 return (klass, form, num)
803 """Create CONTEXT PRIMITIVE tag
805 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
809 """Create CONTEXT CONSTRUCTED tag
811 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
815 """Take off tag from the data
817 :returns: (encoded tag, tag length, remaining data)
820 raise NotEnoughData("no data at all")
821 if byte2int(data) & 0x1F < 31:
822 return data[:1], 1, data[1:]
827 raise DecodeError("unfinished tag")
828 if indexbytes(data, i) & 0x80 == 0:
831 return data[:i], i, data[i:]
837 octets = bytearray(int_bytes_len(l) + 1)
838 octets[0] = 0x80 | (len(octets) - 1)
839 for i in six_xrange(len(octets) - 1, 0, -1):
845 def len_decode(data):
848 :returns: (decoded length, length's length, remaining data)
849 :raises LenIndefForm: if indefinite form encoding is met
852 raise NotEnoughData("no data at all")
853 first_octet = byte2int(data)
854 if first_octet & 0x80 == 0:
855 return first_octet, 1, data[1:]
856 octets_num = first_octet & 0x7F
857 if octets_num + 1 > len(data):
858 raise NotEnoughData("encoded length is longer than data")
861 if byte2int(data[1:]) == 0:
862 raise DecodeError("leading zeros")
864 for v in iterbytes(data[1:1 + octets_num]):
867 raise DecodeError("long form instead of short one")
868 return l, 1 + octets_num, data[1 + octets_num:]
871 ########################################################################
873 ########################################################################
875 class AutoAddSlots(type):
876 def __new__(mcs, name, bases, _dict):
877 _dict["__slots__"] = _dict.get("__slots__", ())
878 return type.__new__(mcs, name, bases, _dict)
881 @add_metaclass(AutoAddSlots)
883 """Common ASN.1 object class
885 All ASN.1 types are inherited from it. It has metaclass that
886 automatically adds ``__slots__`` to all inherited classes.
910 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
911 self._expl = getattr(self, "expl", None) if expl is None else expl
912 if self.tag != self.tag_default and self._expl is not None:
913 raise ValueError("implicit and explicit tags can not be set simultaneously")
914 if default is not None:
916 self.optional = optional
917 self.offset, self.llen, self.vlen = _decoded
919 self.expl_lenindef = False
920 self.lenindef = False
924 def ready(self): # pragma: no cover
925 """Is object ready to be encoded?
927 raise NotImplementedError()
929 def _assert_ready(self):
931 raise ObjNotReady(self.__class__.__name__)
935 """Is object decoded?
937 return (self.llen + self.vlen) > 0
939 def copy(self): # pragma: no cover
940 """Make a copy of object, safe to be mutated
942 raise NotImplementedError()
950 return self.tlen + self.llen + self.vlen
952 def __str__(self): # pragma: no cover
953 return self.__bytes__() if PY2 else self.__unicode__()
955 def __ne__(self, their):
956 return not(self == their)
958 def __gt__(self, their): # pragma: no cover
959 return not(self < their)
961 def __le__(self, their): # pragma: no cover
962 return (self == their) or (self < their)
964 def __ge__(self, their): # pragma: no cover
965 return (self == their) or (self > their)
967 def _encode(self): # pragma: no cover
968 raise NotImplementedError()
970 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
971 raise NotImplementedError()
975 if self._expl is None:
977 return b"".join((self._expl, len_encode(len(raw)), raw))
990 :param data: either binary or memoryview
991 :param int offset: initial data's offset
992 :param bool leavemm: do we need to leave memoryview of remaining
993 data as is, or convert it to bytes otherwise
994 :param ctx: optional :ref:`context <ctx>` governing decoding process.
995 :param tag_only: decode only the tag, without length and contents
996 (used only in Choice and Set structures, trying to
997 determine if tag satisfies the scheme)
998 :returns: (Obj, remaining data)
1002 tlv = memoryview(data)
1003 if self._expl is None:
1004 result = self._decode(
1007 decode_path=decode_path,
1016 t, tlen, lv = tag_strip(tlv)
1017 except DecodeError as err:
1018 raise err.__class__(
1020 klass=self.__class__,
1021 decode_path=decode_path,
1026 klass=self.__class__,
1027 decode_path=decode_path,
1031 l, llen, v = len_decode(lv)
1032 except LenIndefForm as err:
1033 if not ctx.get("bered", False):
1034 raise err.__class__(
1036 klass=self.__class__,
1037 decode_path=decode_path,
1041 offset += tlen + llen
1042 result = self._decode(
1045 decode_path=decode_path,
1052 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1053 if eoc_expected.tobytes() != EOC:
1056 klass=self.__class__,
1057 decode_path=decode_path,
1061 obj.expl_lenindef = True
1062 except DecodeError as err:
1063 raise err.__class__(
1065 klass=self.__class__,
1066 decode_path=decode_path,
1071 raise NotEnoughData(
1072 "encoded length is longer than data",
1073 klass=self.__class__,
1074 decode_path=decode_path,
1077 result = self._decode(
1079 offset=offset + tlen + llen,
1080 decode_path=decode_path,
1087 return obj, (tail if leavemm else tail.tobytes())
1091 return self._expl is not None
1098 def expl_tlen(self):
1099 return len(self._expl)
1102 def expl_llen(self):
1103 if self.expl_lenindef:
1105 return len(len_encode(self.tlvlen))
1108 def expl_offset(self):
1109 return self.offset - self.expl_tlen - self.expl_llen
1112 def expl_vlen(self):
1116 def expl_tlvlen(self):
1117 return self.expl_tlen + self.expl_llen + self.expl_vlen
1120 class DecodePathDefBy(object):
1121 """DEFINED BY representation inside decode path
1123 __slots__ = ("defined_by",)
1125 def __init__(self, defined_by):
1126 self.defined_by = defined_by
1128 def __ne__(self, their):
1129 return not(self == their)
1131 def __eq__(self, their):
1132 if not isinstance(their, self.__class__):
1134 return self.defined_by == their.defined_by
1137 return "DEFINED BY " + str(self.defined_by)
1140 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1143 ########################################################################
1145 ########################################################################
1147 PP = namedtuple("PP", (
1172 asn1_type_name="unknown",
1189 expl_lenindef=False,
1217 def _colorize(what, colour, with_colours, attrs=("bold",)):
1218 return colored(what, colour, attrs=attrs) if with_colours else what
1233 " " if pp.expl_offset is None else
1234 ("-%d" % (pp.offset - pp.expl_offset))
1237 cols.append(_colorize(col, "red", with_colours, ()))
1238 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1239 col = _colorize(col, "green", with_colours, ())
1241 if pp.expl_lenindef:
1246 " " if ber_deoffset == 0 else
1247 _colorize(("-%d" % ber_deoffset), "red", with_colours)
1250 if len(pp.decode_path) > 0:
1251 cols.append(" ." * (len(pp.decode_path)))
1252 ent = pp.decode_path[-1]
1253 if isinstance(ent, DecodePathDefBy):
1254 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1255 value = str(ent.defined_by)
1257 oids is not None and
1258 ent.defined_by.asn1_type_name ==
1259 ObjectIdentifier.asn1_type_name and
1262 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1264 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1266 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1267 if pp.expl is not None:
1268 klass, _, num = pp.expl
1269 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1270 cols.append(_colorize(col, "blue", with_colours))
1271 if pp.impl is not None:
1272 klass, _, num = pp.impl
1273 col = "[%s%d]" % (TagClassReprs[klass], num)
1274 cols.append(_colorize(col, "blue", with_colours))
1275 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1276 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1278 cols.append(_colorize("BER", "red", with_colours))
1279 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1280 if pp.value is not None:
1282 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1284 oids is not None and
1285 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1288 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1290 if isinstance(pp.blob, binary_type):
1291 cols.append(hexenc(pp.blob))
1292 elif isinstance(pp.blob, tuple):
1293 cols.append(", ".join(pp.blob))
1295 cols.append(_colorize("OPTIONAL", "red", with_colours))
1297 cols.append(_colorize("DEFAULT", "red", with_colours))
1298 return " ".join(cols)
1301 def pp_console_blob(pp):
1302 cols = [" " * len("XXXXXYY [X,X,XXXX]YY")]
1303 if len(pp.decode_path) > 0:
1304 cols.append(" ." * (len(pp.decode_path) + 1))
1305 if isinstance(pp.blob, binary_type):
1306 blob = hexenc(pp.blob).upper()
1307 for i in range(0, len(blob), 32):
1308 chunk = blob[i:i + 32]
1309 yield " ".join(cols + [":".join(
1310 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1312 elif isinstance(pp.blob, tuple):
1313 yield " ".join(cols + [", ".join(pp.blob)])
1316 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1317 """Pretty print object
1319 :param Obj obj: object you want to pretty print
1320 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1321 from it is met, then its humand readable form is printed
1322 :param big_blobs: if large binary objects are met (like OctetString
1323 values), do we need to print them too, on separate
1325 :param with_colours: colourize output, if ``termcolor`` library
1328 def _pprint_pps(pps):
1330 if hasattr(pp, "_fields"):
1332 yield pp_console_row(
1337 with_colours=with_colours,
1339 for row in pp_console_blob(pp):
1342 yield pp_console_row(
1347 with_colours=with_colours,
1350 for row in _pprint_pps(pp):
1352 return "\n".join(_pprint_pps(obj.pps()))
1355 ########################################################################
1356 # ASN.1 primitive types
1357 ########################################################################
1360 """``BOOLEAN`` boolean type
1362 >>> b = Boolean(True)
1364 >>> b == Boolean(True)
1370 tag_default = tag_encode(1)
1371 asn1_type_name = "BOOLEAN"
1383 :param value: set the value. Either boolean type, or
1384 :py:class:`pyderasn.Boolean` object
1385 :param bytes impl: override default tag with ``IMPLICIT`` one
1386 :param bytes expl: override default tag with ``EXPLICIT`` one
1387 :param default: set default value. Type same as in ``value``
1388 :param bool optional: is object ``OPTIONAL`` in sequence
1390 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1391 self._value = None if value is None else self._value_sanitize(value)
1392 if default is not None:
1393 default = self._value_sanitize(default)
1394 self.default = self.__class__(
1400 self._value = default
1402 def _value_sanitize(self, value):
1403 if issubclass(value.__class__, Boolean):
1405 if isinstance(value, bool):
1407 raise InvalidValueType((self.__class__, bool))
1411 return self._value is not None
1414 obj = self.__class__()
1415 obj._value = self._value
1417 obj._expl = self._expl
1418 obj.default = self.default
1419 obj.optional = self.optional
1420 obj.offset = self.offset
1421 obj.llen = self.llen
1422 obj.vlen = self.vlen
1425 def __nonzero__(self):
1426 self._assert_ready()
1430 self._assert_ready()
1433 def __eq__(self, their):
1434 if isinstance(their, bool):
1435 return self._value == their
1436 if not issubclass(their.__class__, Boolean):
1439 self._value == their._value and
1440 self.tag == their.tag and
1441 self._expl == their._expl
1452 return self.__class__(
1454 impl=self.tag if impl is None else impl,
1455 expl=self._expl if expl is None else expl,
1456 default=self.default if default is None else default,
1457 optional=self.optional if optional is None else optional,
1461 self._assert_ready()
1465 (b"\xFF" if self._value else b"\x00"),
1468 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1470 t, _, lv = tag_strip(tlv)
1471 except DecodeError as err:
1472 raise err.__class__(
1474 klass=self.__class__,
1475 decode_path=decode_path,
1480 klass=self.__class__,
1481 decode_path=decode_path,
1487 l, _, v = len_decode(lv)
1488 except DecodeError as err:
1489 raise err.__class__(
1491 klass=self.__class__,
1492 decode_path=decode_path,
1496 raise InvalidLength(
1497 "Boolean's length must be equal to 1",
1498 klass=self.__class__,
1499 decode_path=decode_path,
1503 raise NotEnoughData(
1504 "encoded length is longer than data",
1505 klass=self.__class__,
1506 decode_path=decode_path,
1509 first_octet = byte2int(v)
1511 if first_octet == 0:
1513 elif first_octet == 0xFF:
1515 elif ctx.get("bered", False):
1520 "unacceptable Boolean value",
1521 klass=self.__class__,
1522 decode_path=decode_path,
1525 obj = self.__class__(
1529 default=self.default,
1530 optional=self.optional,
1531 _decoded=(offset, 1, 1),
1537 return pp_console_row(next(self.pps()))
1539 def pps(self, decode_path=()):
1541 asn1_type_name=self.asn1_type_name,
1542 obj_name=self.__class__.__name__,
1543 decode_path=decode_path,
1544 value=str(self._value) if self.ready else None,
1545 optional=self.optional,
1546 default=self == self.default,
1547 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1548 expl=None if self._expl is None else tag_decode(self._expl),
1553 expl_offset=self.expl_offset if self.expled else None,
1554 expl_tlen=self.expl_tlen if self.expled else None,
1555 expl_llen=self.expl_llen if self.expled else None,
1556 expl_vlen=self.expl_vlen if self.expled else None,
1557 expl_lenindef=self.expl_lenindef,
1563 """``INTEGER`` integer type
1565 >>> b = Integer(-123)
1567 >>> b == Integer(-123)
1572 >>> Integer(2, bounds=(1, 3))
1574 >>> Integer(5, bounds=(1, 3))
1575 Traceback (most recent call last):
1576 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1580 class Version(Integer):
1587 >>> v = Version("v1")
1594 {'v3': 2, 'v1': 0, 'v2': 1}
1596 __slots__ = ("specs", "_bound_min", "_bound_max")
1597 tag_default = tag_encode(2)
1598 asn1_type_name = "INTEGER"
1612 :param value: set the value. Either integer type, named value
1613 (if ``schema`` is specified in the class), or
1614 :py:class:`pyderasn.Integer` object
1615 :param bounds: set ``(MIN, MAX)`` value constraint.
1616 (-inf, +inf) by default
1617 :param bytes impl: override default tag with ``IMPLICIT`` one
1618 :param bytes expl: override default tag with ``EXPLICIT`` one
1619 :param default: set default value. Type same as in ``value``
1620 :param bool optional: is object ``OPTIONAL`` in sequence
1622 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1624 specs = getattr(self, "schema", {}) if _specs is None else _specs
1625 self.specs = specs if isinstance(specs, dict) else dict(specs)
1626 self._bound_min, self._bound_max = getattr(
1629 (float("-inf"), float("+inf")),
1630 ) if bounds is None else bounds
1631 if value is not None:
1632 self._value = self._value_sanitize(value)
1633 if default is not None:
1634 default = self._value_sanitize(default)
1635 self.default = self.__class__(
1641 if self._value is None:
1642 self._value = default
1644 def _value_sanitize(self, value):
1645 if issubclass(value.__class__, Integer):
1646 value = value._value
1647 elif isinstance(value, integer_types):
1649 elif isinstance(value, str):
1650 value = self.specs.get(value)
1652 raise ObjUnknown("integer value: %s" % value)
1654 raise InvalidValueType((self.__class__, int, str))
1655 if not self._bound_min <= value <= self._bound_max:
1656 raise BoundsError(self._bound_min, value, self._bound_max)
1661 return self._value is not None
1664 obj = self.__class__(_specs=self.specs)
1665 obj._value = self._value
1666 obj._bound_min = self._bound_min
1667 obj._bound_max = self._bound_max
1669 obj._expl = self._expl
1670 obj.default = self.default
1671 obj.optional = self.optional
1672 obj.offset = self.offset
1673 obj.llen = self.llen
1674 obj.vlen = self.vlen
1678 self._assert_ready()
1679 return int(self._value)
1682 self._assert_ready()
1685 bytes(self._expl or b"") +
1686 str(self._value).encode("ascii"),
1689 def __eq__(self, their):
1690 if isinstance(their, integer_types):
1691 return self._value == their
1692 if not issubclass(their.__class__, Integer):
1695 self._value == their._value and
1696 self.tag == their.tag and
1697 self._expl == their._expl
1700 def __lt__(self, their):
1701 return self._value < their._value
1705 for name, value in self.specs.items():
1706 if value == self._value:
1718 return self.__class__(
1721 (self._bound_min, self._bound_max)
1722 if bounds is None else bounds
1724 impl=self.tag if impl is None else impl,
1725 expl=self._expl if expl is None else expl,
1726 default=self.default if default is None else default,
1727 optional=self.optional if optional is None else optional,
1732 self._assert_ready()
1736 octets = bytearray([0])
1740 octets = bytearray()
1742 octets.append((value & 0xFF) ^ 0xFF)
1744 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1747 octets = bytearray()
1749 octets.append(value & 0xFF)
1751 if octets[-1] & 0x80 > 0:
1754 octets = bytes(octets)
1756 bytes_len = ceil(value.bit_length() / 8) or 1
1759 octets = value.to_bytes(
1764 except OverflowError:
1768 return b"".join((self.tag, len_encode(len(octets)), octets))
1770 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1772 t, _, lv = tag_strip(tlv)
1773 except DecodeError as err:
1774 raise err.__class__(
1776 klass=self.__class__,
1777 decode_path=decode_path,
1782 klass=self.__class__,
1783 decode_path=decode_path,
1789 l, llen, v = len_decode(lv)
1790 except DecodeError as err:
1791 raise err.__class__(
1793 klass=self.__class__,
1794 decode_path=decode_path,
1798 raise NotEnoughData(
1799 "encoded length is longer than data",
1800 klass=self.__class__,
1801 decode_path=decode_path,
1805 raise NotEnoughData(
1807 klass=self.__class__,
1808 decode_path=decode_path,
1811 v, tail = v[:l], v[l:]
1812 first_octet = byte2int(v)
1814 second_octet = byte2int(v[1:])
1816 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1817 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1820 "non normalized integer",
1821 klass=self.__class__,
1822 decode_path=decode_path,
1827 if first_octet & 0x80 > 0:
1828 octets = bytearray()
1829 for octet in bytearray(v):
1830 octets.append(octet ^ 0xFF)
1831 for octet in octets:
1832 value = (value << 8) | octet
1836 for octet in bytearray(v):
1837 value = (value << 8) | octet
1839 value = int.from_bytes(v, byteorder="big", signed=True)
1841 obj = self.__class__(
1843 bounds=(self._bound_min, self._bound_max),
1846 default=self.default,
1847 optional=self.optional,
1849 _decoded=(offset, llen, l),
1851 except BoundsError as err:
1854 klass=self.__class__,
1855 decode_path=decode_path,
1861 return pp_console_row(next(self.pps()))
1863 def pps(self, decode_path=()):
1865 asn1_type_name=self.asn1_type_name,
1866 obj_name=self.__class__.__name__,
1867 decode_path=decode_path,
1868 value=(self.named or str(self._value)) if self.ready else None,
1869 optional=self.optional,
1870 default=self == self.default,
1871 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1872 expl=None if self._expl is None else tag_decode(self._expl),
1877 expl_offset=self.expl_offset if self.expled else None,
1878 expl_tlen=self.expl_tlen if self.expled else None,
1879 expl_llen=self.expl_llen if self.expled else None,
1880 expl_vlen=self.expl_vlen if self.expled else None,
1881 expl_lenindef=self.expl_lenindef,
1885 class BitString(Obj):
1886 """``BIT STRING`` bit string type
1888 >>> BitString(b"hello world")
1889 BIT STRING 88 bits 68656c6c6f20776f726c64
1892 >>> b == b"hello world"
1897 >>> BitString("'0A3B5F291CD'H")
1898 BIT STRING 44 bits 0a3b5f291cd0
1899 >>> b = BitString("'010110000000'B")
1900 BIT STRING 12 bits 5800
1903 >>> b[0], b[1], b[2], b[3]
1904 (False, True, False, True)
1908 [False, True, False, True, True, False, False, False, False, False, False, False]
1912 class KeyUsage(BitString):
1914 ("digitalSignature", 0),
1915 ("nonRepudiation", 1),
1916 ("keyEncipherment", 2),
1919 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1920 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1922 ['nonRepudiation', 'keyEncipherment']
1924 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1928 Pay attention that BIT STRING can be encoded both in primitive
1929 and constructed forms. Decoder always checks constructed form tag
1930 additionally to specified primitive one. If BER decoding is
1931 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
1932 of DER restrictions.
1934 __slots__ = ("tag_constructed", "specs", "defined")
1935 tag_default = tag_encode(3)
1936 asn1_type_name = "BIT STRING"
1949 :param value: set the value. Either binary type, tuple of named
1950 values (if ``schema`` is specified in the class),
1951 string in ``'XXX...'B`` form, or
1952 :py:class:`pyderasn.BitString` object
1953 :param bytes impl: override default tag with ``IMPLICIT`` one
1954 :param bytes expl: override default tag with ``EXPLICIT`` one
1955 :param default: set default value. Type same as in ``value``
1956 :param bool optional: is object ``OPTIONAL`` in sequence
1958 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1959 specs = getattr(self, "schema", {}) if _specs is None else _specs
1960 self.specs = specs if isinstance(specs, dict) else dict(specs)
1961 self._value = None if value is None else self._value_sanitize(value)
1962 if default is not None:
1963 default = self._value_sanitize(default)
1964 self.default = self.__class__(
1970 self._value = default
1972 tag_klass, _, tag_num = tag_decode(self.tag)
1973 self.tag_constructed = tag_encode(
1975 form=TagFormConstructed,
1979 def _bits2octets(self, bits):
1980 if len(self.specs) > 0:
1981 bits = bits.rstrip("0")
1983 bits += "0" * ((8 - (bit_len % 8)) % 8)
1984 octets = bytearray(len(bits) // 8)
1985 for i in six_xrange(len(octets)):
1986 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1987 return bit_len, bytes(octets)
1989 def _value_sanitize(self, value):
1990 if issubclass(value.__class__, BitString):
1992 if isinstance(value, (string_types, binary_type)):
1994 isinstance(value, string_types) and
1995 value.startswith("'")
1997 if value.endswith("'B"):
1999 if not set(value) <= set(("0", "1")):
2000 raise ValueError("B's coding contains unacceptable chars")
2001 return self._bits2octets(value)
2002 elif value.endswith("'H"):
2006 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2008 if isinstance(value, binary_type):
2009 return (len(value) * 8, value)
2011 raise InvalidValueType((self.__class__, string_types, binary_type))
2012 if isinstance(value, tuple):
2015 isinstance(value[0], integer_types) and
2016 isinstance(value[1], binary_type)
2021 bit = self.specs.get(name)
2023 raise ObjUnknown("BitString value: %s" % name)
2026 return self._bits2octets("")
2028 return self._bits2octets("".join(
2029 ("1" if bit in bits else "0")
2030 for bit in six_xrange(max(bits) + 1)
2032 raise InvalidValueType((self.__class__, binary_type, string_types))
2036 return self._value is not None
2039 obj = self.__class__(_specs=self.specs)
2041 if value is not None:
2042 value = (value[0], value[1])
2045 obj._expl = self._expl
2046 obj.default = self.default
2047 obj.optional = self.optional
2048 obj.offset = self.offset
2049 obj.llen = self.llen
2050 obj.vlen = self.vlen
2054 self._assert_ready()
2055 for i in six_xrange(self._value[0]):
2060 self._assert_ready()
2061 return self._value[0]
2063 def __bytes__(self):
2064 self._assert_ready()
2065 return self._value[1]
2067 def __eq__(self, their):
2068 if isinstance(their, bytes):
2069 return self._value[1] == their
2070 if not issubclass(their.__class__, BitString):
2073 self._value == their._value and
2074 self.tag == their.tag and
2075 self._expl == their._expl
2080 return [name for name, bit in self.specs.items() if self[bit]]
2090 return self.__class__(
2092 impl=self.tag if impl is None else impl,
2093 expl=self._expl if expl is None else expl,
2094 default=self.default if default is None else default,
2095 optional=self.optional if optional is None else optional,
2099 def __getitem__(self, key):
2100 if isinstance(key, int):
2101 bit_len, octets = self._value
2105 byte2int(memoryview(octets)[key // 8:]) >>
2108 if isinstance(key, string_types):
2109 value = self.specs.get(key)
2111 raise ObjUnknown("BitString value: %s" % key)
2113 raise InvalidValueType((int, str))
2116 self._assert_ready()
2117 bit_len, octets = self._value
2120 len_encode(len(octets) + 1),
2121 int2byte((8 - bit_len % 8) % 8),
2125 def _decode_chunk(self, lv, offset, decode_path, ctx):
2127 l, llen, v = len_decode(lv)
2128 except DecodeError as err:
2129 raise err.__class__(
2131 klass=self.__class__,
2132 decode_path=decode_path,
2136 raise NotEnoughData(
2137 "encoded length is longer than data",
2138 klass=self.__class__,
2139 decode_path=decode_path,
2143 raise NotEnoughData(
2145 klass=self.__class__,
2146 decode_path=decode_path,
2149 pad_size = byte2int(v)
2150 if l == 1 and pad_size != 0:
2152 "invalid empty value",
2153 klass=self.__class__,
2154 decode_path=decode_path,
2160 klass=self.__class__,
2161 decode_path=decode_path,
2164 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2167 klass=self.__class__,
2168 decode_path=decode_path,
2171 v, tail = v[:l], v[l:]
2172 obj = self.__class__(
2173 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2176 default=self.default,
2177 optional=self.optional,
2179 _decoded=(offset, llen, l),
2183 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2185 t, tlen, lv = tag_strip(tlv)
2186 except DecodeError as err:
2187 raise err.__class__(
2189 klass=self.__class__,
2190 decode_path=decode_path,
2196 return self._decode_chunk(lv, offset, decode_path, ctx)
2197 if t == self.tag_constructed:
2198 if not ctx.get("bered", False):
2200 "unallowed BER constructed encoding",
2201 klass=self.__class__,
2202 decode_path=decode_path,
2209 l, llen, v = len_decode(lv)
2210 except LenIndefForm:
2211 llen, l, v = 1, 0, lv[1:]
2213 except DecodeError as err:
2214 raise err.__class__(
2216 klass=self.__class__,
2217 decode_path=decode_path,
2221 raise NotEnoughData(
2222 "encoded length is longer than data",
2223 klass=self.__class__,
2224 decode_path=decode_path,
2227 if not lenindef and l == 0:
2228 raise NotEnoughData(
2230 klass=self.__class__,
2231 decode_path=decode_path,
2235 sub_offset = offset + tlen + llen
2239 if v[:EOC_LEN].tobytes() == EOC:
2246 "chunk out of bounds",
2247 klass=self.__class__,
2248 decode_path=decode_path + (str(len(chunks) - 1),),
2249 offset=chunks[-1].offset,
2251 sub_decode_path = decode_path + (str(len(chunks)),)
2253 chunk, v_tail = BitString().decode(
2256 decode_path=sub_decode_path,
2262 "expected BitString encoded chunk",
2263 klass=self.__class__,
2264 decode_path=sub_decode_path,
2267 chunks.append(chunk)
2268 sub_offset += chunk.tlvlen
2269 vlen += chunk.tlvlen
2271 if len(chunks) == 0:
2274 klass=self.__class__,
2275 decode_path=decode_path,
2280 for chunk_i, chunk in enumerate(chunks[:-1]):
2281 if chunk.bit_len % 8 != 0:
2283 "BitString chunk is not multiple of 8 bits",
2284 klass=self.__class__,
2285 decode_path=decode_path + (str(chunk_i),),
2286 offset=chunk.offset,
2288 values.append(bytes(chunk))
2289 bit_len += chunk.bit_len
2290 chunk_last = chunks[-1]
2291 values.append(bytes(chunk_last))
2292 bit_len += chunk_last.bit_len
2293 obj = self.__class__(
2294 value=(bit_len, b"".join(values)),
2297 default=self.default,
2298 optional=self.optional,
2300 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2302 obj.lenindef = lenindef
2304 return obj, (v[EOC_LEN:] if lenindef else v)
2306 klass=self.__class__,
2307 decode_path=decode_path,
2312 return pp_console_row(next(self.pps()))
2314 def pps(self, decode_path=()):
2318 bit_len, blob = self._value
2319 value = "%d bits" % bit_len
2320 if len(self.specs) > 0:
2321 blob = tuple(self.named)
2323 asn1_type_name=self.asn1_type_name,
2324 obj_name=self.__class__.__name__,
2325 decode_path=decode_path,
2328 optional=self.optional,
2329 default=self == self.default,
2330 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2331 expl=None if self._expl is None else tag_decode(self._expl),
2336 expl_offset=self.expl_offset if self.expled else None,
2337 expl_tlen=self.expl_tlen if self.expled else None,
2338 expl_llen=self.expl_llen if self.expled else None,
2339 expl_vlen=self.expl_vlen if self.expled else None,
2340 expl_lenindef=self.expl_lenindef,
2341 lenindef=self.lenindef,
2344 defined_by, defined = self.defined or (None, None)
2345 if defined_by is not None:
2347 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2351 class OctetString(Obj):
2352 """``OCTET STRING`` binary string type
2354 >>> s = OctetString(b"hello world")
2355 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2356 >>> s == OctetString(b"hello world")
2361 >>> OctetString(b"hello", bounds=(4, 4))
2362 Traceback (most recent call last):
2363 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2364 >>> OctetString(b"hell", bounds=(4, 4))
2365 OCTET STRING 4 bytes 68656c6c
2369 Pay attention that OCTET STRING can be encoded both in primitive
2370 and constructed forms. Decoder always checks constructed form tag
2371 additionally to specified primitive one. If BER decoding is
2372 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2373 of DER restrictions.
2375 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2376 tag_default = tag_encode(4)
2377 asn1_type_name = "OCTET STRING"
2390 :param value: set the value. Either binary type, or
2391 :py:class:`pyderasn.OctetString` object
2392 :param bounds: set ``(MIN, MAX)`` value size constraint.
2393 (-inf, +inf) by default
2394 :param bytes impl: override default tag with ``IMPLICIT`` one
2395 :param bytes expl: override default tag with ``EXPLICIT`` one
2396 :param default: set default value. Type same as in ``value``
2397 :param bool optional: is object ``OPTIONAL`` in sequence
2399 super(OctetString, self).__init__(
2407 self._bound_min, self._bound_max = getattr(
2411 ) if bounds is None else bounds
2412 if value is not None:
2413 self._value = self._value_sanitize(value)
2414 if default is not None:
2415 default = self._value_sanitize(default)
2416 self.default = self.__class__(
2421 if self._value is None:
2422 self._value = default
2424 tag_klass, _, tag_num = tag_decode(self.tag)
2425 self.tag_constructed = tag_encode(
2427 form=TagFormConstructed,
2431 def _value_sanitize(self, value):
2432 if issubclass(value.__class__, OctetString):
2433 value = value._value
2434 elif isinstance(value, binary_type):
2437 raise InvalidValueType((self.__class__, bytes))
2438 if not self._bound_min <= len(value) <= self._bound_max:
2439 raise BoundsError(self._bound_min, len(value), self._bound_max)
2444 return self._value is not None
2447 obj = self.__class__()
2448 obj._value = self._value
2449 obj._bound_min = self._bound_min
2450 obj._bound_max = self._bound_max
2452 obj._expl = self._expl
2453 obj.default = self.default
2454 obj.optional = self.optional
2455 obj.offset = self.offset
2456 obj.llen = self.llen
2457 obj.vlen = self.vlen
2460 def __bytes__(self):
2461 self._assert_ready()
2464 def __eq__(self, their):
2465 if isinstance(their, binary_type):
2466 return self._value == their
2467 if not issubclass(their.__class__, OctetString):
2470 self._value == their._value and
2471 self.tag == their.tag and
2472 self._expl == their._expl
2475 def __lt__(self, their):
2476 return self._value < their._value
2487 return self.__class__(
2490 (self._bound_min, self._bound_max)
2491 if bounds is None else bounds
2493 impl=self.tag if impl is None else impl,
2494 expl=self._expl if expl is None else expl,
2495 default=self.default if default is None else default,
2496 optional=self.optional if optional is None else optional,
2500 self._assert_ready()
2503 len_encode(len(self._value)),
2507 def _decode_chunk(self, lv, offset, decode_path, ctx):
2509 l, llen, v = len_decode(lv)
2510 except DecodeError as err:
2511 raise err.__class__(
2513 klass=self.__class__,
2514 decode_path=decode_path,
2518 raise NotEnoughData(
2519 "encoded length is longer than data",
2520 klass=self.__class__,
2521 decode_path=decode_path,
2524 v, tail = v[:l], v[l:]
2526 obj = self.__class__(
2528 bounds=(self._bound_min, self._bound_max),
2531 default=self.default,
2532 optional=self.optional,
2533 _decoded=(offset, llen, l),
2535 except DecodeError as err:
2538 klass=self.__class__,
2539 decode_path=decode_path,
2542 except BoundsError as err:
2545 klass=self.__class__,
2546 decode_path=decode_path,
2551 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2553 t, tlen, lv = tag_strip(tlv)
2554 except DecodeError as err:
2555 raise err.__class__(
2557 klass=self.__class__,
2558 decode_path=decode_path,
2564 return self._decode_chunk(lv, offset, decode_path, ctx)
2565 if t == self.tag_constructed:
2566 if not ctx.get("bered", False):
2568 "unallowed BER constructed encoding",
2569 klass=self.__class__,
2570 decode_path=decode_path,
2577 l, llen, v = len_decode(lv)
2578 except LenIndefForm:
2579 llen, l, v = 1, 0, lv[1:]
2581 except DecodeError as err:
2582 raise err.__class__(
2584 klass=self.__class__,
2585 decode_path=decode_path,
2589 raise NotEnoughData(
2590 "encoded length is longer than data",
2591 klass=self.__class__,
2592 decode_path=decode_path,
2596 sub_offset = offset + tlen + llen
2600 if v[:EOC_LEN].tobytes() == EOC:
2607 "chunk out of bounds",
2608 klass=self.__class__,
2609 decode_path=decode_path + (str(len(chunks) - 1),),
2610 offset=chunks[-1].offset,
2612 sub_decode_path = decode_path + (str(len(chunks)),)
2614 chunk, v_tail = OctetString().decode(
2617 decode_path=sub_decode_path,
2623 "expected OctetString encoded chunk",
2624 klass=self.__class__,
2625 decode_path=sub_decode_path,
2628 chunks.append(chunk)
2629 sub_offset += chunk.tlvlen
2630 vlen += chunk.tlvlen
2633 obj = self.__class__(
2634 value=b"".join(bytes(chunk) for chunk in chunks),
2635 bounds=(self._bound_min, self._bound_max),
2638 default=self.default,
2639 optional=self.optional,
2640 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2642 except DecodeError as err:
2645 klass=self.__class__,
2646 decode_path=decode_path,
2649 except BoundsError as err:
2652 klass=self.__class__,
2653 decode_path=decode_path,
2656 obj.lenindef = lenindef
2658 return obj, (v[EOC_LEN:] if lenindef else v)
2660 klass=self.__class__,
2661 decode_path=decode_path,
2666 return pp_console_row(next(self.pps()))
2668 def pps(self, decode_path=()):
2670 asn1_type_name=self.asn1_type_name,
2671 obj_name=self.__class__.__name__,
2672 decode_path=decode_path,
2673 value=("%d bytes" % len(self._value)) if self.ready else None,
2674 blob=self._value if self.ready else None,
2675 optional=self.optional,
2676 default=self == self.default,
2677 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2678 expl=None if self._expl is None else tag_decode(self._expl),
2683 expl_offset=self.expl_offset if self.expled else None,
2684 expl_tlen=self.expl_tlen if self.expled else None,
2685 expl_llen=self.expl_llen if self.expled else None,
2686 expl_vlen=self.expl_vlen if self.expled else None,
2687 expl_lenindef=self.expl_lenindef,
2688 lenindef=self.lenindef,
2691 defined_by, defined = self.defined or (None, None)
2692 if defined_by is not None:
2694 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2699 """``NULL`` null object
2707 tag_default = tag_encode(5)
2708 asn1_type_name = "NULL"
2712 value=None, # unused, but Sequence passes it
2719 :param bytes impl: override default tag with ``IMPLICIT`` one
2720 :param bytes expl: override default tag with ``EXPLICIT`` one
2721 :param bool optional: is object ``OPTIONAL`` in sequence
2723 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2731 obj = self.__class__()
2733 obj._expl = self._expl
2734 obj.default = self.default
2735 obj.optional = self.optional
2736 obj.offset = self.offset
2737 obj.llen = self.llen
2738 obj.vlen = self.vlen
2741 def __eq__(self, their):
2742 if not issubclass(their.__class__, Null):
2745 self.tag == their.tag and
2746 self._expl == their._expl
2756 return self.__class__(
2757 impl=self.tag if impl is None else impl,
2758 expl=self._expl if expl is None else expl,
2759 optional=self.optional if optional is None else optional,
2763 return self.tag + len_encode(0)
2765 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2767 t, _, lv = tag_strip(tlv)
2768 except DecodeError as err:
2769 raise err.__class__(
2771 klass=self.__class__,
2772 decode_path=decode_path,
2777 klass=self.__class__,
2778 decode_path=decode_path,
2784 l, _, v = len_decode(lv)
2785 except DecodeError as err:
2786 raise err.__class__(
2788 klass=self.__class__,
2789 decode_path=decode_path,
2793 raise InvalidLength(
2794 "Null must have zero length",
2795 klass=self.__class__,
2796 decode_path=decode_path,
2799 obj = self.__class__(
2802 optional=self.optional,
2803 _decoded=(offset, 1, 0),
2808 return pp_console_row(next(self.pps()))
2810 def pps(self, decode_path=()):
2812 asn1_type_name=self.asn1_type_name,
2813 obj_name=self.__class__.__name__,
2814 decode_path=decode_path,
2815 optional=self.optional,
2816 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2817 expl=None if self._expl is None else tag_decode(self._expl),
2822 expl_offset=self.expl_offset if self.expled else None,
2823 expl_tlen=self.expl_tlen if self.expled else None,
2824 expl_llen=self.expl_llen if self.expled else None,
2825 expl_vlen=self.expl_vlen if self.expled else None,
2826 expl_lenindef=self.expl_lenindef,
2830 class ObjectIdentifier(Obj):
2831 """``OBJECT IDENTIFIER`` OID type
2833 >>> oid = ObjectIdentifier((1, 2, 3))
2834 OBJECT IDENTIFIER 1.2.3
2835 >>> oid == ObjectIdentifier("1.2.3")
2841 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2842 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2844 >>> str(ObjectIdentifier((3, 1)))
2845 Traceback (most recent call last):
2846 pyderasn.InvalidOID: unacceptable first arc value
2848 __slots__ = ("defines",)
2849 tag_default = tag_encode(6)
2850 asn1_type_name = "OBJECT IDENTIFIER"
2863 :param value: set the value. Either tuples of integers,
2864 string of "."-concatenated integers, or
2865 :py:class:`pyderasn.ObjectIdentifier` object
2866 :param defines: sequence of tuples. Each tuple has two elements.
2867 First one is relative to current one decode
2868 path, aiming to the field defined by that OID.
2869 Read about relative path in
2870 :py:func:`pyderasn.abs_decode_path`. Second
2871 tuple element is ``{OID: pyderasn.Obj()}``
2872 dictionary, mapping between current OID value
2873 and structure applied to defined field.
2874 :ref:`Read about DEFINED BY <definedby>`
2875 :param bytes impl: override default tag with ``IMPLICIT`` one
2876 :param bytes expl: override default tag with ``EXPLICIT`` one
2877 :param default: set default value. Type same as in ``value``
2878 :param bool optional: is object ``OPTIONAL`` in sequence
2880 super(ObjectIdentifier, self).__init__(
2888 if value is not None:
2889 self._value = self._value_sanitize(value)
2890 if default is not None:
2891 default = self._value_sanitize(default)
2892 self.default = self.__class__(
2897 if self._value is None:
2898 self._value = default
2899 self.defines = defines
2901 def __add__(self, their):
2902 if isinstance(their, self.__class__):
2903 return self.__class__(self._value + their._value)
2904 if isinstance(their, tuple):
2905 return self.__class__(self._value + their)
2906 raise InvalidValueType((self.__class__, tuple))
2908 def _value_sanitize(self, value):
2909 if issubclass(value.__class__, ObjectIdentifier):
2911 if isinstance(value, string_types):
2913 value = tuple(int(arc) for arc in value.split("."))
2915 raise InvalidOID("unacceptable arcs values")
2916 if isinstance(value, tuple):
2918 raise InvalidOID("less than 2 arcs")
2919 first_arc = value[0]
2920 if first_arc in (0, 1):
2921 if not (0 <= value[1] <= 39):
2922 raise InvalidOID("second arc is too wide")
2923 elif first_arc == 2:
2926 raise InvalidOID("unacceptable first arc value")
2928 raise InvalidValueType((self.__class__, str, tuple))
2932 return self._value is not None
2935 obj = self.__class__()
2936 obj._value = self._value
2937 obj.defines = self.defines
2939 obj._expl = self._expl
2940 obj.default = self.default
2941 obj.optional = self.optional
2942 obj.offset = self.offset
2943 obj.llen = self.llen
2944 obj.vlen = self.vlen
2948 self._assert_ready()
2949 return iter(self._value)
2952 return ".".join(str(arc) for arc in self._value or ())
2955 self._assert_ready()
2958 bytes(self._expl or b"") +
2959 str(self._value).encode("ascii"),
2962 def __eq__(self, their):
2963 if isinstance(their, tuple):
2964 return self._value == their
2965 if not issubclass(their.__class__, ObjectIdentifier):
2968 self.tag == their.tag and
2969 self._expl == their._expl and
2970 self._value == their._value
2973 def __lt__(self, their):
2974 return self._value < their._value
2985 return self.__class__(
2987 defines=self.defines if defines is None else defines,
2988 impl=self.tag if impl is None else impl,
2989 expl=self._expl if expl is None else expl,
2990 default=self.default if default is None else default,
2991 optional=self.optional if optional is None else optional,
2995 self._assert_ready()
2997 first_value = value[1]
2998 first_arc = value[0]
3001 elif first_arc == 1:
3003 elif first_arc == 2:
3005 else: # pragma: no cover
3006 raise RuntimeError("invalid arc is stored")
3007 octets = [zero_ended_encode(first_value)]
3008 for arc in value[2:]:
3009 octets.append(zero_ended_encode(arc))
3010 v = b"".join(octets)
3011 return b"".join((self.tag, len_encode(len(v)), v))
3013 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3015 t, _, lv = tag_strip(tlv)
3016 except DecodeError as err:
3017 raise err.__class__(
3019 klass=self.__class__,
3020 decode_path=decode_path,
3025 klass=self.__class__,
3026 decode_path=decode_path,
3032 l, llen, v = len_decode(lv)
3033 except DecodeError as err:
3034 raise err.__class__(
3036 klass=self.__class__,
3037 decode_path=decode_path,
3041 raise NotEnoughData(
3042 "encoded length is longer than data",
3043 klass=self.__class__,
3044 decode_path=decode_path,
3048 raise NotEnoughData(
3050 klass=self.__class__,
3051 decode_path=decode_path,
3054 v, tail = v[:l], v[l:]
3060 octet = indexbytes(v, i)
3061 arc = (arc << 7) | (octet & 0x7F)
3062 if octet & 0x80 == 0:
3070 klass=self.__class__,
3071 decode_path=decode_path,
3075 second_arc = arcs[0]
3076 if 0 <= second_arc <= 39:
3078 elif 40 <= second_arc <= 79:
3084 obj = self.__class__(
3085 value=tuple([first_arc, second_arc] + arcs[1:]),
3088 default=self.default,
3089 optional=self.optional,
3090 _decoded=(offset, llen, l),
3095 return pp_console_row(next(self.pps()))
3097 def pps(self, decode_path=()):
3099 asn1_type_name=self.asn1_type_name,
3100 obj_name=self.__class__.__name__,
3101 decode_path=decode_path,
3102 value=str(self) if self.ready else None,
3103 optional=self.optional,
3104 default=self == self.default,
3105 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3106 expl=None if self._expl is None else tag_decode(self._expl),
3111 expl_offset=self.expl_offset if self.expled else None,
3112 expl_tlen=self.expl_tlen if self.expled else None,
3113 expl_llen=self.expl_llen if self.expled else None,
3114 expl_vlen=self.expl_vlen if self.expled else None,
3115 expl_lenindef=self.expl_lenindef,
3119 class Enumerated(Integer):
3120 """``ENUMERATED`` integer type
3122 This type is identical to :py:class:`pyderasn.Integer`, but requires
3123 schema to be specified and does not accept values missing from it.
3126 tag_default = tag_encode(10)
3127 asn1_type_name = "ENUMERATED"
3138 bounds=None, # dummy argument, workability for Integer.decode
3140 super(Enumerated, self).__init__(
3149 if len(self.specs) == 0:
3150 raise ValueError("schema must be specified")
3152 def _value_sanitize(self, value):
3153 if isinstance(value, self.__class__):
3154 value = value._value
3155 elif isinstance(value, integer_types):
3156 if value not in list(self.specs.values()):
3158 "unknown integer value: %s" % value,
3159 klass=self.__class__,
3161 elif isinstance(value, string_types):
3162 value = self.specs.get(value)
3164 raise ObjUnknown("integer value: %s" % value)
3166 raise InvalidValueType((self.__class__, int, str))
3170 obj = self.__class__(_specs=self.specs)
3171 obj._value = self._value
3172 obj._bound_min = self._bound_min
3173 obj._bound_max = self._bound_max
3175 obj._expl = self._expl
3176 obj.default = self.default
3177 obj.optional = self.optional
3178 obj.offset = self.offset
3179 obj.llen = self.llen
3180 obj.vlen = self.vlen
3192 return self.__class__(
3194 impl=self.tag if impl is None else impl,
3195 expl=self._expl if expl is None else expl,
3196 default=self.default if default is None else default,
3197 optional=self.optional if optional is None else optional,
3202 class CommonString(OctetString):
3203 """Common class for all strings
3205 Everything resembles :py:class:`pyderasn.OctetString`, except
3206 ability to deal with unicode text strings.
3208 >>> hexenc("привет мир".encode("utf-8"))
3209 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3210 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3212 >>> s = UTF8String("привет мир")
3213 UTF8String UTF8String привет мир
3215 'привет мир'
3216 >>> hexenc(bytes(s))
3217 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3219 >>> PrintableString("привет мир")
3220 Traceback (most recent call last):
3221 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3223 >>> BMPString("ада", bounds=(2, 2))
3224 Traceback (most recent call last):
3225 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3226 >>> s = BMPString("ад", bounds=(2, 2))
3229 >>> hexenc(bytes(s))
3237 * - :py:class:`pyderasn.UTF8String`
3239 * - :py:class:`pyderasn.NumericString`
3241 * - :py:class:`pyderasn.PrintableString`
3243 * - :py:class:`pyderasn.TeletexString`
3245 * - :py:class:`pyderasn.T61String`
3247 * - :py:class:`pyderasn.VideotexString`
3249 * - :py:class:`pyderasn.IA5String`
3251 * - :py:class:`pyderasn.GraphicString`
3253 * - :py:class:`pyderasn.VisibleString`
3255 * - :py:class:`pyderasn.ISO646String`
3257 * - :py:class:`pyderasn.GeneralString`
3259 * - :py:class:`pyderasn.UniversalString`
3261 * - :py:class:`pyderasn.BMPString`
3264 __slots__ = ("encoding",)
3266 def _value_sanitize(self, value):
3268 value_decoded = None
3269 if isinstance(value, self.__class__):
3270 value_raw = value._value
3271 elif isinstance(value, text_type):
3272 value_decoded = value
3273 elif isinstance(value, binary_type):
3276 raise InvalidValueType((self.__class__, text_type, binary_type))
3279 value_decoded.encode(self.encoding)
3280 if value_raw is None else value_raw
3283 value_raw.decode(self.encoding)
3284 if value_decoded is None else value_decoded
3286 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3287 raise DecodeError(str(err))
3288 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3296 def __eq__(self, their):
3297 if isinstance(their, binary_type):
3298 return self._value == their
3299 if isinstance(their, text_type):
3300 return self._value == their.encode(self.encoding)
3301 if not isinstance(their, self.__class__):
3304 self._value == their._value and
3305 self.tag == their.tag and
3306 self._expl == their._expl
3309 def __unicode__(self):
3311 return self._value.decode(self.encoding)
3312 return text_type(self._value)
3315 return pp_console_row(next(self.pps(no_unicode=PY2)))
3317 def pps(self, decode_path=(), no_unicode=False):
3320 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3322 asn1_type_name=self.asn1_type_name,
3323 obj_name=self.__class__.__name__,
3324 decode_path=decode_path,
3326 optional=self.optional,
3327 default=self == self.default,
3328 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3329 expl=None if self._expl is None else tag_decode(self._expl),
3334 expl_offset=self.expl_offset if self.expled else None,
3335 expl_tlen=self.expl_tlen if self.expled else None,
3336 expl_llen=self.expl_llen if self.expled else None,
3337 expl_vlen=self.expl_vlen if self.expled else None,
3338 expl_lenindef=self.expl_lenindef,
3342 class UTF8String(CommonString):
3344 tag_default = tag_encode(12)
3346 asn1_type_name = "UTF8String"
3349 class NumericString(CommonString):
3352 Its value is properly sanitized: only ASCII digits can be stored.
3355 tag_default = tag_encode(18)
3357 asn1_type_name = "NumericString"
3358 allowable_chars = set(digits.encode("ascii"))
3360 def _value_sanitize(self, value):
3361 value = super(NumericString, self)._value_sanitize(value)
3362 if not set(value) <= self.allowable_chars:
3363 raise DecodeError("non-numeric value")
3367 class PrintableString(CommonString):
3369 tag_default = tag_encode(19)
3371 asn1_type_name = "PrintableString"
3374 class TeletexString(CommonString):
3376 tag_default = tag_encode(20)
3378 asn1_type_name = "TeletexString"
3381 class T61String(TeletexString):
3383 asn1_type_name = "T61String"
3386 class VideotexString(CommonString):
3388 tag_default = tag_encode(21)
3389 encoding = "iso-8859-1"
3390 asn1_type_name = "VideotexString"
3393 class IA5String(CommonString):
3395 tag_default = tag_encode(22)
3397 asn1_type_name = "IA5"
3400 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3401 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3402 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3405 class UTCTime(CommonString):
3406 """``UTCTime`` datetime type
3408 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3409 UTCTime UTCTime 2017-09-30T22:07:50
3415 datetime.datetime(2017, 9, 30, 22, 7, 50)
3416 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3417 datetime.datetime(1957, 9, 30, 22, 7, 50)
3420 tag_default = tag_encode(23)
3422 asn1_type_name = "UTCTime"
3424 fmt = "%y%m%d%H%M%SZ"
3434 bounds=None, # dummy argument, workability for OctetString.decode
3437 :param value: set the value. Either datetime type, or
3438 :py:class:`pyderasn.UTCTime` object
3439 :param bytes impl: override default tag with ``IMPLICIT`` one
3440 :param bytes expl: override default tag with ``EXPLICIT`` one
3441 :param default: set default value. Type same as in ``value``
3442 :param bool optional: is object ``OPTIONAL`` in sequence
3444 super(UTCTime, self).__init__(
3452 if value is not None:
3453 self._value = self._value_sanitize(value)
3454 if default is not None:
3455 default = self._value_sanitize(default)
3456 self.default = self.__class__(
3461 if self._value is None:
3462 self._value = default
3464 def _value_sanitize(self, value):
3465 if isinstance(value, self.__class__):
3467 if isinstance(value, datetime):
3468 return value.strftime(self.fmt).encode("ascii")
3469 if isinstance(value, binary_type):
3470 value_decoded = value.decode("ascii")
3471 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3473 datetime.strptime(value_decoded, self.fmt)
3475 raise DecodeError("invalid UTCTime format")
3478 raise DecodeError("invalid UTCTime length")
3479 raise InvalidValueType((self.__class__, datetime))
3481 def __eq__(self, their):
3482 if isinstance(their, binary_type):
3483 return self._value == their
3484 if isinstance(their, datetime):
3485 return self.todatetime() == their
3486 if not isinstance(their, self.__class__):
3489 self._value == their._value and
3490 self.tag == their.tag and
3491 self._expl == their._expl
3494 def todatetime(self):
3495 """Convert to datetime
3499 Pay attention that UTCTime can not hold full year, so all years
3500 having < 50 years are treated as 20xx, 19xx otherwise, according
3501 to X.509 recomendation.
3503 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3504 year = value.year % 100
3506 year=(2000 + year) if year < 50 else (1900 + year),
3510 minute=value.minute,
3511 second=value.second,
3515 return pp_console_row(next(self.pps()))
3517 def pps(self, decode_path=()):
3519 asn1_type_name=self.asn1_type_name,
3520 obj_name=self.__class__.__name__,
3521 decode_path=decode_path,
3522 value=self.todatetime().isoformat() if self.ready else None,
3523 optional=self.optional,
3524 default=self == self.default,
3525 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3526 expl=None if self._expl is None else tag_decode(self._expl),
3531 expl_offset=self.expl_offset if self.expled else None,
3532 expl_tlen=self.expl_tlen if self.expled else None,
3533 expl_llen=self.expl_llen if self.expled else None,
3534 expl_vlen=self.expl_vlen if self.expled else None,
3535 expl_lenindef=self.expl_lenindef,
3539 class GeneralizedTime(UTCTime):
3540 """``GeneralizedTime`` datetime type
3542 This type is similar to :py:class:`pyderasn.UTCTime`.
3544 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3545 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3547 '20170930220750.000123Z'
3548 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3549 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3552 tag_default = tag_encode(24)
3553 asn1_type_name = "GeneralizedTime"
3555 fmt = "%Y%m%d%H%M%SZ"
3556 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3558 def _value_sanitize(self, value):
3559 if isinstance(value, self.__class__):
3561 if isinstance(value, datetime):
3562 return value.strftime(
3563 self.fmt_ms if value.microsecond > 0 else self.fmt
3565 if isinstance(value, binary_type):
3566 value_decoded = value.decode("ascii")
3567 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3569 datetime.strptime(value_decoded, self.fmt)
3572 "invalid GeneralizedTime (without ms) format",
3575 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3577 datetime.strptime(value_decoded, self.fmt_ms)
3580 "invalid GeneralizedTime (with ms) format",
3585 "invalid GeneralizedTime length",
3586 klass=self.__class__,
3588 raise InvalidValueType((self.__class__, datetime))
3590 def todatetime(self):
3591 value = self._value.decode("ascii")
3592 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3593 return datetime.strptime(value, self.fmt)
3594 return datetime.strptime(value, self.fmt_ms)
3597 class GraphicString(CommonString):
3599 tag_default = tag_encode(25)
3600 encoding = "iso-8859-1"
3601 asn1_type_name = "GraphicString"
3604 class VisibleString(CommonString):
3606 tag_default = tag_encode(26)
3608 asn1_type_name = "VisibleString"
3611 class ISO646String(VisibleString):
3613 asn1_type_name = "ISO646String"
3616 class GeneralString(CommonString):
3618 tag_default = tag_encode(27)
3619 encoding = "iso-8859-1"
3620 asn1_type_name = "GeneralString"
3623 class UniversalString(CommonString):
3625 tag_default = tag_encode(28)
3626 encoding = "utf-32-be"
3627 asn1_type_name = "UniversalString"
3630 class BMPString(CommonString):
3632 tag_default = tag_encode(30)
3633 encoding = "utf-16-be"
3634 asn1_type_name = "BMPString"
3638 """``CHOICE`` special type
3642 class GeneralName(Choice):
3644 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3645 ("dNSName", IA5String(impl=tag_ctxp(2))),
3648 >>> gn = GeneralName()
3650 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3651 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3652 >>> gn["dNSName"] = IA5String("bar.baz")
3653 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3654 >>> gn["rfc822Name"]
3657 [2] IA5String IA5 bar.baz
3660 >>> gn.value == gn["dNSName"]
3663 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3665 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3666 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3668 __slots__ = ("specs",)
3670 asn1_type_name = "CHOICE"
3683 :param value: set the value. Either ``(choice, value)`` tuple, or
3684 :py:class:`pyderasn.Choice` object
3685 :param bytes impl: can not be set, do **not** use it
3686 :param bytes expl: override default tag with ``EXPLICIT`` one
3687 :param default: set default value. Type same as in ``value``
3688 :param bool optional: is object ``OPTIONAL`` in sequence
3690 if impl is not None:
3691 raise ValueError("no implicit tag allowed for CHOICE")
3692 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3694 schema = getattr(self, "schema", ())
3695 if len(schema) == 0:
3696 raise ValueError("schema must be specified")
3698 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3701 if value is not None:
3702 self._value = self._value_sanitize(value)
3703 if default is not None:
3704 default_value = self._value_sanitize(default)
3705 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3706 default_obj.specs = self.specs
3707 default_obj._value = default_value
3708 self.default = default_obj
3710 self._value = default_obj.copy()._value
3712 def _value_sanitize(self, value):
3713 if isinstance(value, self.__class__):
3715 if isinstance(value, tuple) and len(value) == 2:
3717 spec = self.specs.get(choice)
3719 raise ObjUnknown(choice)
3720 if not isinstance(obj, spec.__class__):
3721 raise InvalidValueType((spec,))
3722 return (choice, spec(obj))
3723 raise InvalidValueType((self.__class__, tuple))
3727 return self._value is not None and self._value[1].ready
3730 obj = self.__class__(schema=self.specs)
3731 obj._expl = self._expl
3732 obj.default = self.default
3733 obj.optional = self.optional
3734 obj.offset = self.offset
3735 obj.llen = self.llen
3736 obj.vlen = self.vlen
3738 if value is not None:
3739 obj._value = (value[0], value[1].copy())
3742 def __eq__(self, their):
3743 if isinstance(their, tuple) and len(their) == 2:
3744 return self._value == their
3745 if not isinstance(their, self.__class__):
3748 self.specs == their.specs and
3749 self._value == their._value
3759 return self.__class__(
3762 expl=self._expl if expl is None else expl,
3763 default=self.default if default is None else default,
3764 optional=self.optional if optional is None else optional,
3769 self._assert_ready()
3770 return self._value[0]
3774 self._assert_ready()
3775 return self._value[1]
3777 def __getitem__(self, key):
3778 if key not in self.specs:
3779 raise ObjUnknown(key)
3780 if self._value is None:
3782 choice, value = self._value
3787 def __setitem__(self, key, value):
3788 spec = self.specs.get(key)
3790 raise ObjUnknown(key)
3791 if not isinstance(value, spec.__class__):
3792 raise InvalidValueType((spec.__class__,))
3793 self._value = (key, spec(value))
3801 return self._value[1].decoded if self.ready else False
3804 self._assert_ready()
3805 return self._value[1].encode()
3807 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3808 for choice, spec in self.specs.items():
3809 sub_decode_path = decode_path + (choice,)
3815 decode_path=sub_decode_path,
3824 klass=self.__class__,
3825 decode_path=decode_path,
3830 value, tail = spec.decode(
3834 decode_path=sub_decode_path,
3837 obj = self.__class__(
3840 default=self.default,
3841 optional=self.optional,
3842 _decoded=(offset, 0, value.tlvlen),
3844 obj._value = (choice, value)
3848 value = pp_console_row(next(self.pps()))
3850 value = "%s[%r]" % (value, self.value)
3853 def pps(self, decode_path=()):
3855 asn1_type_name=self.asn1_type_name,
3856 obj_name=self.__class__.__name__,
3857 decode_path=decode_path,
3858 value=self.choice if self.ready else None,
3859 optional=self.optional,
3860 default=self == self.default,
3861 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3862 expl=None if self._expl is None else tag_decode(self._expl),
3867 expl_lenindef=self.expl_lenindef,
3870 yield self.value.pps(decode_path=decode_path + (self.choice,))
3873 class PrimitiveTypes(Choice):
3874 """Predefined ``CHOICE`` for all generic primitive types
3876 It could be useful for general decoding of some unspecified values:
3878 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3879 OCTET STRING 3 bytes 666f6f
3880 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3884 schema = tuple((klass.__name__, klass()) for klass in (
3909 """``ANY`` special type
3911 >>> Any(Integer(-123))
3913 >>> a = Any(OctetString(b"hello world").encode())
3914 ANY 040b68656c6c6f20776f726c64
3915 >>> hexenc(bytes(a))
3916 b'0x040x0bhello world'
3918 __slots__ = ("defined",)
3919 tag_default = tag_encode(0)
3920 asn1_type_name = "ANY"
3930 :param value: set the value. Either any kind of pyderasn's
3931 **ready** object, or bytes. Pay attention that
3932 **no** validation is performed is raw binary value
3934 :param bytes expl: override default tag with ``EXPLICIT`` one
3935 :param bool optional: is object ``OPTIONAL`` in sequence
3937 super(Any, self).__init__(None, expl, None, optional, _decoded)
3938 self._value = None if value is None else self._value_sanitize(value)
3941 def _value_sanitize(self, value):
3942 if isinstance(value, self.__class__):
3944 if isinstance(value, Obj):
3945 return value.encode()
3946 if isinstance(value, binary_type):
3948 raise InvalidValueType((self.__class__, Obj, binary_type))
3952 return self._value is not None
3955 obj = self.__class__()
3956 obj._value = self._value
3958 obj._expl = self._expl
3959 obj.optional = self.optional
3960 obj.offset = self.offset
3961 obj.llen = self.llen
3962 obj.vlen = self.vlen
3965 def __eq__(self, their):
3966 if isinstance(their, binary_type):
3967 return self._value == their
3968 if issubclass(their.__class__, Any):
3969 return self._value == their._value
3978 return self.__class__(
3980 expl=self._expl if expl is None else expl,
3981 optional=self.optional if optional is None else optional,
3984 def __bytes__(self):
3985 self._assert_ready()
3993 self._assert_ready()
3996 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3998 t, tlen, lv = tag_strip(tlv)
3999 except DecodeError as err:
4000 raise err.__class__(
4002 klass=self.__class__,
4003 decode_path=decode_path,
4007 l, llen, v = len_decode(lv)
4008 except LenIndefForm as err:
4009 if not ctx.get("bered", False):
4010 raise err.__class__(
4012 klass=self.__class__,
4013 decode_path=decode_path,
4016 llen, vlen, v = 1, 0, lv[1:]
4017 sub_offset = offset + tlen + llen
4019 while v[:EOC_LEN].tobytes() != EOC:
4020 chunk, v = Any().decode(
4023 decode_path=decode_path + (str(chunk_i),),
4027 vlen += chunk.tlvlen
4028 sub_offset += chunk.tlvlen
4030 tlvlen = tlen + llen + vlen + EOC_LEN
4031 obj = self.__class__(
4032 value=tlv[:tlvlen].tobytes(),
4034 optional=self.optional,
4035 _decoded=(offset, 0, tlvlen),
4039 return obj, v[EOC_LEN:]
4040 except DecodeError as err:
4041 raise err.__class__(
4043 klass=self.__class__,
4044 decode_path=decode_path,
4048 raise NotEnoughData(
4049 "encoded length is longer than data",
4050 klass=self.__class__,
4051 decode_path=decode_path,
4054 tlvlen = tlen + llen + l
4055 v, tail = tlv[:tlvlen], v[l:]
4056 obj = self.__class__(
4059 optional=self.optional,
4060 _decoded=(offset, 0, tlvlen),
4066 return pp_console_row(next(self.pps()))
4068 def pps(self, decode_path=()):
4070 asn1_type_name=self.asn1_type_name,
4071 obj_name=self.__class__.__name__,
4072 decode_path=decode_path,
4073 blob=self._value if self.ready else None,
4074 optional=self.optional,
4075 default=self == self.default,
4076 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4077 expl=None if self._expl is None else tag_decode(self._expl),
4082 expl_offset=self.expl_offset if self.expled else None,
4083 expl_tlen=self.expl_tlen if self.expled else None,
4084 expl_llen=self.expl_llen if self.expled else None,
4085 expl_vlen=self.expl_vlen if self.expled else None,
4086 expl_lenindef=self.expl_lenindef,
4087 lenindef=self.lenindef,
4089 defined_by, defined = self.defined or (None, None)
4090 if defined_by is not None:
4092 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4096 ########################################################################
4097 # ASN.1 constructed types
4098 ########################################################################
4100 def get_def_by_path(defines_by_path, sub_decode_path):
4101 """Get define by decode path
4103 for path, define in defines_by_path:
4104 if len(path) != len(sub_decode_path):
4106 for p1, p2 in zip(path, sub_decode_path):
4107 if (p1 != any) and (p1 != p2):
4113 def abs_decode_path(decode_path, rel_path):
4114 """Create an absolute decode path from current and relative ones
4116 :param decode_path: current decode path, starting point.
4118 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4119 If first tuple's element is "/", then treat it as
4120 an absolute path, ignoring ``decode_path`` as
4121 starting point. Also this tuple can contain ".."
4122 elements, stripping the leading element from
4125 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4126 ("foo", "bar", "baz", "whatever")
4127 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4129 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4132 if rel_path[0] == "/":
4134 if rel_path[0] == "..":
4135 return abs_decode_path(decode_path[:-1], rel_path[1:])
4136 return decode_path + rel_path
4139 class Sequence(Obj):
4140 """``SEQUENCE`` structure type
4142 You have to make specification of sequence::
4144 class Extension(Sequence):
4146 ("extnID", ObjectIdentifier()),
4147 ("critical", Boolean(default=False)),
4148 ("extnValue", OctetString()),
4151 Then, you can work with it as with dictionary.
4153 >>> ext = Extension()
4154 >>> Extension().specs
4156 ('extnID', OBJECT IDENTIFIER),
4157 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4158 ('extnValue', OCTET STRING),
4160 >>> ext["extnID"] = "1.2.3"
4161 Traceback (most recent call last):
4162 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4163 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4165 You can determine if sequence is ready to be encoded:
4170 Traceback (most recent call last):
4171 pyderasn.ObjNotReady: object is not ready: extnValue
4172 >>> ext["extnValue"] = OctetString(b"foobar")
4176 Value you want to assign, must have the same **type** as in
4177 corresponding specification, but it can have different tags,
4178 optional/default attributes -- they will be taken from specification
4181 class TBSCertificate(Sequence):
4183 ("version", Version(expl=tag_ctxc(0), default="v1")),
4186 >>> tbs = TBSCertificate()
4187 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4189 Assign ``None`` to remove value from sequence.
4191 You can set values in Sequence during its initialization:
4193 >>> AlgorithmIdentifier((
4194 ("algorithm", ObjectIdentifier("1.2.3")),
4195 ("parameters", Any(Null()))
4197 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4199 You can determine if value exists/set in the sequence and take its value:
4201 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4204 OBJECT IDENTIFIER 1.2.3
4206 But pay attention that if value has default, then it won't be (not
4207 in) in the sequence (because ``DEFAULT`` must not be encoded in
4208 DER), but you can read its value:
4210 >>> "critical" in ext, ext["critical"]
4211 (False, BOOLEAN False)
4212 >>> ext["critical"] = Boolean(True)
4213 >>> "critical" in ext, ext["critical"]
4214 (True, BOOLEAN True)
4216 All defaulted values are always optional.
4218 .. _strict_default_existence_ctx:
4222 When decoded DER contains defaulted value inside, then
4223 technically this is not valid DER encoding. But we allow and pass
4224 it **by default**. Of course reencoding of that kind of DER will
4225 result in different binary representation (validly without
4226 defaulted value inside). You can enable strict defaulted values
4227 existence validation by setting ``"strict_default_existence":
4228 True`` :ref:`context <ctx>` option -- decoding process will raise
4229 an exception if defaulted value is met.
4231 Two sequences are equal if they have equal specification (schema),
4232 implicit/explicit tagging and the same values.
4234 __slots__ = ("specs",)
4235 tag_default = tag_encode(form=TagFormConstructed, num=16)
4236 asn1_type_name = "SEQUENCE"
4248 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4250 schema = getattr(self, "schema", ())
4252 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4255 if value is not None:
4256 if issubclass(value.__class__, Sequence):
4257 self._value = value._value
4258 elif hasattr(value, "__iter__"):
4259 for seq_key, seq_value in value:
4260 self[seq_key] = seq_value
4262 raise InvalidValueType((Sequence,))
4263 if default is not None:
4264 if not issubclass(default.__class__, Sequence):
4265 raise InvalidValueType((Sequence,))
4266 default_value = default._value
4267 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4268 default_obj.specs = self.specs
4269 default_obj._value = default_value
4270 self.default = default_obj
4272 self._value = default_obj.copy()._value
4276 for name, spec in self.specs.items():
4277 value = self._value.get(name)
4288 obj = self.__class__(schema=self.specs)
4290 obj._expl = self._expl
4291 obj.default = self.default
4292 obj.optional = self.optional
4293 obj.offset = self.offset
4294 obj.llen = self.llen
4295 obj.vlen = self.vlen
4296 obj._value = {k: v.copy() for k, v in self._value.items()}
4299 def __eq__(self, their):
4300 if not isinstance(their, self.__class__):
4303 self.specs == their.specs and
4304 self.tag == their.tag and
4305 self._expl == their._expl and
4306 self._value == their._value
4317 return self.__class__(
4320 impl=self.tag if impl is None else impl,
4321 expl=self._expl if expl is None else expl,
4322 default=self.default if default is None else default,
4323 optional=self.optional if optional is None else optional,
4326 def __contains__(self, key):
4327 return key in self._value
4329 def __setitem__(self, key, value):
4330 spec = self.specs.get(key)
4332 raise ObjUnknown(key)
4334 self._value.pop(key, None)
4336 if not isinstance(value, spec.__class__):
4337 raise InvalidValueType((spec.__class__,))
4338 value = spec(value=value)
4339 if spec.default is not None and value == spec.default:
4340 self._value.pop(key, None)
4342 self._value[key] = value
4344 def __getitem__(self, key):
4345 value = self._value.get(key)
4346 if value is not None:
4348 spec = self.specs.get(key)
4350 raise ObjUnknown(key)
4351 if spec.default is not None:
4355 def _encoded_values(self):
4357 for name, spec in self.specs.items():
4358 value = self._value.get(name)
4362 raise ObjNotReady(name)
4363 raws.append(value.encode())
4367 v = b"".join(self._encoded_values())
4368 return b"".join((self.tag, len_encode(len(v)), v))
4370 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4372 t, tlen, lv = tag_strip(tlv)
4373 except DecodeError as err:
4374 raise err.__class__(
4376 klass=self.__class__,
4377 decode_path=decode_path,
4382 klass=self.__class__,
4383 decode_path=decode_path,
4390 l, llen, v = len_decode(lv)
4391 except LenIndefForm as err:
4392 if not ctx.get("bered", False):
4393 raise err.__class__(
4395 klass=self.__class__,
4396 decode_path=decode_path,
4399 l, llen, v = 0, 1, lv[1:]
4401 except DecodeError as err:
4402 raise err.__class__(
4404 klass=self.__class__,
4405 decode_path=decode_path,
4409 raise NotEnoughData(
4410 "encoded length is longer than data",
4411 klass=self.__class__,
4412 decode_path=decode_path,
4416 v, tail = v[:l], v[l:]
4418 sub_offset = offset + tlen + llen
4420 for name, spec in self.specs.items():
4421 if spec.optional and (
4422 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4426 sub_decode_path = decode_path + (name,)
4428 value, v_tail = spec.decode(
4432 decode_path=sub_decode_path,
4440 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4441 if defined is not None:
4442 defined_by, defined_spec = defined
4443 if issubclass(value.__class__, SequenceOf):
4444 for i, _value in enumerate(value):
4445 sub_sub_decode_path = sub_decode_path + (
4447 DecodePathDefBy(defined_by),
4449 defined_value, defined_tail = defined_spec.decode(
4450 memoryview(bytes(_value)),
4452 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4453 if value.expled else (value.tlen + value.llen)
4456 decode_path=sub_sub_decode_path,
4459 if len(defined_tail) > 0:
4462 klass=self.__class__,
4463 decode_path=sub_sub_decode_path,
4466 _value.defined = (defined_by, defined_value)
4468 defined_value, defined_tail = defined_spec.decode(
4469 memoryview(bytes(value)),
4471 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4472 if value.expled else (value.tlen + value.llen)
4475 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4478 if len(defined_tail) > 0:
4481 klass=self.__class__,
4482 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4485 value.defined = (defined_by, defined_value)
4487 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4489 sub_offset += value_len
4491 if spec.default is not None and value == spec.default:
4492 if ctx.get("strict_default_existence", False):
4494 "DEFAULT value met",
4495 klass=self.__class__,
4496 decode_path=sub_decode_path,
4501 values[name] = value
4503 spec_defines = getattr(spec, "defines", ())
4504 if len(spec_defines) == 0:
4505 defines_by_path = ctx.get("defines_by_path", ())
4506 if len(defines_by_path) > 0:
4507 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4508 if spec_defines is not None and len(spec_defines) > 0:
4509 for rel_path, schema in spec_defines:
4510 defined = schema.get(value, None)
4511 if defined is not None:
4512 ctx.setdefault("defines", []).append((
4513 abs_decode_path(sub_decode_path[:-1], rel_path),
4517 if v[:EOC_LEN].tobytes() != EOC:
4520 klass=self.__class__,
4521 decode_path=decode_path,
4529 klass=self.__class__,
4530 decode_path=decode_path,
4533 obj = self.__class__(
4537 default=self.default,
4538 optional=self.optional,
4539 _decoded=(offset, llen, vlen),
4542 obj.lenindef = lenindef
4546 value = pp_console_row(next(self.pps()))
4548 for name in self.specs:
4549 _value = self._value.get(name)
4552 cols.append(repr(_value))
4553 return "%s[%s]" % (value, ", ".join(cols))
4555 def pps(self, decode_path=()):
4557 asn1_type_name=self.asn1_type_name,
4558 obj_name=self.__class__.__name__,
4559 decode_path=decode_path,
4560 optional=self.optional,
4561 default=self == self.default,
4562 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4563 expl=None if self._expl is None else tag_decode(self._expl),
4568 expl_offset=self.expl_offset if self.expled else None,
4569 expl_tlen=self.expl_tlen if self.expled else None,
4570 expl_llen=self.expl_llen if self.expled else None,
4571 expl_vlen=self.expl_vlen if self.expled else None,
4572 expl_lenindef=self.expl_lenindef,
4573 lenindef=self.lenindef,
4575 for name in self.specs:
4576 value = self._value.get(name)
4579 yield value.pps(decode_path=decode_path + (name,))
4582 class Set(Sequence):
4583 """``SET`` structure type
4585 Its usage is identical to :py:class:`pyderasn.Sequence`.
4588 tag_default = tag_encode(form=TagFormConstructed, num=17)
4589 asn1_type_name = "SET"
4592 raws = self._encoded_values()
4595 return b"".join((self.tag, len_encode(len(v)), v))
4597 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4599 t, tlen, lv = tag_strip(tlv)
4600 except DecodeError as err:
4601 raise err.__class__(
4603 klass=self.__class__,
4604 decode_path=decode_path,
4609 klass=self.__class__,
4610 decode_path=decode_path,
4617 l, llen, v = len_decode(lv)
4618 except LenIndefForm as err:
4619 if not ctx.get("bered", False):
4620 raise err.__class__(
4622 klass=self.__class__,
4623 decode_path=decode_path,
4626 l, llen, v = 0, 1, lv[1:]
4628 except DecodeError as err:
4629 raise err.__class__(
4631 klass=self.__class__,
4632 decode_path=decode_path,
4636 raise NotEnoughData(
4637 "encoded length is longer than data",
4638 klass=self.__class__,
4642 v, tail = v[:l], v[l:]
4644 sub_offset = offset + tlen + llen
4646 specs_items = self.specs.items
4648 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4650 for name, spec in specs_items():
4651 sub_decode_path = decode_path + (name,)
4657 decode_path=sub_decode_path,
4666 klass=self.__class__,
4667 decode_path=decode_path,
4670 value, v_tail = spec.decode(
4674 decode_path=sub_decode_path,
4677 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4678 sub_offset += value_len
4681 if spec.default is None or value != spec.default: # pragma: no cover
4682 # SeqMixing.test_encoded_default_accepted covers that place
4683 values[name] = value
4684 obj = self.__class__(
4688 default=self.default,
4689 optional=self.optional,
4690 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4694 if v[:EOC_LEN].tobytes() != EOC:
4697 klass=self.__class__,
4698 decode_path=decode_path,
4705 "not all values are ready",
4706 klass=self.__class__,
4707 decode_path=decode_path,
4713 class SequenceOf(Obj):
4714 """``SEQUENCE OF`` sequence type
4716 For that kind of type you must specify the object it will carry on
4717 (bounds are for example here, not required)::
4719 class Ints(SequenceOf):
4724 >>> ints.append(Integer(123))
4725 >>> ints.append(Integer(234))
4727 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4728 >>> [int(i) for i in ints]
4730 >>> ints.append(Integer(345))
4731 Traceback (most recent call last):
4732 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4735 >>> ints[1] = Integer(345)
4737 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4739 Also you can initialize sequence with preinitialized values:
4741 >>> ints = Ints([Integer(123), Integer(234)])
4743 __slots__ = ("spec", "_bound_min", "_bound_max")
4744 tag_default = tag_encode(form=TagFormConstructed, num=16)
4745 asn1_type_name = "SEQUENCE OF"
4758 super(SequenceOf, self).__init__(
4766 schema = getattr(self, "schema", None)
4768 raise ValueError("schema must be specified")
4770 self._bound_min, self._bound_max = getattr(
4774 ) if bounds is None else bounds
4776 if value is not None:
4777 self._value = self._value_sanitize(value)
4778 if default is not None:
4779 default_value = self._value_sanitize(default)
4780 default_obj = self.__class__(
4785 default_obj._value = default_value
4786 self.default = default_obj
4788 self._value = default_obj.copy()._value
4790 def _value_sanitize(self, value):
4791 if issubclass(value.__class__, SequenceOf):
4792 value = value._value
4793 elif hasattr(value, "__iter__"):
4796 raise InvalidValueType((self.__class__, iter))
4797 if not self._bound_min <= len(value) <= self._bound_max:
4798 raise BoundsError(self._bound_min, len(value), self._bound_max)
4800 if not isinstance(v, self.spec.__class__):
4801 raise InvalidValueType((self.spec.__class__,))
4806 return all(v.ready for v in self._value)
4809 obj = self.__class__(schema=self.spec)
4810 obj._bound_min = self._bound_min
4811 obj._bound_max = self._bound_max
4813 obj._expl = self._expl
4814 obj.default = self.default
4815 obj.optional = self.optional
4816 obj.offset = self.offset
4817 obj.llen = self.llen
4818 obj.vlen = self.vlen
4819 obj._value = [v.copy() for v in self._value]
4822 def __eq__(self, their):
4823 if isinstance(their, self.__class__):
4825 self.spec == their.spec and
4826 self.tag == their.tag and
4827 self._expl == their._expl and
4828 self._value == their._value
4830 if hasattr(their, "__iter__"):
4831 return self._value == list(their)
4843 return self.__class__(
4847 (self._bound_min, self._bound_max)
4848 if bounds is None else bounds
4850 impl=self.tag if impl is None else impl,
4851 expl=self._expl if expl is None else expl,
4852 default=self.default if default is None else default,
4853 optional=self.optional if optional is None else optional,
4856 def __contains__(self, key):
4857 return key in self._value
4859 def append(self, value):
4860 if not isinstance(value, self.spec.__class__):
4861 raise InvalidValueType((self.spec.__class__,))
4862 if len(self._value) + 1 > self._bound_max:
4865 len(self._value) + 1,
4868 self._value.append(value)
4871 self._assert_ready()
4872 return iter(self._value)
4875 self._assert_ready()
4876 return len(self._value)
4878 def __setitem__(self, key, value):
4879 if not isinstance(value, self.spec.__class__):
4880 raise InvalidValueType((self.spec.__class__,))
4881 self._value[key] = self.spec(value=value)
4883 def __getitem__(self, key):
4884 return self._value[key]
4886 def _encoded_values(self):
4887 return [v.encode() for v in self._value]
4890 v = b"".join(self._encoded_values())
4891 return b"".join((self.tag, len_encode(len(v)), v))
4893 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4895 t, tlen, lv = tag_strip(tlv)
4896 except DecodeError as err:
4897 raise err.__class__(
4899 klass=self.__class__,
4900 decode_path=decode_path,
4905 klass=self.__class__,
4906 decode_path=decode_path,
4913 l, llen, v = len_decode(lv)
4914 except LenIndefForm as err:
4915 if not ctx.get("bered", False):
4916 raise err.__class__(
4918 klass=self.__class__,
4919 decode_path=decode_path,
4922 l, llen, v = 0, 1, lv[1:]
4924 except DecodeError as err:
4925 raise err.__class__(
4927 klass=self.__class__,
4928 decode_path=decode_path,
4932 raise NotEnoughData(
4933 "encoded length is longer than data",
4934 klass=self.__class__,
4935 decode_path=decode_path,
4939 v, tail = v[:l], v[l:]
4941 sub_offset = offset + tlen + llen
4945 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4947 value, v_tail = spec.decode(
4951 decode_path=decode_path + (str(len(_value)),),
4954 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4955 sub_offset += value_len
4958 _value.append(value)
4959 obj = self.__class__(
4962 bounds=(self._bound_min, self._bound_max),
4965 default=self.default,
4966 optional=self.optional,
4967 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4970 if v[:EOC_LEN].tobytes() != EOC:
4973 klass=self.__class__,
4974 decode_path=decode_path,
4983 pp_console_row(next(self.pps())),
4984 ", ".join(repr(v) for v in self._value),
4987 def pps(self, decode_path=()):
4989 asn1_type_name=self.asn1_type_name,
4990 obj_name=self.__class__.__name__,
4991 decode_path=decode_path,
4992 optional=self.optional,
4993 default=self == self.default,
4994 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4995 expl=None if self._expl is None else tag_decode(self._expl),
5000 expl_offset=self.expl_offset if self.expled else None,
5001 expl_tlen=self.expl_tlen if self.expled else None,
5002 expl_llen=self.expl_llen if self.expled else None,
5003 expl_vlen=self.expl_vlen if self.expled else None,
5004 expl_lenindef=self.expl_lenindef,
5005 lenindef=self.lenindef,
5007 for i, value in enumerate(self._value):
5008 yield value.pps(decode_path=decode_path + (str(i),))
5011 class SetOf(SequenceOf):
5012 """``SET OF`` sequence type
5014 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5017 tag_default = tag_encode(form=TagFormConstructed, num=17)
5018 asn1_type_name = "SET OF"
5021 raws = self._encoded_values()
5024 return b"".join((self.tag, len_encode(len(v)), v))
5027 def obj_by_path(pypath): # pragma: no cover
5028 """Import object specified as string Python path
5030 Modules must be separated from classes/functions with ``:``.
5032 >>> obj_by_path("foo.bar:Baz")
5033 <class 'foo.bar.Baz'>
5034 >>> obj_by_path("foo.bar:Baz.boo")
5035 <classmethod 'foo.bar.Baz.boo'>
5037 mod, objs = pypath.rsplit(":", 1)
5038 from importlib import import_module
5039 obj = import_module(mod)
5040 for obj_name in objs.split("."):
5041 obj = getattr(obj, obj_name)
5045 def generic_decoder(): # pragma: no cover
5046 # All of this below is a big hack with self references
5047 choice = PrimitiveTypes()
5048 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5049 choice.specs["SetOf"] = SetOf(schema=choice)
5051 choice.specs["SequenceOf%d" % i] = SequenceOf(
5055 choice.specs["Any"] = Any()
5057 # Class name equals to type name, to omit it from output
5058 class SEQUENCEOF(SequenceOf):
5062 def pprint_any(obj, oids=None, with_colours=False):
5063 def _pprint_pps(pps):
5065 if hasattr(pp, "_fields"):
5066 if pp.asn1_type_name == Choice.asn1_type_name:
5068 pp_kwargs = pp._asdict()
5069 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5070 pp = _pp(**pp_kwargs)
5071 yield pp_console_row(
5076 with_colours=with_colours,
5078 for row in pp_console_blob(pp):
5081 for row in _pprint_pps(pp):
5083 return "\n".join(_pprint_pps(obj.pps()))
5084 return SEQUENCEOF(), pprint_any
5087 def main(): # pragma: no cover
5089 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5090 parser.add_argument(
5094 help="Skip that number of bytes from the beginning",
5096 parser.add_argument(
5098 help="Python path to dictionary with OIDs",
5100 parser.add_argument(
5102 help="Python path to schema definition to use",
5104 parser.add_argument(
5105 "--defines-by-path",
5106 help="Python path to decoder's defines_by_path",
5108 parser.add_argument(
5110 action='store_true',
5111 help="Disallow BER encoding",
5113 parser.add_argument(
5115 type=argparse.FileType("rb"),
5116 help="Path to DER file you want to decode",
5118 args = parser.parse_args()
5119 args.DERFile.seek(args.skip)
5120 der = memoryview(args.DERFile.read())
5121 args.DERFile.close()
5122 oids = obj_by_path(args.oids) if args.oids else {}
5124 schema = obj_by_path(args.schema)
5125 from functools import partial
5126 pprinter = partial(pprint, big_blobs=True)
5128 schema, pprinter = generic_decoder()
5129 ctx = {"bered": not args.nobered}
5130 if args.defines_by_path is not None:
5131 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5132 obj, tail = schema().decode(der, ctx=ctx)
5136 with_colours=True if environ.get("NO_COLOR") is None else False,
5139 print("\nTrailing data: %s" % hexenc(tail))
5142 if __name__ == "__main__":