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.
374 Currently BER support is not extensively tested.
376 By default PyDERASN accepts only DER encoded data. It always encodes to
377 DER. But you can optionally enable BER decoding with setting ``bered``
378 :ref:`context <ctx>` argument to True. Indefinite lengths and
379 constructed primitive types should be parsed successfully.
381 * If object is encoded in BER form (not the DER one), then ``bered``
382 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
383 STRING`` can contain it.
384 * If object has an indefinite length encoding, then its ``lenindef``
385 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
386 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
388 * If object has an indefinite length encoded explicit tag, then
389 ``expl_lenindef`` is set to True.
391 EOC (end-of-contents) token's length is taken in advance in object's
399 .. autoclass:: pyderasn.Boolean
404 .. autoclass:: pyderasn.Integer
409 .. autoclass:: pyderasn.BitString
414 .. autoclass:: pyderasn.OctetString
419 .. autoclass:: pyderasn.Null
424 .. autoclass:: pyderasn.ObjectIdentifier
429 .. autoclass:: pyderasn.Enumerated
433 .. autoclass:: pyderasn.CommonString
437 .. autoclass:: pyderasn.NumericString
441 .. autoclass:: pyderasn.UTCTime
442 :members: __init__, todatetime
446 .. autoclass:: pyderasn.GeneralizedTime
453 .. autoclass:: pyderasn.Choice
458 .. autoclass:: PrimitiveTypes
462 .. autoclass:: pyderasn.Any
470 .. autoclass:: pyderasn.Sequence
475 .. autoclass:: pyderasn.Set
480 .. autoclass:: pyderasn.SequenceOf
485 .. autoclass:: pyderasn.SetOf
491 .. autofunction:: pyderasn.abs_decode_path
492 .. autofunction:: pyderasn.hexenc
493 .. autofunction:: pyderasn.hexdec
494 .. autofunction:: pyderasn.tag_encode
495 .. autofunction:: pyderasn.tag_decode
496 .. autofunction:: pyderasn.tag_ctxp
497 .. autofunction:: pyderasn.tag_ctxc
498 .. autoclass:: pyderasn.Obj
501 from codecs import getdecoder
502 from codecs import getencoder
503 from collections import namedtuple
504 from collections import OrderedDict
505 from datetime import datetime
506 from math import ceil
507 from os import environ
508 from string import digits
510 from six import add_metaclass
511 from six import binary_type
512 from six import byte2int
513 from six import indexbytes
514 from six import int2byte
515 from six import integer_types
516 from six import iterbytes
518 from six import string_types
519 from six import text_type
520 from six.moves import xrange as six_xrange
524 from termcolor import colored
526 def colored(what, *args):
569 "TagClassApplication",
573 "TagFormConstructed",
584 TagClassUniversal = 0
585 TagClassApplication = 1 << 6
586 TagClassContext = 1 << 7
587 TagClassPrivate = 1 << 6 | 1 << 7
589 TagFormConstructed = 1 << 5
592 TagClassApplication: "APPLICATION ",
593 TagClassPrivate: "PRIVATE ",
594 TagClassUniversal: "UNIV ",
600 ########################################################################
602 ########################################################################
604 class DecodeError(Exception):
605 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
607 :param str msg: reason of decode failing
608 :param klass: optional exact DecodeError inherited class (like
609 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
610 :py:exc:`InvalidLength`)
611 :param decode_path: tuple of strings. It contains human
612 readable names of the fields through which
613 decoding process has passed
614 :param int offset: binary offset where failure happened
616 super(DecodeError, self).__init__()
619 self.decode_path = decode_path
625 "" if self.klass is None else self.klass.__name__,
627 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
628 if len(self.decode_path) > 0 else ""
630 ("(at %d)" % self.offset) if self.offset > 0 else "",
636 return "%s(%s)" % (self.__class__.__name__, self)
639 class NotEnoughData(DecodeError):
643 class LenIndefForm(DecodeError):
647 class TagMismatch(DecodeError):
651 class InvalidLength(DecodeError):
655 class InvalidOID(DecodeError):
659 class ObjUnknown(ValueError):
660 def __init__(self, name):
661 super(ObjUnknown, self).__init__()
665 return "object is unknown: %s" % self.name
668 return "%s(%s)" % (self.__class__.__name__, self)
671 class ObjNotReady(ValueError):
672 def __init__(self, name):
673 super(ObjNotReady, self).__init__()
677 return "object is not ready: %s" % self.name
680 return "%s(%s)" % (self.__class__.__name__, self)
683 class InvalidValueType(ValueError):
684 def __init__(self, expected_types):
685 super(InvalidValueType, self).__init__()
686 self.expected_types = expected_types
689 return "invalid value type, expected: %s" % ", ".join(
690 [repr(t) for t in self.expected_types]
694 return "%s(%s)" % (self.__class__.__name__, self)
697 class BoundsError(ValueError):
698 def __init__(self, bound_min, value, bound_max):
699 super(BoundsError, self).__init__()
700 self.bound_min = bound_min
702 self.bound_max = bound_max
705 return "unsatisfied bounds: %s <= %s <= %s" % (
712 return "%s(%s)" % (self.__class__.__name__, self)
715 ########################################################################
717 ########################################################################
719 _hexdecoder = getdecoder("hex")
720 _hexencoder = getencoder("hex")
724 """Binary data to hexadecimal string convert
726 return _hexdecoder(data)[0]
730 """Hexadecimal string to binary data convert
732 return _hexencoder(data)[0].decode("ascii")
735 def int_bytes_len(num, byte_len=8):
738 return int(ceil(float(num.bit_length()) / byte_len))
741 def zero_ended_encode(num):
742 octets = bytearray(int_bytes_len(num, 7))
744 octets[i] = num & 0x7F
748 octets[i] = 0x80 | (num & 0x7F)
754 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
755 """Encode tag to binary form
757 :param int num: tag's number
758 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
759 :py:data:`pyderasn.TagClassContext`,
760 :py:data:`pyderasn.TagClassApplication`,
761 :py:data:`pyderasn.TagClassPrivate`)
762 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
763 :py:data:`pyderasn.TagFormConstructed`)
767 return int2byte(klass | form | num)
768 # [XX|X|11111][1.......][1.......] ... [0.......]
769 return int2byte(klass | form | 31) + zero_ended_encode(num)
773 """Decode tag from binary form
777 No validation is performed, assuming that it has already passed.
779 It returns tuple with three integers, as
780 :py:func:`pyderasn.tag_encode` accepts.
782 first_octet = byte2int(tag)
783 klass = first_octet & 0xC0
784 form = first_octet & 0x20
785 if first_octet & 0x1F < 0x1F:
786 return (klass, form, first_octet & 0x1F)
788 for octet in iterbytes(tag[1:]):
791 return (klass, form, num)
795 """Create CONTEXT PRIMITIVE tag
797 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
801 """Create CONTEXT CONSTRUCTED tag
803 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
807 """Take off tag from the data
809 :returns: (encoded tag, tag length, remaining data)
812 raise NotEnoughData("no data at all")
813 if byte2int(data) & 0x1F < 31:
814 return data[:1], 1, data[1:]
819 raise DecodeError("unfinished tag")
820 if indexbytes(data, i) & 0x80 == 0:
823 return data[:i], i, data[i:]
829 octets = bytearray(int_bytes_len(l) + 1)
830 octets[0] = 0x80 | (len(octets) - 1)
831 for i in six_xrange(len(octets) - 1, 0, -1):
837 def len_decode(data):
840 :returns: (decoded length, length's length, remaining data)
841 :raises LenIndefForm: if indefinite form encoding is met
844 raise NotEnoughData("no data at all")
845 first_octet = byte2int(data)
846 if first_octet & 0x80 == 0:
847 return first_octet, 1, data[1:]
848 octets_num = first_octet & 0x7F
849 if octets_num + 1 > len(data):
850 raise NotEnoughData("encoded length is longer than data")
853 if byte2int(data[1:]) == 0:
854 raise DecodeError("leading zeros")
856 for v in iterbytes(data[1:1 + octets_num]):
859 raise DecodeError("long form instead of short one")
860 return l, 1 + octets_num, data[1 + octets_num:]
863 ########################################################################
865 ########################################################################
867 class AutoAddSlots(type):
868 def __new__(mcs, name, bases, _dict):
869 _dict["__slots__"] = _dict.get("__slots__", ())
870 return type.__new__(mcs, name, bases, _dict)
873 @add_metaclass(AutoAddSlots)
875 """Common ASN.1 object class
877 All ASN.1 types are inherited from it. It has metaclass that
878 automatically adds ``__slots__`` to all inherited classes.
902 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
903 self._expl = getattr(self, "expl", None) if expl is None else expl
904 if self.tag != self.tag_default and self._expl is not None:
906 "implicit and explicit tags can not be set simultaneously"
908 if default is not None:
910 self.optional = optional
911 self.offset, self.llen, self.vlen = _decoded
913 self.expl_lenindef = False
914 self.lenindef = False
918 def ready(self): # pragma: no cover
919 """Is object ready to be encoded?
921 raise NotImplementedError()
923 def _assert_ready(self):
925 raise ObjNotReady(self.__class__.__name__)
929 """Is object decoded?
931 return (self.llen + self.vlen) > 0
933 def copy(self): # pragma: no cover
934 """Make a copy of object, safe to be mutated
936 raise NotImplementedError()
944 return self.tlen + self.llen + self.vlen
946 def __str__(self): # pragma: no cover
947 return self.__bytes__() if PY2 else self.__unicode__()
949 def __ne__(self, their):
950 return not(self == their)
952 def __gt__(self, their): # pragma: no cover
953 return not(self < their)
955 def __le__(self, their): # pragma: no cover
956 return (self == their) or (self < their)
958 def __ge__(self, their): # pragma: no cover
959 return (self == their) or (self > their)
961 def _encode(self): # pragma: no cover
962 raise NotImplementedError()
964 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
965 raise NotImplementedError()
969 if self._expl is None:
971 return b"".join((self._expl, len_encode(len(raw)), raw))
984 :param data: either binary or memoryview
985 :param int offset: initial data's offset
986 :param bool leavemm: do we need to leave memoryview of remaining
987 data as is, or convert it to bytes otherwise
988 :param ctx: optional :ref:`context <ctx>` governing decoding process.
989 :param tag_only: decode only the tag, without length and contents
990 (used only in Choice and Set structures, trying to
991 determine if tag satisfies the scheme)
992 :returns: (Obj, remaining data)
996 tlv = memoryview(data)
997 if self._expl is None:
998 result = self._decode(
1001 decode_path=decode_path,
1010 t, tlen, lv = tag_strip(tlv)
1011 except DecodeError as err:
1012 raise err.__class__(
1014 klass=self.__class__,
1015 decode_path=decode_path,
1020 klass=self.__class__,
1021 decode_path=decode_path,
1025 l, llen, v = len_decode(lv)
1026 except LenIndefForm as err:
1027 if not ctx.get("bered", False):
1028 raise err.__class__(
1030 klass=self.__class__,
1031 decode_path=decode_path,
1035 offset += tlen + llen
1036 result = self._decode(
1039 decode_path=decode_path,
1046 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1047 if eoc_expected.tobytes() != EOC:
1050 decode_path=decode_path,
1054 obj.expl_lenindef = True
1055 except DecodeError as err:
1056 raise err.__class__(
1058 klass=self.__class__,
1059 decode_path=decode_path,
1064 raise NotEnoughData(
1065 "encoded length is longer than data",
1066 klass=self.__class__,
1067 decode_path=decode_path,
1070 result = self._decode(
1072 offset=offset + tlen + llen,
1073 decode_path=decode_path,
1080 return obj, (tail if leavemm else tail.tobytes())
1084 return self._expl is not None
1091 def expl_tlen(self):
1092 return len(self._expl)
1095 def expl_llen(self):
1096 if self.expl_lenindef:
1098 return len(len_encode(self.tlvlen))
1101 def expl_offset(self):
1102 return self.offset - self.expl_tlen - self.expl_llen
1105 def expl_vlen(self):
1109 def expl_tlvlen(self):
1110 return self.expl_tlen + self.expl_llen + self.expl_vlen
1113 class DecodePathDefBy(object):
1114 """DEFINED BY representation inside decode path
1116 __slots__ = ("defined_by",)
1118 def __init__(self, defined_by):
1119 self.defined_by = defined_by
1121 def __ne__(self, their):
1122 return not(self == their)
1124 def __eq__(self, their):
1125 if not isinstance(their, self.__class__):
1127 return self.defined_by == their.defined_by
1130 return "DEFINED BY " + str(self.defined_by)
1133 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1136 ########################################################################
1138 ########################################################################
1140 PP = namedtuple("PP", (
1165 asn1_type_name="unknown",
1182 expl_lenindef=False,
1210 def _colorize(what, colour, with_colours, attrs=("bold",)):
1211 return colored(what, colour, attrs=attrs) if with_colours else what
1226 " " if pp.expl_offset is None else
1227 ("-%d" % (pp.offset - pp.expl_offset))
1230 cols.append(_colorize(col, "red", with_colours, ()))
1231 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1232 col = _colorize(col, "green", with_colours, ())
1234 if pp.expl_lenindef:
1239 " " if ber_deoffset == 0 else
1240 _colorize(("-%d" % ber_deoffset), "red", with_colours)
1243 if len(pp.decode_path) > 0:
1244 cols.append(" ." * (len(pp.decode_path)))
1245 ent = pp.decode_path[-1]
1246 if isinstance(ent, DecodePathDefBy):
1247 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1248 value = str(ent.defined_by)
1250 oids is not None and
1251 ent.defined_by.asn1_type_name ==
1252 ObjectIdentifier.asn1_type_name and
1255 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1257 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1259 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1260 if pp.expl is not None:
1261 klass, _, num = pp.expl
1262 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1263 cols.append(_colorize(col, "blue", with_colours))
1264 if pp.impl is not None:
1265 klass, _, num = pp.impl
1266 col = "[%s%d]" % (TagClassReprs[klass], num)
1267 cols.append(_colorize(col, "blue", with_colours))
1268 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1269 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1271 cols.append(_colorize("BER", "red", with_colours))
1272 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1273 if pp.value is not None:
1275 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1277 oids is not None and
1278 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1281 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1283 if isinstance(pp.blob, binary_type):
1284 cols.append(hexenc(pp.blob))
1285 elif isinstance(pp.blob, tuple):
1286 cols.append(", ".join(pp.blob))
1288 cols.append(_colorize("OPTIONAL", "red", with_colours))
1290 cols.append(_colorize("DEFAULT", "red", with_colours))
1291 return " ".join(cols)
1294 def pp_console_blob(pp):
1295 cols = [" " * len("XXXXXYY [X,X,XXXX]YY")]
1296 if len(pp.decode_path) > 0:
1297 cols.append(" ." * (len(pp.decode_path) + 1))
1298 if isinstance(pp.blob, binary_type):
1299 blob = hexenc(pp.blob).upper()
1300 for i in range(0, len(blob), 32):
1301 chunk = blob[i:i + 32]
1302 yield " ".join(cols + [":".join(
1303 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1305 elif isinstance(pp.blob, tuple):
1306 yield " ".join(cols + [", ".join(pp.blob)])
1309 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1310 """Pretty print object
1312 :param Obj obj: object you want to pretty print
1313 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1314 from it is met, then its humand readable form is printed
1315 :param big_blobs: if large binary objects are met (like OctetString
1316 values), do we need to print them too, on separate
1318 :param with_colours: colourize output, if ``termcolor`` library
1321 def _pprint_pps(pps):
1323 if hasattr(pp, "_fields"):
1325 yield pp_console_row(
1330 with_colours=with_colours,
1332 for row in pp_console_blob(pp):
1335 yield pp_console_row(
1340 with_colours=with_colours,
1343 for row in _pprint_pps(pp):
1345 return "\n".join(_pprint_pps(obj.pps()))
1348 ########################################################################
1349 # ASN.1 primitive types
1350 ########################################################################
1353 """``BOOLEAN`` boolean type
1355 >>> b = Boolean(True)
1357 >>> b == Boolean(True)
1363 tag_default = tag_encode(1)
1364 asn1_type_name = "BOOLEAN"
1376 :param value: set the value. Either boolean type, or
1377 :py:class:`pyderasn.Boolean` object
1378 :param bytes impl: override default tag with ``IMPLICIT`` one
1379 :param bytes expl: override default tag with ``EXPLICIT`` one
1380 :param default: set default value. Type same as in ``value``
1381 :param bool optional: is object ``OPTIONAL`` in sequence
1383 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1384 self._value = None if value is None else self._value_sanitize(value)
1385 if default is not None:
1386 default = self._value_sanitize(default)
1387 self.default = self.__class__(
1393 self._value = default
1395 def _value_sanitize(self, value):
1396 if issubclass(value.__class__, Boolean):
1398 if isinstance(value, bool):
1400 raise InvalidValueType((self.__class__, bool))
1404 return self._value is not None
1407 obj = self.__class__()
1408 obj._value = self._value
1410 obj._expl = self._expl
1411 obj.default = self.default
1412 obj.optional = self.optional
1413 obj.offset = self.offset
1414 obj.llen = self.llen
1415 obj.vlen = self.vlen
1418 def __nonzero__(self):
1419 self._assert_ready()
1423 self._assert_ready()
1426 def __eq__(self, their):
1427 if isinstance(their, bool):
1428 return self._value == their
1429 if not issubclass(their.__class__, Boolean):
1432 self._value == their._value and
1433 self.tag == their.tag and
1434 self._expl == their._expl
1445 return self.__class__(
1447 impl=self.tag if impl is None else impl,
1448 expl=self._expl if expl is None else expl,
1449 default=self.default if default is None else default,
1450 optional=self.optional if optional is None else optional,
1454 self._assert_ready()
1458 (b"\xFF" if self._value else b"\x00"),
1461 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1463 t, _, lv = tag_strip(tlv)
1464 except DecodeError as err:
1465 raise err.__class__(
1467 klass=self.__class__,
1468 decode_path=decode_path,
1473 klass=self.__class__,
1474 decode_path=decode_path,
1480 l, _, v = len_decode(lv)
1481 except DecodeError as err:
1482 raise err.__class__(
1484 klass=self.__class__,
1485 decode_path=decode_path,
1489 raise InvalidLength(
1490 "Boolean's length must be equal to 1",
1491 klass=self.__class__,
1492 decode_path=decode_path,
1496 raise NotEnoughData(
1497 "encoded length is longer than data",
1498 klass=self.__class__,
1499 decode_path=decode_path,
1502 first_octet = byte2int(v)
1504 if first_octet == 0:
1506 elif first_octet == 0xFF:
1508 elif ctx.get("bered", False):
1513 "unacceptable Boolean value",
1514 klass=self.__class__,
1515 decode_path=decode_path,
1518 obj = self.__class__(
1522 default=self.default,
1523 optional=self.optional,
1524 _decoded=(offset, 1, 1),
1530 return pp_console_row(next(self.pps()))
1532 def pps(self, decode_path=()):
1534 asn1_type_name=self.asn1_type_name,
1535 obj_name=self.__class__.__name__,
1536 decode_path=decode_path,
1537 value=str(self._value) if self.ready else None,
1538 optional=self.optional,
1539 default=self == self.default,
1540 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1541 expl=None if self._expl is None else tag_decode(self._expl),
1546 expl_offset=self.expl_offset if self.expled else None,
1547 expl_tlen=self.expl_tlen if self.expled else None,
1548 expl_llen=self.expl_llen if self.expled else None,
1549 expl_vlen=self.expl_vlen if self.expled else None,
1550 expl_lenindef=self.expl_lenindef,
1556 """``INTEGER`` integer type
1558 >>> b = Integer(-123)
1560 >>> b == Integer(-123)
1565 >>> Integer(2, bounds=(1, 3))
1567 >>> Integer(5, bounds=(1, 3))
1568 Traceback (most recent call last):
1569 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1573 class Version(Integer):
1580 >>> v = Version("v1")
1587 {'v3': 2, 'v1': 0, 'v2': 1}
1589 __slots__ = ("specs", "_bound_min", "_bound_max")
1590 tag_default = tag_encode(2)
1591 asn1_type_name = "INTEGER"
1605 :param value: set the value. Either integer type, named value
1606 (if ``schema`` is specified in the class), or
1607 :py:class:`pyderasn.Integer` object
1608 :param bounds: set ``(MIN, MAX)`` value constraint.
1609 (-inf, +inf) by default
1610 :param bytes impl: override default tag with ``IMPLICIT`` one
1611 :param bytes expl: override default tag with ``EXPLICIT`` one
1612 :param default: set default value. Type same as in ``value``
1613 :param bool optional: is object ``OPTIONAL`` in sequence
1615 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1617 specs = getattr(self, "schema", {}) if _specs is None else _specs
1618 self.specs = specs if isinstance(specs, dict) else dict(specs)
1619 self._bound_min, self._bound_max = getattr(
1622 (float("-inf"), float("+inf")),
1623 ) if bounds is None else bounds
1624 if value is not None:
1625 self._value = self._value_sanitize(value)
1626 if default is not None:
1627 default = self._value_sanitize(default)
1628 self.default = self.__class__(
1634 if self._value is None:
1635 self._value = default
1637 def _value_sanitize(self, value):
1638 if issubclass(value.__class__, Integer):
1639 value = value._value
1640 elif isinstance(value, integer_types):
1642 elif isinstance(value, str):
1643 value = self.specs.get(value)
1645 raise ObjUnknown("integer value: %s" % value)
1647 raise InvalidValueType((self.__class__, int, str))
1648 if not self._bound_min <= value <= self._bound_max:
1649 raise BoundsError(self._bound_min, value, self._bound_max)
1654 return self._value is not None
1657 obj = self.__class__(_specs=self.specs)
1658 obj._value = self._value
1659 obj._bound_min = self._bound_min
1660 obj._bound_max = self._bound_max
1662 obj._expl = self._expl
1663 obj.default = self.default
1664 obj.optional = self.optional
1665 obj.offset = self.offset
1666 obj.llen = self.llen
1667 obj.vlen = self.vlen
1671 self._assert_ready()
1672 return int(self._value)
1675 self._assert_ready()
1678 bytes(self._expl or b"") +
1679 str(self._value).encode("ascii"),
1682 def __eq__(self, their):
1683 if isinstance(their, integer_types):
1684 return self._value == their
1685 if not issubclass(their.__class__, Integer):
1688 self._value == their._value and
1689 self.tag == their.tag and
1690 self._expl == their._expl
1693 def __lt__(self, their):
1694 return self._value < their._value
1698 for name, value in self.specs.items():
1699 if value == self._value:
1711 return self.__class__(
1714 (self._bound_min, self._bound_max)
1715 if bounds is None else bounds
1717 impl=self.tag if impl is None else impl,
1718 expl=self._expl if expl is None else expl,
1719 default=self.default if default is None else default,
1720 optional=self.optional if optional is None else optional,
1725 self._assert_ready()
1729 octets = bytearray([0])
1733 octets = bytearray()
1735 octets.append((value & 0xFF) ^ 0xFF)
1737 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1740 octets = bytearray()
1742 octets.append(value & 0xFF)
1744 if octets[-1] & 0x80 > 0:
1747 octets = bytes(octets)
1749 bytes_len = ceil(value.bit_length() / 8) or 1
1752 octets = value.to_bytes(
1757 except OverflowError:
1761 return b"".join((self.tag, len_encode(len(octets)), octets))
1763 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1765 t, _, lv = tag_strip(tlv)
1766 except DecodeError as err:
1767 raise err.__class__(
1769 klass=self.__class__,
1770 decode_path=decode_path,
1775 klass=self.__class__,
1776 decode_path=decode_path,
1782 l, llen, v = len_decode(lv)
1783 except DecodeError as err:
1784 raise err.__class__(
1786 klass=self.__class__,
1787 decode_path=decode_path,
1791 raise NotEnoughData(
1792 "encoded length is longer than data",
1793 klass=self.__class__,
1794 decode_path=decode_path,
1798 raise NotEnoughData(
1800 klass=self.__class__,
1801 decode_path=decode_path,
1804 v, tail = v[:l], v[l:]
1805 first_octet = byte2int(v)
1807 second_octet = byte2int(v[1:])
1809 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1810 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1813 "non normalized integer",
1814 klass=self.__class__,
1815 decode_path=decode_path,
1820 if first_octet & 0x80 > 0:
1821 octets = bytearray()
1822 for octet in bytearray(v):
1823 octets.append(octet ^ 0xFF)
1824 for octet in octets:
1825 value = (value << 8) | octet
1829 for octet in bytearray(v):
1830 value = (value << 8) | octet
1832 value = int.from_bytes(v, byteorder="big", signed=True)
1834 obj = self.__class__(
1836 bounds=(self._bound_min, self._bound_max),
1839 default=self.default,
1840 optional=self.optional,
1842 _decoded=(offset, llen, l),
1844 except BoundsError as err:
1847 klass=self.__class__,
1848 decode_path=decode_path,
1854 return pp_console_row(next(self.pps()))
1856 def pps(self, decode_path=()):
1858 asn1_type_name=self.asn1_type_name,
1859 obj_name=self.__class__.__name__,
1860 decode_path=decode_path,
1861 value=(self.named or str(self._value)) if self.ready else None,
1862 optional=self.optional,
1863 default=self == self.default,
1864 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1865 expl=None if self._expl is None else tag_decode(self._expl),
1870 expl_offset=self.expl_offset if self.expled else None,
1871 expl_tlen=self.expl_tlen if self.expled else None,
1872 expl_llen=self.expl_llen if self.expled else None,
1873 expl_vlen=self.expl_vlen if self.expled else None,
1874 expl_lenindef=self.expl_lenindef,
1878 class BitString(Obj):
1879 """``BIT STRING`` bit string type
1881 >>> BitString(b"hello world")
1882 BIT STRING 88 bits 68656c6c6f20776f726c64
1885 >>> b == b"hello world"
1890 >>> BitString("'0A3B5F291CD'H")
1891 BIT STRING 44 bits 0a3b5f291cd0
1892 >>> b = BitString("'010110000000'B")
1893 BIT STRING 12 bits 5800
1896 >>> b[0], b[1], b[2], b[3]
1897 (False, True, False, True)
1901 [False, True, False, True, True, False, False, False, False, False, False, False]
1905 class KeyUsage(BitString):
1907 ("digitalSignature", 0),
1908 ("nonRepudiation", 1),
1909 ("keyEncipherment", 2),
1912 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1913 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1915 ['nonRepudiation', 'keyEncipherment']
1917 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1919 __slots__ = ("tag_constructed", "specs", "defined")
1920 tag_default = tag_encode(3)
1921 asn1_type_name = "BIT STRING"
1934 :param value: set the value. Either binary type, tuple of named
1935 values (if ``schema`` is specified in the class),
1936 string in ``'XXX...'B`` form, or
1937 :py:class:`pyderasn.BitString` object
1938 :param bytes impl: override default tag with ``IMPLICIT`` one
1939 :param bytes expl: override default tag with ``EXPLICIT`` one
1940 :param default: set default value. Type same as in ``value``
1941 :param bool optional: is object ``OPTIONAL`` in sequence
1943 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1944 specs = getattr(self, "schema", {}) if _specs is None else _specs
1945 self.specs = specs if isinstance(specs, dict) else dict(specs)
1946 self._value = None if value is None else self._value_sanitize(value)
1947 if default is not None:
1948 default = self._value_sanitize(default)
1949 self.default = self.__class__(
1955 self._value = default
1957 tag_klass, _, tag_num = tag_decode(self.tag)
1958 self.tag_constructed = tag_encode(
1960 form=TagFormConstructed,
1964 def _bits2octets(self, bits):
1965 if len(self.specs) > 0:
1966 bits = bits.rstrip("0")
1968 bits += "0" * ((8 - (bit_len % 8)) % 8)
1969 octets = bytearray(len(bits) // 8)
1970 for i in six_xrange(len(octets)):
1971 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1972 return bit_len, bytes(octets)
1974 def _value_sanitize(self, value):
1975 if issubclass(value.__class__, BitString):
1977 if isinstance(value, (string_types, binary_type)):
1979 isinstance(value, string_types) and
1980 value.startswith("'")
1982 if value.endswith("'B"):
1984 if not set(value) <= set(("0", "1")):
1985 raise ValueError("B's coding contains unacceptable chars")
1986 return self._bits2octets(value)
1987 elif value.endswith("'H"):
1991 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
1994 raise InvalidValueType((self.__class__, string_types, binary_type))
1995 elif isinstance(value, binary_type):
1996 return (len(value) * 8, value)
1998 raise InvalidValueType((self.__class__, string_types, binary_type))
1999 if isinstance(value, tuple):
2002 isinstance(value[0], integer_types) and
2003 isinstance(value[1], binary_type)
2008 bit = self.specs.get(name)
2010 raise ObjUnknown("BitString value: %s" % name)
2013 return self._bits2octets("")
2015 return self._bits2octets("".join(
2016 ("1" if bit in bits else "0")
2017 for bit in six_xrange(max(bits) + 1)
2019 raise InvalidValueType((self.__class__, binary_type, string_types))
2023 return self._value is not None
2026 obj = self.__class__(_specs=self.specs)
2028 if value is not None:
2029 value = (value[0], value[1])
2032 obj._expl = self._expl
2033 obj.default = self.default
2034 obj.optional = self.optional
2035 obj.offset = self.offset
2036 obj.llen = self.llen
2037 obj.vlen = self.vlen
2041 self._assert_ready()
2042 for i in six_xrange(self._value[0]):
2047 self._assert_ready()
2048 return self._value[0]
2050 def __bytes__(self):
2051 self._assert_ready()
2052 return self._value[1]
2054 def __eq__(self, their):
2055 if isinstance(their, bytes):
2056 return self._value[1] == their
2057 if not issubclass(their.__class__, BitString):
2060 self._value == their._value and
2061 self.tag == their.tag and
2062 self._expl == their._expl
2067 return [name for name, bit in self.specs.items() if self[bit]]
2077 return self.__class__(
2079 impl=self.tag if impl is None else impl,
2080 expl=self._expl if expl is None else expl,
2081 default=self.default if default is None else default,
2082 optional=self.optional if optional is None else optional,
2086 def __getitem__(self, key):
2087 if isinstance(key, int):
2088 bit_len, octets = self._value
2092 byte2int(memoryview(octets)[key // 8:]) >>
2095 if isinstance(key, string_types):
2096 value = self.specs.get(key)
2098 raise ObjUnknown("BitString value: %s" % key)
2100 raise InvalidValueType((int, str))
2103 self._assert_ready()
2104 bit_len, octets = self._value
2107 len_encode(len(octets) + 1),
2108 int2byte((8 - bit_len % 8) % 8),
2112 def _decode_chunk(self, lv, offset, decode_path, ctx):
2114 l, llen, v = len_decode(lv)
2115 except DecodeError as err:
2116 raise err.__class__(
2118 klass=self.__class__,
2119 decode_path=decode_path,
2123 raise NotEnoughData(
2124 "encoded length is longer than data",
2125 klass=self.__class__,
2126 decode_path=decode_path,
2130 raise NotEnoughData(
2132 klass=self.__class__,
2133 decode_path=decode_path,
2136 pad_size = byte2int(v)
2137 if l == 1 and pad_size != 0:
2139 "invalid empty value",
2140 klass=self.__class__,
2141 decode_path=decode_path,
2147 klass=self.__class__,
2148 decode_path=decode_path,
2151 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2154 klass=self.__class__,
2155 decode_path=decode_path,
2158 v, tail = v[:l], v[l:]
2159 obj = self.__class__(
2160 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2163 default=self.default,
2164 optional=self.optional,
2166 _decoded=(offset, llen, l),
2170 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2172 t, tlen, lv = tag_strip(tlv)
2173 except DecodeError as err:
2174 raise err.__class__(
2176 klass=self.__class__,
2177 decode_path=decode_path,
2183 return self._decode_chunk(lv, offset, decode_path, ctx)
2184 if t == self.tag_constructed:
2185 if not ctx.get("bered", False):
2187 msg="unallowed BER constructed encoding",
2188 decode_path=decode_path,
2195 l, llen, v = len_decode(lv)
2196 except LenIndefForm:
2197 llen, l, v = 1, 0, lv[1:]
2199 except DecodeError as err:
2200 raise err.__class__(
2202 klass=self.__class__,
2203 decode_path=decode_path,
2206 if l > 0 and l > len(v):
2207 raise NotEnoughData(
2208 "encoded length is longer than data",
2209 klass=self.__class__,
2210 decode_path=decode_path,
2213 if not lenindef and l == 0:
2214 raise NotEnoughData(
2216 klass=self.__class__,
2217 decode_path=decode_path,
2221 sub_offset = offset + tlen + llen
2225 if v[:EOC_LEN].tobytes() == EOC:
2232 msg="chunk out of bounds",
2233 decode_path=len(chunks) - 1,
2234 offset=chunks[-1].offset,
2236 sub_decode_path = decode_path + (str(len(chunks)),)
2238 chunk, v_tail = BitString().decode(
2241 decode_path=sub_decode_path,
2247 msg="expected BitString encoded chunk",
2248 decode_path=sub_decode_path,
2251 chunks.append(chunk)
2252 sub_offset += chunk.tlvlen
2253 vlen += chunk.tlvlen
2255 if len(chunks) == 0:
2258 decode_path=decode_path,
2263 for chunk_i, chunk in enumerate(chunks[:-1]):
2264 if chunk.bit_len % 8 != 0:
2266 msg="BitString chunk is not multiple of 8 bit",
2267 decode_path=decode_path + (str(chunk_i),),
2268 offset=chunk.offset,
2270 values.append(bytes(chunk))
2271 bit_len += chunk.bit_len
2272 chunk_last = chunks[-1]
2273 values.append(bytes(chunk_last))
2274 bit_len += chunk_last.bit_len
2275 obj = self.__class__(
2276 value=(bit_len, b"".join(values)),
2279 default=self.default,
2280 optional=self.optional,
2282 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2284 obj.lenindef = lenindef
2286 return obj, (v[EOC_LEN:] if lenindef else v)
2288 klass=self.__class__,
2289 decode_path=decode_path,
2294 return pp_console_row(next(self.pps()))
2296 def pps(self, decode_path=()):
2300 bit_len, blob = self._value
2301 value = "%d bits" % bit_len
2302 if len(self.specs) > 0:
2303 blob = tuple(self.named)
2305 asn1_type_name=self.asn1_type_name,
2306 obj_name=self.__class__.__name__,
2307 decode_path=decode_path,
2310 optional=self.optional,
2311 default=self == self.default,
2312 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2313 expl=None if self._expl is None else tag_decode(self._expl),
2318 expl_offset=self.expl_offset if self.expled else None,
2319 expl_tlen=self.expl_tlen if self.expled else None,
2320 expl_llen=self.expl_llen if self.expled else None,
2321 expl_vlen=self.expl_vlen if self.expled else None,
2322 expl_lenindef=self.expl_lenindef,
2323 lenindef=self.lenindef,
2326 defined_by, defined = self.defined or (None, None)
2327 if defined_by is not None:
2329 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2333 class OctetString(Obj):
2334 """``OCTET STRING`` binary string type
2336 >>> s = OctetString(b"hello world")
2337 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2338 >>> s == OctetString(b"hello world")
2343 >>> OctetString(b"hello", bounds=(4, 4))
2344 Traceback (most recent call last):
2345 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2346 >>> OctetString(b"hell", bounds=(4, 4))
2347 OCTET STRING 4 bytes 68656c6c
2349 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2350 tag_default = tag_encode(4)
2351 asn1_type_name = "OCTET STRING"
2364 :param value: set the value. Either binary type, or
2365 :py:class:`pyderasn.OctetString` object
2366 :param bounds: set ``(MIN, MAX)`` value size constraint.
2367 (-inf, +inf) by default
2368 :param bytes impl: override default tag with ``IMPLICIT`` one
2369 :param bytes expl: override default tag with ``EXPLICIT`` one
2370 :param default: set default value. Type same as in ``value``
2371 :param bool optional: is object ``OPTIONAL`` in sequence
2373 super(OctetString, self).__init__(
2381 self._bound_min, self._bound_max = getattr(
2385 ) if bounds is None else bounds
2386 if value is not None:
2387 self._value = self._value_sanitize(value)
2388 if default is not None:
2389 default = self._value_sanitize(default)
2390 self.default = self.__class__(
2395 if self._value is None:
2396 self._value = default
2398 tag_klass, _, tag_num = tag_decode(self.tag)
2399 self.tag_constructed = tag_encode(
2401 form=TagFormConstructed,
2405 def _value_sanitize(self, value):
2406 if issubclass(value.__class__, OctetString):
2407 value = value._value
2408 elif isinstance(value, binary_type):
2411 raise InvalidValueType((self.__class__, bytes))
2412 if not self._bound_min <= len(value) <= self._bound_max:
2413 raise BoundsError(self._bound_min, len(value), self._bound_max)
2418 return self._value is not None
2421 obj = self.__class__()
2422 obj._value = self._value
2423 obj._bound_min = self._bound_min
2424 obj._bound_max = self._bound_max
2426 obj._expl = self._expl
2427 obj.default = self.default
2428 obj.optional = self.optional
2429 obj.offset = self.offset
2430 obj.llen = self.llen
2431 obj.vlen = self.vlen
2434 def __bytes__(self):
2435 self._assert_ready()
2438 def __eq__(self, their):
2439 if isinstance(their, binary_type):
2440 return self._value == their
2441 if not issubclass(their.__class__, OctetString):
2444 self._value == their._value and
2445 self.tag == their.tag and
2446 self._expl == their._expl
2449 def __lt__(self, their):
2450 return self._value < their._value
2461 return self.__class__(
2464 (self._bound_min, self._bound_max)
2465 if bounds is None else bounds
2467 impl=self.tag if impl is None else impl,
2468 expl=self._expl if expl is None else expl,
2469 default=self.default if default is None else default,
2470 optional=self.optional if optional is None else optional,
2474 self._assert_ready()
2477 len_encode(len(self._value)),
2481 def _decode_chunk(self, lv, offset, decode_path, ctx):
2483 l, llen, v = len_decode(lv)
2484 except DecodeError as err:
2485 raise err.__class__(
2487 klass=self.__class__,
2488 decode_path=decode_path,
2492 raise NotEnoughData(
2493 "encoded length is longer than data",
2494 klass=self.__class__,
2495 decode_path=decode_path,
2498 v, tail = v[:l], v[l:]
2500 obj = self.__class__(
2502 bounds=(self._bound_min, self._bound_max),
2505 default=self.default,
2506 optional=self.optional,
2507 _decoded=(offset, llen, l),
2509 except DecodeError as err:
2512 klass=self.__class__,
2513 decode_path=decode_path,
2516 except BoundsError as err:
2519 klass=self.__class__,
2520 decode_path=decode_path,
2525 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2527 t, tlen, lv = tag_strip(tlv)
2528 except DecodeError as err:
2529 raise err.__class__(
2531 klass=self.__class__,
2532 decode_path=decode_path,
2538 return self._decode_chunk(lv, offset, decode_path, ctx)
2539 if t == self.tag_constructed:
2540 if not ctx.get("bered", False):
2542 msg="unallowed BER constructed encoding",
2543 decode_path=decode_path,
2550 l, llen, v = len_decode(lv)
2551 except LenIndefForm:
2552 llen, l, v = 1, 0, lv[1:]
2554 except DecodeError as err:
2555 raise err.__class__(
2557 klass=self.__class__,
2558 decode_path=decode_path,
2561 if l > 0 and l > len(v):
2562 raise NotEnoughData(
2563 "encoded length is longer than data",
2564 klass=self.__class__,
2565 decode_path=decode_path,
2568 if not lenindef and l == 0:
2569 raise NotEnoughData(
2571 klass=self.__class__,
2572 decode_path=decode_path,
2576 sub_offset = offset + tlen + llen
2580 if v[:EOC_LEN].tobytes() == EOC:
2587 msg="chunk out of bounds",
2588 decode_path=len(chunks) - 1,
2589 offset=chunks[-1].offset,
2591 sub_decode_path = decode_path + (str(len(chunks)),)
2593 chunk, v_tail = OctetString().decode(
2596 decode_path=sub_decode_path,
2602 msg="expected OctetString encoded chunk",
2603 decode_path=sub_decode_path,
2606 chunks.append(chunk)
2607 sub_offset += chunk.tlvlen
2608 vlen += chunk.tlvlen
2610 if len(chunks) == 0:
2613 decode_path=decode_path,
2617 obj = self.__class__(
2618 value=b"".join(bytes(chunk) for chunk in chunks),
2619 bounds=(self._bound_min, self._bound_max),
2622 default=self.default,
2623 optional=self.optional,
2624 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2626 except DecodeError as err:
2629 klass=self.__class__,
2630 decode_path=decode_path,
2633 except BoundsError as err:
2636 klass=self.__class__,
2637 decode_path=decode_path,
2640 obj.lenindef = lenindef
2642 return obj, (v[EOC_LEN:] if lenindef else v)
2644 klass=self.__class__,
2645 decode_path=decode_path,
2650 return pp_console_row(next(self.pps()))
2652 def pps(self, decode_path=()):
2654 asn1_type_name=self.asn1_type_name,
2655 obj_name=self.__class__.__name__,
2656 decode_path=decode_path,
2657 value=("%d bytes" % len(self._value)) if self.ready else None,
2658 blob=self._value if self.ready else None,
2659 optional=self.optional,
2660 default=self == self.default,
2661 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2662 expl=None if self._expl is None else tag_decode(self._expl),
2667 expl_offset=self.expl_offset if self.expled else None,
2668 expl_tlen=self.expl_tlen if self.expled else None,
2669 expl_llen=self.expl_llen if self.expled else None,
2670 expl_vlen=self.expl_vlen if self.expled else None,
2671 expl_lenindef=self.expl_lenindef,
2672 lenindef=self.lenindef,
2675 defined_by, defined = self.defined or (None, None)
2676 if defined_by is not None:
2678 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2683 """``NULL`` null object
2691 tag_default = tag_encode(5)
2692 asn1_type_name = "NULL"
2696 value=None, # unused, but Sequence passes it
2703 :param bytes impl: override default tag with ``IMPLICIT`` one
2704 :param bytes expl: override default tag with ``EXPLICIT`` one
2705 :param bool optional: is object ``OPTIONAL`` in sequence
2707 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2715 obj = self.__class__()
2717 obj._expl = self._expl
2718 obj.default = self.default
2719 obj.optional = self.optional
2720 obj.offset = self.offset
2721 obj.llen = self.llen
2722 obj.vlen = self.vlen
2725 def __eq__(self, their):
2726 if not issubclass(their.__class__, Null):
2729 self.tag == their.tag and
2730 self._expl == their._expl
2740 return self.__class__(
2741 impl=self.tag if impl is None else impl,
2742 expl=self._expl if expl is None else expl,
2743 optional=self.optional if optional is None else optional,
2747 return self.tag + len_encode(0)
2749 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2751 t, _, lv = tag_strip(tlv)
2752 except DecodeError as err:
2753 raise err.__class__(
2755 klass=self.__class__,
2756 decode_path=decode_path,
2761 klass=self.__class__,
2762 decode_path=decode_path,
2768 l, _, v = len_decode(lv)
2769 except DecodeError as err:
2770 raise err.__class__(
2772 klass=self.__class__,
2773 decode_path=decode_path,
2777 raise InvalidLength(
2778 "Null must have zero length",
2779 klass=self.__class__,
2780 decode_path=decode_path,
2783 obj = self.__class__(
2786 optional=self.optional,
2787 _decoded=(offset, 1, 0),
2792 return pp_console_row(next(self.pps()))
2794 def pps(self, decode_path=()):
2796 asn1_type_name=self.asn1_type_name,
2797 obj_name=self.__class__.__name__,
2798 decode_path=decode_path,
2799 optional=self.optional,
2800 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2801 expl=None if self._expl is None else tag_decode(self._expl),
2806 expl_offset=self.expl_offset if self.expled else None,
2807 expl_tlen=self.expl_tlen if self.expled else None,
2808 expl_llen=self.expl_llen if self.expled else None,
2809 expl_vlen=self.expl_vlen if self.expled else None,
2810 expl_lenindef=self.expl_lenindef,
2814 class ObjectIdentifier(Obj):
2815 """``OBJECT IDENTIFIER`` OID type
2817 >>> oid = ObjectIdentifier((1, 2, 3))
2818 OBJECT IDENTIFIER 1.2.3
2819 >>> oid == ObjectIdentifier("1.2.3")
2825 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2826 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2828 >>> str(ObjectIdentifier((3, 1)))
2829 Traceback (most recent call last):
2830 pyderasn.InvalidOID: unacceptable first arc value
2832 __slots__ = ("defines",)
2833 tag_default = tag_encode(6)
2834 asn1_type_name = "OBJECT IDENTIFIER"
2847 :param value: set the value. Either tuples of integers,
2848 string of "."-concatenated integers, or
2849 :py:class:`pyderasn.ObjectIdentifier` object
2850 :param defines: sequence of tuples. Each tuple has two elements.
2851 First one is relative to current one decode
2852 path, aiming to the field defined by that OID.
2853 Read about relative path in
2854 :py:func:`pyderasn.abs_decode_path`. Second
2855 tuple element is ``{OID: pyderasn.Obj()}``
2856 dictionary, mapping between current OID value
2857 and structure applied to defined field.
2858 :ref:`Read about DEFINED BY <definedby>`
2859 :param bytes impl: override default tag with ``IMPLICIT`` one
2860 :param bytes expl: override default tag with ``EXPLICIT`` one
2861 :param default: set default value. Type same as in ``value``
2862 :param bool optional: is object ``OPTIONAL`` in sequence
2864 super(ObjectIdentifier, self).__init__(
2872 if value is not None:
2873 self._value = self._value_sanitize(value)
2874 if default is not None:
2875 default = self._value_sanitize(default)
2876 self.default = self.__class__(
2881 if self._value is None:
2882 self._value = default
2883 self.defines = defines
2885 def __add__(self, their):
2886 if isinstance(their, self.__class__):
2887 return self.__class__(self._value + their._value)
2888 if isinstance(their, tuple):
2889 return self.__class__(self._value + their)
2890 raise InvalidValueType((self.__class__, tuple))
2892 def _value_sanitize(self, value):
2893 if issubclass(value.__class__, ObjectIdentifier):
2895 if isinstance(value, string_types):
2897 value = tuple(int(arc) for arc in value.split("."))
2899 raise InvalidOID("unacceptable arcs values")
2900 if isinstance(value, tuple):
2902 raise InvalidOID("less than 2 arcs")
2903 first_arc = value[0]
2904 if first_arc in (0, 1):
2905 if not (0 <= value[1] <= 39):
2906 raise InvalidOID("second arc is too wide")
2907 elif first_arc == 2:
2910 raise InvalidOID("unacceptable first arc value")
2912 raise InvalidValueType((self.__class__, str, tuple))
2916 return self._value is not None
2919 obj = self.__class__()
2920 obj._value = self._value
2921 obj.defines = self.defines
2923 obj._expl = self._expl
2924 obj.default = self.default
2925 obj.optional = self.optional
2926 obj.offset = self.offset
2927 obj.llen = self.llen
2928 obj.vlen = self.vlen
2932 self._assert_ready()
2933 return iter(self._value)
2936 return ".".join(str(arc) for arc in self._value or ())
2939 self._assert_ready()
2942 bytes(self._expl or b"") +
2943 str(self._value).encode("ascii"),
2946 def __eq__(self, their):
2947 if isinstance(their, tuple):
2948 return self._value == their
2949 if not issubclass(their.__class__, ObjectIdentifier):
2952 self.tag == their.tag and
2953 self._expl == their._expl and
2954 self._value == their._value
2957 def __lt__(self, their):
2958 return self._value < their._value
2969 return self.__class__(
2971 defines=self.defines if defines is None else defines,
2972 impl=self.tag if impl is None else impl,
2973 expl=self._expl if expl is None else expl,
2974 default=self.default if default is None else default,
2975 optional=self.optional if optional is None else optional,
2979 self._assert_ready()
2981 first_value = value[1]
2982 first_arc = value[0]
2985 elif first_arc == 1:
2987 elif first_arc == 2:
2989 else: # pragma: no cover
2990 raise RuntimeError("invalid arc is stored")
2991 octets = [zero_ended_encode(first_value)]
2992 for arc in value[2:]:
2993 octets.append(zero_ended_encode(arc))
2994 v = b"".join(octets)
2995 return b"".join((self.tag, len_encode(len(v)), v))
2997 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2999 t, _, lv = tag_strip(tlv)
3000 except DecodeError as err:
3001 raise err.__class__(
3003 klass=self.__class__,
3004 decode_path=decode_path,
3009 klass=self.__class__,
3010 decode_path=decode_path,
3016 l, llen, v = len_decode(lv)
3017 except DecodeError as err:
3018 raise err.__class__(
3020 klass=self.__class__,
3021 decode_path=decode_path,
3025 raise NotEnoughData(
3026 "encoded length is longer than data",
3027 klass=self.__class__,
3028 decode_path=decode_path,
3032 raise NotEnoughData(
3034 klass=self.__class__,
3035 decode_path=decode_path,
3038 v, tail = v[:l], v[l:]
3044 octet = indexbytes(v, i)
3045 arc = (arc << 7) | (octet & 0x7F)
3046 if octet & 0x80 == 0:
3054 klass=self.__class__,
3055 decode_path=decode_path,
3059 second_arc = arcs[0]
3060 if 0 <= second_arc <= 39:
3062 elif 40 <= second_arc <= 79:
3068 obj = self.__class__(
3069 value=tuple([first_arc, second_arc] + arcs[1:]),
3072 default=self.default,
3073 optional=self.optional,
3074 _decoded=(offset, llen, l),
3079 return pp_console_row(next(self.pps()))
3081 def pps(self, decode_path=()):
3083 asn1_type_name=self.asn1_type_name,
3084 obj_name=self.__class__.__name__,
3085 decode_path=decode_path,
3086 value=str(self) if self.ready else None,
3087 optional=self.optional,
3088 default=self == self.default,
3089 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3090 expl=None if self._expl is None else tag_decode(self._expl),
3095 expl_offset=self.expl_offset if self.expled else None,
3096 expl_tlen=self.expl_tlen if self.expled else None,
3097 expl_llen=self.expl_llen if self.expled else None,
3098 expl_vlen=self.expl_vlen if self.expled else None,
3099 expl_lenindef=self.expl_lenindef,
3103 class Enumerated(Integer):
3104 """``ENUMERATED`` integer type
3106 This type is identical to :py:class:`pyderasn.Integer`, but requires
3107 schema to be specified and does not accept values missing from it.
3110 tag_default = tag_encode(10)
3111 asn1_type_name = "ENUMERATED"
3122 bounds=None, # dummy argument, workability for Integer.decode
3124 super(Enumerated, self).__init__(
3133 if len(self.specs) == 0:
3134 raise ValueError("schema must be specified")
3136 def _value_sanitize(self, value):
3137 if isinstance(value, self.__class__):
3138 value = value._value
3139 elif isinstance(value, integer_types):
3140 if value not in list(self.specs.values()):
3142 "unknown integer value: %s" % value,
3143 klass=self.__class__,
3145 elif isinstance(value, string_types):
3146 value = self.specs.get(value)
3148 raise ObjUnknown("integer value: %s" % value)
3150 raise InvalidValueType((self.__class__, int, str))
3154 obj = self.__class__(_specs=self.specs)
3155 obj._value = self._value
3156 obj._bound_min = self._bound_min
3157 obj._bound_max = self._bound_max
3159 obj._expl = self._expl
3160 obj.default = self.default
3161 obj.optional = self.optional
3162 obj.offset = self.offset
3163 obj.llen = self.llen
3164 obj.vlen = self.vlen
3176 return self.__class__(
3178 impl=self.tag if impl is None else impl,
3179 expl=self._expl if expl is None else expl,
3180 default=self.default if default is None else default,
3181 optional=self.optional if optional is None else optional,
3186 class CommonString(OctetString):
3187 """Common class for all strings
3189 Everything resembles :py:class:`pyderasn.OctetString`, except
3190 ability to deal with unicode text strings.
3192 >>> hexenc("привет мир".encode("utf-8"))
3193 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3194 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3196 >>> s = UTF8String("привет мир")
3197 UTF8String UTF8String привет мир
3199 'привет мир'
3200 >>> hexenc(bytes(s))
3201 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3203 >>> PrintableString("привет мир")
3204 Traceback (most recent call last):
3205 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3207 >>> BMPString("ада", bounds=(2, 2))
3208 Traceback (most recent call last):
3209 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3210 >>> s = BMPString("ад", bounds=(2, 2))
3213 >>> hexenc(bytes(s))
3221 * - :py:class:`pyderasn.UTF8String`
3223 * - :py:class:`pyderasn.NumericString`
3225 * - :py:class:`pyderasn.PrintableString`
3227 * - :py:class:`pyderasn.TeletexString`
3229 * - :py:class:`pyderasn.T61String`
3231 * - :py:class:`pyderasn.VideotexString`
3233 * - :py:class:`pyderasn.IA5String`
3235 * - :py:class:`pyderasn.GraphicString`
3237 * - :py:class:`pyderasn.VisibleString`
3239 * - :py:class:`pyderasn.ISO646String`
3241 * - :py:class:`pyderasn.GeneralString`
3243 * - :py:class:`pyderasn.UniversalString`
3245 * - :py:class:`pyderasn.BMPString`
3248 __slots__ = ("encoding",)
3250 def _value_sanitize(self, value):
3252 value_decoded = None
3253 if isinstance(value, self.__class__):
3254 value_raw = value._value
3255 elif isinstance(value, text_type):
3256 value_decoded = value
3257 elif isinstance(value, binary_type):
3260 raise InvalidValueType((self.__class__, text_type, binary_type))
3263 value_decoded.encode(self.encoding)
3264 if value_raw is None else value_raw
3267 value_raw.decode(self.encoding)
3268 if value_decoded is None else value_decoded
3270 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3271 raise DecodeError(str(err))
3272 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3280 def __eq__(self, their):
3281 if isinstance(their, binary_type):
3282 return self._value == their
3283 if isinstance(their, text_type):
3284 return self._value == their.encode(self.encoding)
3285 if not isinstance(their, self.__class__):
3288 self._value == their._value and
3289 self.tag == their.tag and
3290 self._expl == their._expl
3293 def __unicode__(self):
3295 return self._value.decode(self.encoding)
3296 return text_type(self._value)
3299 return pp_console_row(next(self.pps(no_unicode=PY2)))
3301 def pps(self, decode_path=(), no_unicode=False):
3304 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3306 asn1_type_name=self.asn1_type_name,
3307 obj_name=self.__class__.__name__,
3308 decode_path=decode_path,
3310 optional=self.optional,
3311 default=self == self.default,
3312 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3313 expl=None if self._expl is None else tag_decode(self._expl),
3318 expl_offset=self.expl_offset if self.expled else None,
3319 expl_tlen=self.expl_tlen if self.expled else None,
3320 expl_llen=self.expl_llen if self.expled else None,
3321 expl_vlen=self.expl_vlen if self.expled else None,
3322 expl_lenindef=self.expl_lenindef,
3326 class UTF8String(CommonString):
3328 tag_default = tag_encode(12)
3330 asn1_type_name = "UTF8String"
3333 class NumericString(CommonString):
3336 Its value is properly sanitized: only ASCII digits can be stored.
3339 tag_default = tag_encode(18)
3341 asn1_type_name = "NumericString"
3342 allowable_chars = set(digits.encode("ascii"))
3344 def _value_sanitize(self, value):
3345 value = super(NumericString, self)._value_sanitize(value)
3346 if not set(value) <= self.allowable_chars:
3347 raise DecodeError("non-numeric value")
3351 class PrintableString(CommonString):
3353 tag_default = tag_encode(19)
3355 asn1_type_name = "PrintableString"
3358 class TeletexString(CommonString):
3360 tag_default = tag_encode(20)
3362 asn1_type_name = "TeletexString"
3365 class T61String(TeletexString):
3367 asn1_type_name = "T61String"
3370 class VideotexString(CommonString):
3372 tag_default = tag_encode(21)
3373 encoding = "iso-8859-1"
3374 asn1_type_name = "VideotexString"
3377 class IA5String(CommonString):
3379 tag_default = tag_encode(22)
3381 asn1_type_name = "IA5"
3384 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3385 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3386 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3389 class UTCTime(CommonString):
3390 """``UTCTime`` datetime type
3392 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3393 UTCTime UTCTime 2017-09-30T22:07:50
3399 datetime.datetime(2017, 9, 30, 22, 7, 50)
3400 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3401 datetime.datetime(1957, 9, 30, 22, 7, 50)
3404 tag_default = tag_encode(23)
3406 asn1_type_name = "UTCTime"
3408 fmt = "%y%m%d%H%M%SZ"
3418 bounds=None, # dummy argument, workability for OctetString.decode
3421 :param value: set the value. Either datetime type, or
3422 :py:class:`pyderasn.UTCTime` object
3423 :param bytes impl: override default tag with ``IMPLICIT`` one
3424 :param bytes expl: override default tag with ``EXPLICIT`` one
3425 :param default: set default value. Type same as in ``value``
3426 :param bool optional: is object ``OPTIONAL`` in sequence
3428 super(UTCTime, self).__init__(
3436 if value is not None:
3437 self._value = self._value_sanitize(value)
3438 if default is not None:
3439 default = self._value_sanitize(default)
3440 self.default = self.__class__(
3445 if self._value is None:
3446 self._value = default
3448 def _value_sanitize(self, value):
3449 if isinstance(value, self.__class__):
3451 if isinstance(value, datetime):
3452 return value.strftime(self.fmt).encode("ascii")
3453 if isinstance(value, binary_type):
3454 value_decoded = value.decode("ascii")
3455 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3457 datetime.strptime(value_decoded, self.fmt)
3459 raise DecodeError("invalid UTCTime format")
3462 raise DecodeError("invalid UTCTime length")
3463 raise InvalidValueType((self.__class__, datetime))
3465 def __eq__(self, their):
3466 if isinstance(their, binary_type):
3467 return self._value == their
3468 if isinstance(their, datetime):
3469 return self.todatetime() == their
3470 if not isinstance(their, self.__class__):
3473 self._value == their._value and
3474 self.tag == their.tag and
3475 self._expl == their._expl
3478 def todatetime(self):
3479 """Convert to datetime
3483 Pay attention that UTCTime can not hold full year, so all years
3484 having < 50 years are treated as 20xx, 19xx otherwise, according
3485 to X.509 recomendation.
3487 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3488 year = value.year % 100
3490 year=(2000 + year) if year < 50 else (1900 + year),
3494 minute=value.minute,
3495 second=value.second,
3499 return pp_console_row(next(self.pps()))
3501 def pps(self, decode_path=()):
3503 asn1_type_name=self.asn1_type_name,
3504 obj_name=self.__class__.__name__,
3505 decode_path=decode_path,
3506 value=self.todatetime().isoformat() if self.ready else None,
3507 optional=self.optional,
3508 default=self == self.default,
3509 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3510 expl=None if self._expl is None else tag_decode(self._expl),
3515 expl_offset=self.expl_offset if self.expled else None,
3516 expl_tlen=self.expl_tlen if self.expled else None,
3517 expl_llen=self.expl_llen if self.expled else None,
3518 expl_vlen=self.expl_vlen if self.expled else None,
3519 expl_lenindef=self.expl_lenindef,
3523 class GeneralizedTime(UTCTime):
3524 """``GeneralizedTime`` datetime type
3526 This type is similar to :py:class:`pyderasn.UTCTime`.
3528 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3529 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3531 '20170930220750.000123Z'
3532 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3533 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3536 tag_default = tag_encode(24)
3537 asn1_type_name = "GeneralizedTime"
3539 fmt = "%Y%m%d%H%M%SZ"
3540 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3542 def _value_sanitize(self, value):
3543 if isinstance(value, self.__class__):
3545 if isinstance(value, datetime):
3546 return value.strftime(
3547 self.fmt_ms if value.microsecond > 0 else self.fmt
3549 if isinstance(value, binary_type):
3550 value_decoded = value.decode("ascii")
3551 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3553 datetime.strptime(value_decoded, self.fmt)
3556 "invalid GeneralizedTime (without ms) format",
3559 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3561 datetime.strptime(value_decoded, self.fmt_ms)
3564 "invalid GeneralizedTime (with ms) format",
3569 "invalid GeneralizedTime length",
3570 klass=self.__class__,
3572 raise InvalidValueType((self.__class__, datetime))
3574 def todatetime(self):
3575 value = self._value.decode("ascii")
3576 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3577 return datetime.strptime(value, self.fmt)
3578 return datetime.strptime(value, self.fmt_ms)
3581 class GraphicString(CommonString):
3583 tag_default = tag_encode(25)
3584 encoding = "iso-8859-1"
3585 asn1_type_name = "GraphicString"
3588 class VisibleString(CommonString):
3590 tag_default = tag_encode(26)
3592 asn1_type_name = "VisibleString"
3595 class ISO646String(VisibleString):
3597 asn1_type_name = "ISO646String"
3600 class GeneralString(CommonString):
3602 tag_default = tag_encode(27)
3603 encoding = "iso-8859-1"
3604 asn1_type_name = "GeneralString"
3607 class UniversalString(CommonString):
3609 tag_default = tag_encode(28)
3610 encoding = "utf-32-be"
3611 asn1_type_name = "UniversalString"
3614 class BMPString(CommonString):
3616 tag_default = tag_encode(30)
3617 encoding = "utf-16-be"
3618 asn1_type_name = "BMPString"
3622 """``CHOICE`` special type
3626 class GeneralName(Choice):
3628 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3629 ("dNSName", IA5String(impl=tag_ctxp(2))),
3632 >>> gn = GeneralName()
3634 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3635 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3636 >>> gn["dNSName"] = IA5String("bar.baz")
3637 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3638 >>> gn["rfc822Name"]
3641 [2] IA5String IA5 bar.baz
3644 >>> gn.value == gn["dNSName"]
3647 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3649 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3650 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3652 __slots__ = ("specs",)
3654 asn1_type_name = "CHOICE"
3667 :param value: set the value. Either ``(choice, value)`` tuple, or
3668 :py:class:`pyderasn.Choice` object
3669 :param bytes impl: can not be set, do **not** use it
3670 :param bytes expl: override default tag with ``EXPLICIT`` one
3671 :param default: set default value. Type same as in ``value``
3672 :param bool optional: is object ``OPTIONAL`` in sequence
3674 if impl is not None:
3675 raise ValueError("no implicit tag allowed for CHOICE")
3676 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3678 schema = getattr(self, "schema", ())
3679 if len(schema) == 0:
3680 raise ValueError("schema must be specified")
3682 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3685 if value is not None:
3686 self._value = self._value_sanitize(value)
3687 if default is not None:
3688 default_value = self._value_sanitize(default)
3689 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3690 default_obj.specs = self.specs
3691 default_obj._value = default_value
3692 self.default = default_obj
3694 self._value = default_obj.copy()._value
3696 def _value_sanitize(self, value):
3697 if isinstance(value, self.__class__):
3699 if isinstance(value, tuple) and len(value) == 2:
3701 spec = self.specs.get(choice)
3703 raise ObjUnknown(choice)
3704 if not isinstance(obj, spec.__class__):
3705 raise InvalidValueType((spec,))
3706 return (choice, spec(obj))
3707 raise InvalidValueType((self.__class__, tuple))
3711 return self._value is not None and self._value[1].ready
3714 obj = self.__class__(schema=self.specs)
3715 obj._expl = self._expl
3716 obj.default = self.default
3717 obj.optional = self.optional
3718 obj.offset = self.offset
3719 obj.llen = self.llen
3720 obj.vlen = self.vlen
3722 if value is not None:
3723 obj._value = (value[0], value[1].copy())
3726 def __eq__(self, their):
3727 if isinstance(their, tuple) and len(their) == 2:
3728 return self._value == their
3729 if not isinstance(their, self.__class__):
3732 self.specs == their.specs and
3733 self._value == their._value
3743 return self.__class__(
3746 expl=self._expl if expl is None else expl,
3747 default=self.default if default is None else default,
3748 optional=self.optional if optional is None else optional,
3753 self._assert_ready()
3754 return self._value[0]
3758 self._assert_ready()
3759 return self._value[1]
3761 def __getitem__(self, key):
3762 if key not in self.specs:
3763 raise ObjUnknown(key)
3764 if self._value is None:
3766 choice, value = self._value
3771 def __setitem__(self, key, value):
3772 spec = self.specs.get(key)
3774 raise ObjUnknown(key)
3775 if not isinstance(value, spec.__class__):
3776 raise InvalidValueType((spec.__class__,))
3777 self._value = (key, spec(value))
3785 return self._value[1].decoded if self.ready else False
3788 self._assert_ready()
3789 return self._value[1].encode()
3791 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3792 for choice, spec in self.specs.items():
3793 sub_decode_path = decode_path + (choice,)
3799 decode_path=sub_decode_path,
3808 klass=self.__class__,
3809 decode_path=decode_path,
3814 value, tail = spec.decode(
3818 decode_path=sub_decode_path,
3821 obj = self.__class__(
3824 default=self.default,
3825 optional=self.optional,
3826 _decoded=(offset, 0, value.tlvlen),
3828 obj._value = (choice, value)
3832 value = pp_console_row(next(self.pps()))
3834 value = "%s[%r]" % (value, self.value)
3837 def pps(self, decode_path=()):
3839 asn1_type_name=self.asn1_type_name,
3840 obj_name=self.__class__.__name__,
3841 decode_path=decode_path,
3842 value=self.choice if self.ready else None,
3843 optional=self.optional,
3844 default=self == self.default,
3845 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3846 expl=None if self._expl is None else tag_decode(self._expl),
3851 expl_lenindef=self.expl_lenindef,
3854 yield self.value.pps(decode_path=decode_path + (self.choice,))
3857 class PrimitiveTypes(Choice):
3858 """Predefined ``CHOICE`` for all generic primitive types
3860 It could be useful for general decoding of some unspecified values:
3862 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3863 OCTET STRING 3 bytes 666f6f
3864 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3868 schema = tuple((klass.__name__, klass()) for klass in (
3893 """``ANY`` special type
3895 >>> Any(Integer(-123))
3897 >>> a = Any(OctetString(b"hello world").encode())
3898 ANY 040b68656c6c6f20776f726c64
3899 >>> hexenc(bytes(a))
3900 b'0x040x0bhello world'
3902 __slots__ = ("defined",)
3903 tag_default = tag_encode(0)
3904 asn1_type_name = "ANY"
3914 :param value: set the value. Either any kind of pyderasn's
3915 **ready** object, or bytes. Pay attention that
3916 **no** validation is performed is raw binary value
3918 :param bytes expl: override default tag with ``EXPLICIT`` one
3919 :param bool optional: is object ``OPTIONAL`` in sequence
3921 super(Any, self).__init__(None, expl, None, optional, _decoded)
3922 self._value = None if value is None else self._value_sanitize(value)
3925 def _value_sanitize(self, value):
3926 if isinstance(value, self.__class__):
3928 if isinstance(value, Obj):
3929 return value.encode()
3930 if isinstance(value, binary_type):
3932 raise InvalidValueType((self.__class__, Obj, binary_type))
3936 return self._value is not None
3939 obj = self.__class__()
3940 obj._value = self._value
3942 obj._expl = self._expl
3943 obj.optional = self.optional
3944 obj.offset = self.offset
3945 obj.llen = self.llen
3946 obj.vlen = self.vlen
3949 def __eq__(self, their):
3950 if isinstance(their, binary_type):
3951 return self._value == their
3952 if issubclass(their.__class__, Any):
3953 return self._value == their._value
3962 return self.__class__(
3964 expl=self._expl if expl is None else expl,
3965 optional=self.optional if optional is None else optional,
3968 def __bytes__(self):
3969 self._assert_ready()
3977 self._assert_ready()
3980 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3982 t, tlen, lv = tag_strip(tlv)
3983 except DecodeError as err:
3984 raise err.__class__(
3986 klass=self.__class__,
3987 decode_path=decode_path,
3991 l, llen, v = len_decode(lv)
3992 except LenIndefForm as err:
3993 if not ctx.get("bered", False):
3994 raise err.__class__(
3996 klass=self.__class__,
3997 decode_path=decode_path,
4000 llen, vlen, v = 1, 0, lv[1:]
4001 sub_offset = offset + tlen + llen
4004 if v[:EOC_LEN].tobytes() == EOC:
4005 tlvlen = tlen + llen + vlen + EOC_LEN
4006 obj = self.__class__(
4007 value=tlv[:tlvlen].tobytes(),
4009 optional=self.optional,
4010 _decoded=(offset, 0, tlvlen),
4014 return obj, v[EOC_LEN:]
4016 chunk, v = Any().decode(
4019 decode_path=decode_path + (str(chunk_i),),
4023 vlen += chunk.tlvlen
4024 sub_offset += chunk.tlvlen
4026 except DecodeError as err:
4027 raise err.__class__(
4029 klass=self.__class__,
4030 decode_path=decode_path,
4034 raise NotEnoughData(
4035 "encoded length is longer than data",
4036 klass=self.__class__,
4037 decode_path=decode_path,
4040 tlvlen = tlen + llen + l
4041 v, tail = tlv[:tlvlen], v[l:]
4042 obj = self.__class__(
4045 optional=self.optional,
4046 _decoded=(offset, 0, tlvlen),
4052 return pp_console_row(next(self.pps()))
4054 def pps(self, decode_path=()):
4056 asn1_type_name=self.asn1_type_name,
4057 obj_name=self.__class__.__name__,
4058 decode_path=decode_path,
4059 blob=self._value if self.ready else None,
4060 optional=self.optional,
4061 default=self == self.default,
4062 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4063 expl=None if self._expl is None else tag_decode(self._expl),
4068 expl_offset=self.expl_offset if self.expled else None,
4069 expl_tlen=self.expl_tlen if self.expled else None,
4070 expl_llen=self.expl_llen if self.expled else None,
4071 expl_vlen=self.expl_vlen if self.expled else None,
4072 expl_lenindef=self.expl_lenindef,
4073 lenindef=self.lenindef,
4075 defined_by, defined = self.defined or (None, None)
4076 if defined_by is not None:
4078 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4082 ########################################################################
4083 # ASN.1 constructed types
4084 ########################################################################
4086 def get_def_by_path(defines_by_path, sub_decode_path):
4087 """Get define by decode path
4089 for path, define in defines_by_path:
4090 if len(path) != len(sub_decode_path):
4092 for p1, p2 in zip(path, sub_decode_path):
4093 if (p1 != any) and (p1 != p2):
4099 def abs_decode_path(decode_path, rel_path):
4100 """Create an absolute decode path from current and relative ones
4102 :param decode_path: current decode path, starting point.
4104 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4105 If first tuple's element is "/", then treat it as
4106 an absolute path, ignoring ``decode_path`` as
4107 starting point. Also this tuple can contain ".."
4108 elements, stripping the leading element from
4111 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4112 ("foo", "bar", "baz", "whatever")
4113 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4115 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4118 if rel_path[0] == "/":
4120 if rel_path[0] == "..":
4121 return abs_decode_path(decode_path[:-1], rel_path[1:])
4122 return decode_path + rel_path
4125 class Sequence(Obj):
4126 """``SEQUENCE`` structure type
4128 You have to make specification of sequence::
4130 class Extension(Sequence):
4132 ("extnID", ObjectIdentifier()),
4133 ("critical", Boolean(default=False)),
4134 ("extnValue", OctetString()),
4137 Then, you can work with it as with dictionary.
4139 >>> ext = Extension()
4140 >>> Extension().specs
4142 ('extnID', OBJECT IDENTIFIER),
4143 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4144 ('extnValue', OCTET STRING),
4146 >>> ext["extnID"] = "1.2.3"
4147 Traceback (most recent call last):
4148 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4149 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4151 You can determine if sequence is ready to be encoded:
4156 Traceback (most recent call last):
4157 pyderasn.ObjNotReady: object is not ready: extnValue
4158 >>> ext["extnValue"] = OctetString(b"foobar")
4162 Value you want to assign, must have the same **type** as in
4163 corresponding specification, but it can have different tags,
4164 optional/default attributes -- they will be taken from specification
4167 class TBSCertificate(Sequence):
4169 ("version", Version(expl=tag_ctxc(0), default="v1")),
4172 >>> tbs = TBSCertificate()
4173 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4175 Assign ``None`` to remove value from sequence.
4177 You can set values in Sequence during its initialization:
4179 >>> AlgorithmIdentifier((
4180 ("algorithm", ObjectIdentifier("1.2.3")),
4181 ("parameters", Any(Null()))
4183 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4185 You can determine if value exists/set in the sequence and take its value:
4187 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4190 OBJECT IDENTIFIER 1.2.3
4192 But pay attention that if value has default, then it won't be (not
4193 in) in the sequence (because ``DEFAULT`` must not be encoded in
4194 DER), but you can read its value:
4196 >>> "critical" in ext, ext["critical"]
4197 (False, BOOLEAN False)
4198 >>> ext["critical"] = Boolean(True)
4199 >>> "critical" in ext, ext["critical"]
4200 (True, BOOLEAN True)
4202 All defaulted values are always optional.
4204 .. _strict_default_existence_ctx:
4208 When decoded DER contains defaulted value inside, then
4209 technically this is not valid DER encoding. But we allow and pass
4210 it **by default**. Of course reencoding of that kind of DER will
4211 result in different binary representation (validly without
4212 defaulted value inside). You can enable strict defaulted values
4213 existence validation by setting ``"strict_default_existence":
4214 True`` :ref:`context <ctx>` option -- decoding process will raise
4215 an exception if defaulted value is met.
4217 Two sequences are equal if they have equal specification (schema),
4218 implicit/explicit tagging and the same values.
4220 __slots__ = ("specs",)
4221 tag_default = tag_encode(form=TagFormConstructed, num=16)
4222 asn1_type_name = "SEQUENCE"
4234 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4236 schema = getattr(self, "schema", ())
4238 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4241 if value is not None:
4242 if issubclass(value.__class__, Sequence):
4243 self._value = value._value
4244 elif hasattr(value, "__iter__"):
4245 for seq_key, seq_value in value:
4246 self[seq_key] = seq_value
4248 raise InvalidValueType((Sequence,))
4249 if default is not None:
4250 if not issubclass(default.__class__, Sequence):
4251 raise InvalidValueType((Sequence,))
4252 default_value = default._value
4253 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4254 default_obj.specs = self.specs
4255 default_obj._value = default_value
4256 self.default = default_obj
4258 self._value = default_obj.copy()._value
4262 for name, spec in self.specs.items():
4263 value = self._value.get(name)
4274 obj = self.__class__(schema=self.specs)
4276 obj._expl = self._expl
4277 obj.default = self.default
4278 obj.optional = self.optional
4279 obj.offset = self.offset
4280 obj.llen = self.llen
4281 obj.vlen = self.vlen
4282 obj._value = {k: v.copy() for k, v in self._value.items()}
4285 def __eq__(self, their):
4286 if not isinstance(their, self.__class__):
4289 self.specs == their.specs and
4290 self.tag == their.tag and
4291 self._expl == their._expl and
4292 self._value == their._value
4303 return self.__class__(
4306 impl=self.tag if impl is None else impl,
4307 expl=self._expl if expl is None else expl,
4308 default=self.default if default is None else default,
4309 optional=self.optional if optional is None else optional,
4312 def __contains__(self, key):
4313 return key in self._value
4315 def __setitem__(self, key, value):
4316 spec = self.specs.get(key)
4318 raise ObjUnknown(key)
4320 self._value.pop(key, None)
4322 if not isinstance(value, spec.__class__):
4323 raise InvalidValueType((spec.__class__,))
4324 value = spec(value=value)
4325 if spec.default is not None and value == spec.default:
4326 self._value.pop(key, None)
4328 self._value[key] = value
4330 def __getitem__(self, key):
4331 value = self._value.get(key)
4332 if value is not None:
4334 spec = self.specs.get(key)
4336 raise ObjUnknown(key)
4337 if spec.default is not None:
4341 def _encoded_values(self):
4343 for name, spec in self.specs.items():
4344 value = self._value.get(name)
4348 raise ObjNotReady(name)
4349 raws.append(value.encode())
4353 v = b"".join(self._encoded_values())
4354 return b"".join((self.tag, len_encode(len(v)), v))
4356 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4358 t, tlen, lv = tag_strip(tlv)
4359 except DecodeError as err:
4360 raise err.__class__(
4362 klass=self.__class__,
4363 decode_path=decode_path,
4368 klass=self.__class__,
4369 decode_path=decode_path,
4376 l, llen, v = len_decode(lv)
4377 except LenIndefForm as err:
4378 if not ctx.get("bered", False):
4379 raise err.__class__(
4381 klass=self.__class__,
4382 decode_path=decode_path,
4385 l, llen, v = 0, 1, lv[1:]
4387 except DecodeError as err:
4388 raise err.__class__(
4390 klass=self.__class__,
4391 decode_path=decode_path,
4395 raise NotEnoughData(
4396 "encoded length is longer than data",
4397 klass=self.__class__,
4398 decode_path=decode_path,
4402 v, tail = v[:l], v[l:]
4404 sub_offset = offset + tlen + llen
4406 for name, spec in self.specs.items():
4407 if spec.optional and (
4408 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4412 sub_decode_path = decode_path + (name,)
4414 value, v_tail = spec.decode(
4418 decode_path=sub_decode_path,
4426 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4427 if defined is not None:
4428 defined_by, defined_spec = defined
4429 if issubclass(value.__class__, SequenceOf):
4430 for i, _value in enumerate(value):
4431 sub_sub_decode_path = sub_decode_path + (
4433 DecodePathDefBy(defined_by),
4435 defined_value, defined_tail = defined_spec.decode(
4436 memoryview(bytes(_value)),
4438 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4439 if value.expled else (value.tlen + value.llen)
4442 decode_path=sub_sub_decode_path,
4445 if len(defined_tail) > 0:
4448 klass=self.__class__,
4449 decode_path=sub_sub_decode_path,
4452 _value.defined = (defined_by, defined_value)
4454 defined_value, defined_tail = defined_spec.decode(
4455 memoryview(bytes(value)),
4457 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4458 if value.expled else (value.tlen + value.llen)
4461 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4464 if len(defined_tail) > 0:
4467 klass=self.__class__,
4468 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4471 value.defined = (defined_by, defined_value)
4473 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4475 sub_offset += value_len
4477 if spec.default is not None and value == spec.default:
4478 if ctx.get("strict_default_existence", False):
4480 "DEFAULT value met",
4481 klass=self.__class__,
4482 decode_path=sub_decode_path,
4487 values[name] = value
4489 spec_defines = getattr(spec, "defines", ())
4490 if len(spec_defines) == 0:
4491 defines_by_path = ctx.get("defines_by_path", ())
4492 if len(defines_by_path) > 0:
4493 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4494 if spec_defines is not None and len(spec_defines) > 0:
4495 for rel_path, schema in spec_defines:
4496 defined = schema.get(value, None)
4497 if defined is not None:
4498 ctx.setdefault("defines", []).append((
4499 abs_decode_path(sub_decode_path[:-1], rel_path),
4503 if v[:EOC_LEN].tobytes() != EOC:
4506 klass=self.__class__,
4507 decode_path=decode_path,
4515 klass=self.__class__,
4516 decode_path=decode_path,
4519 obj = self.__class__(
4523 default=self.default,
4524 optional=self.optional,
4525 _decoded=(offset, llen, vlen),
4528 obj.lenindef = lenindef
4532 value = pp_console_row(next(self.pps()))
4534 for name in self.specs:
4535 _value = self._value.get(name)
4538 cols.append(repr(_value))
4539 return "%s[%s]" % (value, ", ".join(cols))
4541 def pps(self, decode_path=()):
4543 asn1_type_name=self.asn1_type_name,
4544 obj_name=self.__class__.__name__,
4545 decode_path=decode_path,
4546 optional=self.optional,
4547 default=self == self.default,
4548 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4549 expl=None if self._expl is None else tag_decode(self._expl),
4554 expl_offset=self.expl_offset if self.expled else None,
4555 expl_tlen=self.expl_tlen if self.expled else None,
4556 expl_llen=self.expl_llen if self.expled else None,
4557 expl_vlen=self.expl_vlen if self.expled else None,
4558 expl_lenindef=self.expl_lenindef,
4559 lenindef=self.lenindef,
4561 for name in self.specs:
4562 value = self._value.get(name)
4565 yield value.pps(decode_path=decode_path + (name,))
4568 class Set(Sequence):
4569 """``SET`` structure type
4571 Its usage is identical to :py:class:`pyderasn.Sequence`.
4574 tag_default = tag_encode(form=TagFormConstructed, num=17)
4575 asn1_type_name = "SET"
4578 raws = self._encoded_values()
4581 return b"".join((self.tag, len_encode(len(v)), v))
4583 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4585 t, tlen, lv = tag_strip(tlv)
4586 except DecodeError as err:
4587 raise err.__class__(
4589 klass=self.__class__,
4590 decode_path=decode_path,
4595 klass=self.__class__,
4596 decode_path=decode_path,
4603 l, llen, v = len_decode(lv)
4604 except LenIndefForm as err:
4605 if not ctx.get("bered", False):
4606 raise err.__class__(
4608 klass=self.__class__,
4609 decode_path=decode_path,
4612 l, llen, v = 0, 1, lv[1:]
4614 except DecodeError as err:
4615 raise err.__class__(
4617 klass=self.__class__,
4618 decode_path=decode_path,
4622 raise NotEnoughData(
4623 "encoded length is longer than data",
4624 klass=self.__class__,
4628 v, tail = v[:l], v[l:]
4630 sub_offset = offset + tlen + llen
4632 specs_items = self.specs.items
4634 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4636 for name, spec in specs_items():
4637 sub_decode_path = decode_path + (name,)
4643 decode_path=sub_decode_path,
4652 klass=self.__class__,
4653 decode_path=decode_path,
4656 value, v_tail = spec.decode(
4660 decode_path=sub_decode_path,
4663 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4664 sub_offset += value_len
4667 if spec.default is None or value != spec.default: # pragma: no cover
4668 # SeqMixing.test_encoded_default_accepted covers that place
4669 values[name] = value
4670 obj = self.__class__(
4674 default=self.default,
4675 optional=self.optional,
4676 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4681 msg="not all values are ready",
4682 klass=self.__class__,
4683 decode_path=decode_path,
4686 obj.lenindef = lenindef
4687 return obj, (v[EOC_LEN:] if lenindef else tail)
4690 class SequenceOf(Obj):
4691 """``SEQUENCE OF`` sequence type
4693 For that kind of type you must specify the object it will carry on
4694 (bounds are for example here, not required)::
4696 class Ints(SequenceOf):
4701 >>> ints.append(Integer(123))
4702 >>> ints.append(Integer(234))
4704 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4705 >>> [int(i) for i in ints]
4707 >>> ints.append(Integer(345))
4708 Traceback (most recent call last):
4709 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4712 >>> ints[1] = Integer(345)
4714 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4716 Also you can initialize sequence with preinitialized values:
4718 >>> ints = Ints([Integer(123), Integer(234)])
4720 __slots__ = ("spec", "_bound_min", "_bound_max")
4721 tag_default = tag_encode(form=TagFormConstructed, num=16)
4722 asn1_type_name = "SEQUENCE OF"
4735 super(SequenceOf, self).__init__(
4743 schema = getattr(self, "schema", None)
4745 raise ValueError("schema must be specified")
4747 self._bound_min, self._bound_max = getattr(
4751 ) if bounds is None else bounds
4753 if value is not None:
4754 self._value = self._value_sanitize(value)
4755 if default is not None:
4756 default_value = self._value_sanitize(default)
4757 default_obj = self.__class__(
4762 default_obj._value = default_value
4763 self.default = default_obj
4765 self._value = default_obj.copy()._value
4767 def _value_sanitize(self, value):
4768 if issubclass(value.__class__, SequenceOf):
4769 value = value._value
4770 elif hasattr(value, "__iter__"):
4773 raise InvalidValueType((self.__class__, iter))
4774 if not self._bound_min <= len(value) <= self._bound_max:
4775 raise BoundsError(self._bound_min, len(value), self._bound_max)
4777 if not isinstance(v, self.spec.__class__):
4778 raise InvalidValueType((self.spec.__class__,))
4783 return all(v.ready for v in self._value)
4786 obj = self.__class__(schema=self.spec)
4787 obj._bound_min = self._bound_min
4788 obj._bound_max = self._bound_max
4790 obj._expl = self._expl
4791 obj.default = self.default
4792 obj.optional = self.optional
4793 obj.offset = self.offset
4794 obj.llen = self.llen
4795 obj.vlen = self.vlen
4796 obj._value = [v.copy() for v in self._value]
4799 def __eq__(self, their):
4800 if isinstance(their, self.__class__):
4802 self.spec == their.spec and
4803 self.tag == their.tag and
4804 self._expl == their._expl and
4805 self._value == their._value
4807 if hasattr(their, "__iter__"):
4808 return self._value == list(their)
4820 return self.__class__(
4824 (self._bound_min, self._bound_max)
4825 if bounds is None else bounds
4827 impl=self.tag if impl is None else impl,
4828 expl=self._expl if expl is None else expl,
4829 default=self.default if default is None else default,
4830 optional=self.optional if optional is None else optional,
4833 def __contains__(self, key):
4834 return key in self._value
4836 def append(self, value):
4837 if not isinstance(value, self.spec.__class__):
4838 raise InvalidValueType((self.spec.__class__,))
4839 if len(self._value) + 1 > self._bound_max:
4842 len(self._value) + 1,
4845 self._value.append(value)
4848 self._assert_ready()
4849 return iter(self._value)
4852 self._assert_ready()
4853 return len(self._value)
4855 def __setitem__(self, key, value):
4856 if not isinstance(value, self.spec.__class__):
4857 raise InvalidValueType((self.spec.__class__,))
4858 self._value[key] = self.spec(value=value)
4860 def __getitem__(self, key):
4861 return self._value[key]
4863 def _encoded_values(self):
4864 return [v.encode() for v in self._value]
4867 v = b"".join(self._encoded_values())
4868 return b"".join((self.tag, len_encode(len(v)), v))
4870 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4872 t, tlen, lv = tag_strip(tlv)
4873 except DecodeError as err:
4874 raise err.__class__(
4876 klass=self.__class__,
4877 decode_path=decode_path,
4882 klass=self.__class__,
4883 decode_path=decode_path,
4890 l, llen, v = len_decode(lv)
4891 except LenIndefForm as err:
4892 if not ctx.get("bered", False):
4893 raise err.__class__(
4895 klass=self.__class__,
4896 decode_path=decode_path,
4899 l, llen, v = 0, 1, lv[1:]
4901 except DecodeError as err:
4902 raise err.__class__(
4904 klass=self.__class__,
4905 decode_path=decode_path,
4909 raise NotEnoughData(
4910 "encoded length is longer than data",
4911 klass=self.__class__,
4912 decode_path=decode_path,
4916 v, tail = v[:l], v[l:]
4918 sub_offset = offset + tlen + llen
4922 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4924 value, v_tail = spec.decode(
4928 decode_path=decode_path + (str(len(_value)),),
4931 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4932 sub_offset += value_len
4935 _value.append(value)
4936 obj = self.__class__(
4939 bounds=(self._bound_min, self._bound_max),
4942 default=self.default,
4943 optional=self.optional,
4944 _decoded=(offset, llen, vlen),
4946 obj.lenindef = lenindef
4947 return obj, (v[EOC_LEN:] if lenindef else tail)
4951 pp_console_row(next(self.pps())),
4952 ", ".join(repr(v) for v in self._value),
4955 def pps(self, decode_path=()):
4957 asn1_type_name=self.asn1_type_name,
4958 obj_name=self.__class__.__name__,
4959 decode_path=decode_path,
4960 optional=self.optional,
4961 default=self == self.default,
4962 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4963 expl=None if self._expl is None else tag_decode(self._expl),
4968 expl_offset=self.expl_offset if self.expled else None,
4969 expl_tlen=self.expl_tlen if self.expled else None,
4970 expl_llen=self.expl_llen if self.expled else None,
4971 expl_vlen=self.expl_vlen if self.expled else None,
4972 expl_lenindef=self.expl_lenindef,
4973 lenindef=self.lenindef,
4975 for i, value in enumerate(self._value):
4976 yield value.pps(decode_path=decode_path + (str(i),))
4979 class SetOf(SequenceOf):
4980 """``SET OF`` sequence type
4982 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4985 tag_default = tag_encode(form=TagFormConstructed, num=17)
4986 asn1_type_name = "SET OF"
4989 raws = self._encoded_values()
4992 return b"".join((self.tag, len_encode(len(v)), v))
4995 def obj_by_path(pypath): # pragma: no cover
4996 """Import object specified as string Python path
4998 Modules must be separated from classes/functions with ``:``.
5000 >>> obj_by_path("foo.bar:Baz")
5001 <class 'foo.bar.Baz'>
5002 >>> obj_by_path("foo.bar:Baz.boo")
5003 <classmethod 'foo.bar.Baz.boo'>
5005 mod, objs = pypath.rsplit(":", 1)
5006 from importlib import import_module
5007 obj = import_module(mod)
5008 for obj_name in objs.split("."):
5009 obj = getattr(obj, obj_name)
5013 def generic_decoder(): # pragma: no cover
5014 # All of this below is a big hack with self references
5015 choice = PrimitiveTypes()
5016 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5017 choice.specs["SetOf"] = SetOf(schema=choice)
5019 choice.specs["SequenceOf%d" % i] = SequenceOf(
5023 choice.specs["Any"] = Any()
5025 # Class name equals to type name, to omit it from output
5026 class SEQUENCEOF(SequenceOf):
5030 def pprint_any(obj, oids=None, with_colours=False):
5031 def _pprint_pps(pps):
5033 if hasattr(pp, "_fields"):
5034 if pp.asn1_type_name == Choice.asn1_type_name:
5036 pp_kwargs = pp._asdict()
5037 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5038 pp = _pp(**pp_kwargs)
5039 yield pp_console_row(
5044 with_colours=with_colours,
5046 for row in pp_console_blob(pp):
5049 for row in _pprint_pps(pp):
5051 return "\n".join(_pprint_pps(obj.pps()))
5052 return SEQUENCEOF(), pprint_any
5055 def main(): # pragma: no cover
5057 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5058 parser.add_argument(
5062 help="Skip that number of bytes from the beginning",
5064 parser.add_argument(
5066 help="Python path to dictionary with OIDs",
5068 parser.add_argument(
5070 help="Python path to schema definition to use",
5072 parser.add_argument(
5073 "--defines-by-path",
5074 help="Python path to decoder's defines_by_path",
5076 parser.add_argument(
5078 action='store_true',
5079 help="Disallow BER encoding",
5081 parser.add_argument(
5083 type=argparse.FileType("rb"),
5084 help="Path to DER file you want to decode",
5086 args = parser.parse_args()
5087 args.DERFile.seek(args.skip)
5088 der = memoryview(args.DERFile.read())
5089 args.DERFile.close()
5090 oids = obj_by_path(args.oids) if args.oids else {}
5092 schema = obj_by_path(args.schema)
5093 from functools import partial
5094 pprinter = partial(pprint, big_blobs=True)
5096 schema, pprinter = generic_decoder()
5097 ctx = {"bered": not args.nobered}
5098 if args.defines_by_path is not None:
5099 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5100 obj, tail = schema().decode(der, ctx=ctx)
5104 with_colours=True if environ.get("NO_COLOR") is None else False,
5107 print("\nTrailing data: %s" % hexenc(tail))
5110 if __name__ == "__main__":