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.UTCTime
438 :members: __init__, todatetime
442 .. autoclass:: pyderasn.GeneralizedTime
449 .. autoclass:: pyderasn.Choice
454 .. autoclass:: PrimitiveTypes
458 .. autoclass:: pyderasn.Any
466 .. autoclass:: pyderasn.Sequence
471 .. autoclass:: pyderasn.Set
476 .. autoclass:: pyderasn.SequenceOf
481 .. autoclass:: pyderasn.SetOf
487 .. autofunction:: pyderasn.abs_decode_path
488 .. autofunction:: pyderasn.hexenc
489 .. autofunction:: pyderasn.hexdec
490 .. autofunction:: pyderasn.tag_encode
491 .. autofunction:: pyderasn.tag_decode
492 .. autofunction:: pyderasn.tag_ctxp
493 .. autofunction:: pyderasn.tag_ctxc
494 .. autoclass:: pyderasn.Obj
497 from codecs import getdecoder
498 from codecs import getencoder
499 from collections import namedtuple
500 from collections import OrderedDict
501 from datetime import datetime
502 from math import ceil
503 from os import environ
504 from string import digits
506 from six import add_metaclass
507 from six import binary_type
508 from six import byte2int
509 from six import indexbytes
510 from six import int2byte
511 from six import integer_types
512 from six import iterbytes
514 from six import string_types
515 from six import text_type
516 from six.moves import xrange as six_xrange
520 from termcolor import colored
522 def colored(what, *args):
565 "TagClassApplication",
569 "TagFormConstructed",
580 TagClassUniversal = 0
581 TagClassApplication = 1 << 6
582 TagClassContext = 1 << 7
583 TagClassPrivate = 1 << 6 | 1 << 7
585 TagFormConstructed = 1 << 5
588 TagClassApplication: "APPLICATION ",
589 TagClassPrivate: "PRIVATE ",
590 TagClassUniversal: "UNIV ",
596 ########################################################################
598 ########################################################################
600 class DecodeError(Exception):
601 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
603 :param str msg: reason of decode failing
604 :param klass: optional exact DecodeError inherited class (like
605 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
606 :py:exc:`InvalidLength`)
607 :param decode_path: tuple of strings. It contains human
608 readable names of the fields through which
609 decoding process has passed
610 :param int offset: binary offset where failure happened
612 super(DecodeError, self).__init__()
615 self.decode_path = decode_path
621 "" if self.klass is None else self.klass.__name__,
623 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
624 if len(self.decode_path) > 0 else ""
626 ("(at %d)" % self.offset) if self.offset > 0 else "",
632 return "%s(%s)" % (self.__class__.__name__, self)
635 class NotEnoughData(DecodeError):
639 class LenIndefForm(DecodeError):
643 class TagMismatch(DecodeError):
647 class InvalidLength(DecodeError):
651 class InvalidOID(DecodeError):
655 class ObjUnknown(ValueError):
656 def __init__(self, name):
657 super(ObjUnknown, self).__init__()
661 return "object is unknown: %s" % self.name
664 return "%s(%s)" % (self.__class__.__name__, self)
667 class ObjNotReady(ValueError):
668 def __init__(self, name):
669 super(ObjNotReady, self).__init__()
673 return "object is not ready: %s" % self.name
676 return "%s(%s)" % (self.__class__.__name__, self)
679 class InvalidValueType(ValueError):
680 def __init__(self, expected_types):
681 super(InvalidValueType, self).__init__()
682 self.expected_types = expected_types
685 return "invalid value type, expected: %s" % ", ".join(
686 [repr(t) for t in self.expected_types]
690 return "%s(%s)" % (self.__class__.__name__, self)
693 class BoundsError(ValueError):
694 def __init__(self, bound_min, value, bound_max):
695 super(BoundsError, self).__init__()
696 self.bound_min = bound_min
698 self.bound_max = bound_max
701 return "unsatisfied bounds: %s <= %s <= %s" % (
708 return "%s(%s)" % (self.__class__.__name__, self)
711 ########################################################################
713 ########################################################################
715 _hexdecoder = getdecoder("hex")
716 _hexencoder = getencoder("hex")
720 """Binary data to hexadecimal string convert
722 return _hexdecoder(data)[0]
726 """Hexadecimal string to binary data convert
728 return _hexencoder(data)[0].decode("ascii")
731 def int_bytes_len(num, byte_len=8):
734 return int(ceil(float(num.bit_length()) / byte_len))
737 def zero_ended_encode(num):
738 octets = bytearray(int_bytes_len(num, 7))
740 octets[i] = num & 0x7F
744 octets[i] = 0x80 | (num & 0x7F)
750 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
751 """Encode tag to binary form
753 :param int num: tag's number
754 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
755 :py:data:`pyderasn.TagClassContext`,
756 :py:data:`pyderasn.TagClassApplication`,
757 :py:data:`pyderasn.TagClassPrivate`)
758 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
759 :py:data:`pyderasn.TagFormConstructed`)
763 return int2byte(klass | form | num)
764 # [XX|X|11111][1.......][1.......] ... [0.......]
765 return int2byte(klass | form | 31) + zero_ended_encode(num)
769 """Decode tag from binary form
773 No validation is performed, assuming that it has already passed.
775 It returns tuple with three integers, as
776 :py:func:`pyderasn.tag_encode` accepts.
778 first_octet = byte2int(tag)
779 klass = first_octet & 0xC0
780 form = first_octet & 0x20
781 if first_octet & 0x1F < 0x1F:
782 return (klass, form, first_octet & 0x1F)
784 for octet in iterbytes(tag[1:]):
787 return (klass, form, num)
791 """Create CONTEXT PRIMITIVE tag
793 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
797 """Create CONTEXT CONSTRUCTED tag
799 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
803 """Take off tag from the data
805 :returns: (encoded tag, tag length, remaining data)
808 raise NotEnoughData("no data at all")
809 if byte2int(data) & 0x1F < 31:
810 return data[:1], 1, data[1:]
815 raise DecodeError("unfinished tag")
816 if indexbytes(data, i) & 0x80 == 0:
819 return data[:i], i, data[i:]
825 octets = bytearray(int_bytes_len(l) + 1)
826 octets[0] = 0x80 | (len(octets) - 1)
827 for i in six_xrange(len(octets) - 1, 0, -1):
833 def len_decode(data):
836 :returns: (decoded length, length's length, remaining data)
837 :raises LenIndefForm: if indefinite form encoding is met
840 raise NotEnoughData("no data at all")
841 first_octet = byte2int(data)
842 if first_octet & 0x80 == 0:
843 return first_octet, 1, data[1:]
844 octets_num = first_octet & 0x7F
845 if octets_num + 1 > len(data):
846 raise NotEnoughData("encoded length is longer than data")
849 if byte2int(data[1:]) == 0:
850 raise DecodeError("leading zeros")
852 for v in iterbytes(data[1:1 + octets_num]):
855 raise DecodeError("long form instead of short one")
856 return l, 1 + octets_num, data[1 + octets_num:]
859 ########################################################################
861 ########################################################################
863 class AutoAddSlots(type):
864 def __new__(mcs, name, bases, _dict):
865 _dict["__slots__"] = _dict.get("__slots__", ())
866 return type.__new__(mcs, name, bases, _dict)
869 @add_metaclass(AutoAddSlots)
871 """Common ASN.1 object class
873 All ASN.1 types are inherited from it. It has metaclass that
874 automatically adds ``__slots__`` to all inherited classes.
898 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
899 self._expl = getattr(self, "expl", None) if expl is None else expl
900 if self.tag != self.tag_default and self._expl is not None:
902 "implicit and explicit tags can not be set simultaneously"
904 if default is not None:
906 self.optional = optional
907 self.offset, self.llen, self.vlen = _decoded
909 self.expl_lenindef = False
910 self.lenindef = False
914 def ready(self): # pragma: no cover
915 """Is object ready to be encoded?
917 raise NotImplementedError()
919 def _assert_ready(self):
921 raise ObjNotReady(self.__class__.__name__)
925 """Is object decoded?
927 return (self.llen + self.vlen) > 0
929 def copy(self): # pragma: no cover
930 """Make a copy of object, safe to be mutated
932 raise NotImplementedError()
940 return self.tlen + self.llen + self.vlen
942 def __str__(self): # pragma: no cover
943 return self.__bytes__() if PY2 else self.__unicode__()
945 def __ne__(self, their):
946 return not(self == their)
948 def __gt__(self, their): # pragma: no cover
949 return not(self < their)
951 def __le__(self, their): # pragma: no cover
952 return (self == their) or (self < their)
954 def __ge__(self, their): # pragma: no cover
955 return (self == their) or (self > their)
957 def _encode(self): # pragma: no cover
958 raise NotImplementedError()
960 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
961 raise NotImplementedError()
965 if self._expl is None:
967 return b"".join((self._expl, len_encode(len(raw)), raw))
980 :param data: either binary or memoryview
981 :param int offset: initial data's offset
982 :param bool leavemm: do we need to leave memoryview of remaining
983 data as is, or convert it to bytes otherwise
984 :param ctx: optional :ref:`context <ctx>` governing decoding process.
985 :param tag_only: decode only the tag, without length and contents
986 (used only in Choice and Set structures, trying to
987 determine if tag satisfies the scheme)
988 :returns: (Obj, remaining data)
992 tlv = memoryview(data)
993 if self._expl is None:
994 result = self._decode(
997 decode_path=decode_path,
1006 t, tlen, lv = tag_strip(tlv)
1007 except DecodeError as err:
1008 raise err.__class__(
1010 klass=self.__class__,
1011 decode_path=decode_path,
1016 klass=self.__class__,
1017 decode_path=decode_path,
1021 l, llen, v = len_decode(lv)
1022 except LenIndefForm as err:
1023 if not ctx.get("bered", False):
1024 raise err.__class__(
1026 klass=self.__class__,
1027 decode_path=decode_path,
1031 offset += tlen + llen
1032 result = self._decode(
1035 decode_path=decode_path,
1042 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1043 if eoc_expected.tobytes() != EOC:
1046 decode_path=decode_path,
1050 obj.expl_lenindef = True
1051 except DecodeError as err:
1052 raise err.__class__(
1054 klass=self.__class__,
1055 decode_path=decode_path,
1060 raise NotEnoughData(
1061 "encoded length is longer than data",
1062 klass=self.__class__,
1063 decode_path=decode_path,
1066 result = self._decode(
1068 offset=offset + tlen + llen,
1069 decode_path=decode_path,
1076 return obj, (tail if leavemm else tail.tobytes())
1080 return self._expl is not None
1087 def expl_tlen(self):
1088 return len(self._expl)
1091 def expl_llen(self):
1092 if self.expl_lenindef:
1094 return len(len_encode(self.tlvlen))
1097 def expl_offset(self):
1098 return self.offset - self.expl_tlen - self.expl_llen
1101 def expl_vlen(self):
1105 def expl_tlvlen(self):
1106 return self.expl_tlen + self.expl_llen + self.expl_vlen
1109 class DecodePathDefBy(object):
1110 """DEFINED BY representation inside decode path
1112 __slots__ = ("defined_by",)
1114 def __init__(self, defined_by):
1115 self.defined_by = defined_by
1117 def __ne__(self, their):
1118 return not(self == their)
1120 def __eq__(self, their):
1121 if not isinstance(their, self.__class__):
1123 return self.defined_by == their.defined_by
1126 return "DEFINED BY " + str(self.defined_by)
1129 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1132 ########################################################################
1134 ########################################################################
1136 PP = namedtuple("PP", (
1161 asn1_type_name="unknown",
1178 expl_lenindef=False,
1206 def _colorize(what, colour, with_colours, attrs=("bold",)):
1207 return colored(what, colour, attrs=attrs) if with_colours else what
1222 " " if pp.expl_offset is None else
1223 ("-%d" % (pp.offset - pp.expl_offset))
1226 cols.append(_colorize(col, "red", with_colours, ()))
1227 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1228 col = _colorize(col, "green", with_colours, ())
1230 if pp.expl_lenindef:
1235 " " if ber_deoffset == 0 else
1236 _colorize(("-%d" % ber_deoffset), "red", with_colours)
1239 if len(pp.decode_path) > 0:
1240 cols.append(" ." * (len(pp.decode_path)))
1241 ent = pp.decode_path[-1]
1242 if isinstance(ent, DecodePathDefBy):
1243 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1244 value = str(ent.defined_by)
1246 oids is not None and
1247 ent.defined_by.asn1_type_name ==
1248 ObjectIdentifier.asn1_type_name and
1251 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1253 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1255 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1256 if pp.expl is not None:
1257 klass, _, num = pp.expl
1258 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1259 cols.append(_colorize(col, "blue", with_colours))
1260 if pp.impl is not None:
1261 klass, _, num = pp.impl
1262 col = "[%s%d]" % (TagClassReprs[klass], num)
1263 cols.append(_colorize(col, "blue", with_colours))
1264 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1265 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1267 cols.append(_colorize("BER", "red", with_colours))
1268 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1269 if pp.value is not None:
1271 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1273 oids is not None and
1274 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1277 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1279 if isinstance(pp.blob, binary_type):
1280 cols.append(hexenc(pp.blob))
1281 elif isinstance(pp.blob, tuple):
1282 cols.append(", ".join(pp.blob))
1284 cols.append(_colorize("OPTIONAL", "red", with_colours))
1286 cols.append(_colorize("DEFAULT", "red", with_colours))
1287 return " ".join(cols)
1290 def pp_console_blob(pp):
1291 cols = [" " * len("XXXXXYY [X,X,XXXX]YY")]
1292 if len(pp.decode_path) > 0:
1293 cols.append(" ." * (len(pp.decode_path) + 1))
1294 if isinstance(pp.blob, binary_type):
1295 blob = hexenc(pp.blob).upper()
1296 for i in range(0, len(blob), 32):
1297 chunk = blob[i:i + 32]
1298 yield " ".join(cols + [":".join(
1299 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1301 elif isinstance(pp.blob, tuple):
1302 yield " ".join(cols + [", ".join(pp.blob)])
1305 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1306 """Pretty print object
1308 :param Obj obj: object you want to pretty print
1309 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1310 from it is met, then its humand readable form is printed
1311 :param big_blobs: if large binary objects are met (like OctetString
1312 values), do we need to print them too, on separate
1314 :param with_colours: colourize output, if ``termcolor`` library
1317 def _pprint_pps(pps):
1319 if hasattr(pp, "_fields"):
1321 yield pp_console_row(
1326 with_colours=with_colours,
1328 for row in pp_console_blob(pp):
1331 yield pp_console_row(
1336 with_colours=with_colours,
1339 for row in _pprint_pps(pp):
1341 return "\n".join(_pprint_pps(obj.pps()))
1344 ########################################################################
1345 # ASN.1 primitive types
1346 ########################################################################
1349 """``BOOLEAN`` boolean type
1351 >>> b = Boolean(True)
1353 >>> b == Boolean(True)
1359 tag_default = tag_encode(1)
1360 asn1_type_name = "BOOLEAN"
1372 :param value: set the value. Either boolean type, or
1373 :py:class:`pyderasn.Boolean` object
1374 :param bytes impl: override default tag with ``IMPLICIT`` one
1375 :param bytes expl: override default tag with ``EXPLICIT`` one
1376 :param default: set default value. Type same as in ``value``
1377 :param bool optional: is object ``OPTIONAL`` in sequence
1379 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1380 self._value = None if value is None else self._value_sanitize(value)
1381 if default is not None:
1382 default = self._value_sanitize(default)
1383 self.default = self.__class__(
1389 self._value = default
1391 def _value_sanitize(self, value):
1392 if issubclass(value.__class__, Boolean):
1394 if isinstance(value, bool):
1396 raise InvalidValueType((self.__class__, bool))
1400 return self._value is not None
1403 obj = self.__class__()
1404 obj._value = self._value
1406 obj._expl = self._expl
1407 obj.default = self.default
1408 obj.optional = self.optional
1409 obj.offset = self.offset
1410 obj.llen = self.llen
1411 obj.vlen = self.vlen
1414 def __nonzero__(self):
1415 self._assert_ready()
1419 self._assert_ready()
1422 def __eq__(self, their):
1423 if isinstance(their, bool):
1424 return self._value == their
1425 if not issubclass(their.__class__, Boolean):
1428 self._value == their._value and
1429 self.tag == their.tag and
1430 self._expl == their._expl
1441 return self.__class__(
1443 impl=self.tag if impl is None else impl,
1444 expl=self._expl if expl is None else expl,
1445 default=self.default if default is None else default,
1446 optional=self.optional if optional is None else optional,
1450 self._assert_ready()
1454 (b"\xFF" if self._value else b"\x00"),
1457 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1459 t, _, lv = tag_strip(tlv)
1460 except DecodeError as err:
1461 raise err.__class__(
1463 klass=self.__class__,
1464 decode_path=decode_path,
1469 klass=self.__class__,
1470 decode_path=decode_path,
1476 l, _, v = len_decode(lv)
1477 except DecodeError as err:
1478 raise err.__class__(
1480 klass=self.__class__,
1481 decode_path=decode_path,
1485 raise InvalidLength(
1486 "Boolean's length must be equal to 1",
1487 klass=self.__class__,
1488 decode_path=decode_path,
1492 raise NotEnoughData(
1493 "encoded length is longer than data",
1494 klass=self.__class__,
1495 decode_path=decode_path,
1498 first_octet = byte2int(v)
1500 if first_octet == 0:
1502 elif first_octet == 0xFF:
1504 elif ctx.get("bered", False):
1509 "unacceptable Boolean value",
1510 klass=self.__class__,
1511 decode_path=decode_path,
1514 obj = self.__class__(
1518 default=self.default,
1519 optional=self.optional,
1520 _decoded=(offset, 1, 1),
1526 return pp_console_row(next(self.pps()))
1528 def pps(self, decode_path=()):
1530 asn1_type_name=self.asn1_type_name,
1531 obj_name=self.__class__.__name__,
1532 decode_path=decode_path,
1533 value=str(self._value) if self.ready else None,
1534 optional=self.optional,
1535 default=self == self.default,
1536 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1537 expl=None if self._expl is None else tag_decode(self._expl),
1542 expl_offset=self.expl_offset if self.expled else None,
1543 expl_tlen=self.expl_tlen if self.expled else None,
1544 expl_llen=self.expl_llen if self.expled else None,
1545 expl_vlen=self.expl_vlen if self.expled else None,
1546 expl_lenindef=self.expl_lenindef,
1552 """``INTEGER`` integer type
1554 >>> b = Integer(-123)
1556 >>> b == Integer(-123)
1561 >>> Integer(2, bounds=(1, 3))
1563 >>> Integer(5, bounds=(1, 3))
1564 Traceback (most recent call last):
1565 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1569 class Version(Integer):
1576 >>> v = Version("v1")
1583 {'v3': 2, 'v1': 0, 'v2': 1}
1585 __slots__ = ("specs", "_bound_min", "_bound_max")
1586 tag_default = tag_encode(2)
1587 asn1_type_name = "INTEGER"
1601 :param value: set the value. Either integer type, named value
1602 (if ``schema`` is specified in the class), or
1603 :py:class:`pyderasn.Integer` object
1604 :param bounds: set ``(MIN, MAX)`` value constraint.
1605 (-inf, +inf) by default
1606 :param bytes impl: override default tag with ``IMPLICIT`` one
1607 :param bytes expl: override default tag with ``EXPLICIT`` one
1608 :param default: set default value. Type same as in ``value``
1609 :param bool optional: is object ``OPTIONAL`` in sequence
1611 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1613 specs = getattr(self, "schema", {}) if _specs is None else _specs
1614 self.specs = specs if isinstance(specs, dict) else dict(specs)
1615 self._bound_min, self._bound_max = getattr(
1618 (float("-inf"), float("+inf")),
1619 ) if bounds is None else bounds
1620 if value is not None:
1621 self._value = self._value_sanitize(value)
1622 if default is not None:
1623 default = self._value_sanitize(default)
1624 self.default = self.__class__(
1630 if self._value is None:
1631 self._value = default
1633 def _value_sanitize(self, value):
1634 if issubclass(value.__class__, Integer):
1635 value = value._value
1636 elif isinstance(value, integer_types):
1638 elif isinstance(value, str):
1639 value = self.specs.get(value)
1641 raise ObjUnknown("integer value: %s" % value)
1643 raise InvalidValueType((self.__class__, int, str))
1644 if not self._bound_min <= value <= self._bound_max:
1645 raise BoundsError(self._bound_min, value, self._bound_max)
1650 return self._value is not None
1653 obj = self.__class__(_specs=self.specs)
1654 obj._value = self._value
1655 obj._bound_min = self._bound_min
1656 obj._bound_max = self._bound_max
1658 obj._expl = self._expl
1659 obj.default = self.default
1660 obj.optional = self.optional
1661 obj.offset = self.offset
1662 obj.llen = self.llen
1663 obj.vlen = self.vlen
1667 self._assert_ready()
1668 return int(self._value)
1671 self._assert_ready()
1674 bytes(self._expl or b"") +
1675 str(self._value).encode("ascii"),
1678 def __eq__(self, their):
1679 if isinstance(their, integer_types):
1680 return self._value == their
1681 if not issubclass(their.__class__, Integer):
1684 self._value == their._value and
1685 self.tag == their.tag and
1686 self._expl == their._expl
1689 def __lt__(self, their):
1690 return self._value < their._value
1694 for name, value in self.specs.items():
1695 if value == self._value:
1707 return self.__class__(
1710 (self._bound_min, self._bound_max)
1711 if bounds is None else bounds
1713 impl=self.tag if impl is None else impl,
1714 expl=self._expl if expl is None else expl,
1715 default=self.default if default is None else default,
1716 optional=self.optional if optional is None else optional,
1721 self._assert_ready()
1725 octets = bytearray([0])
1729 octets = bytearray()
1731 octets.append((value & 0xFF) ^ 0xFF)
1733 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1736 octets = bytearray()
1738 octets.append(value & 0xFF)
1740 if octets[-1] & 0x80 > 0:
1743 octets = bytes(octets)
1745 bytes_len = ceil(value.bit_length() / 8) or 1
1748 octets = value.to_bytes(
1753 except OverflowError:
1757 return b"".join((self.tag, len_encode(len(octets)), octets))
1759 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1761 t, _, lv = tag_strip(tlv)
1762 except DecodeError as err:
1763 raise err.__class__(
1765 klass=self.__class__,
1766 decode_path=decode_path,
1771 klass=self.__class__,
1772 decode_path=decode_path,
1778 l, llen, v = len_decode(lv)
1779 except DecodeError as err:
1780 raise err.__class__(
1782 klass=self.__class__,
1783 decode_path=decode_path,
1787 raise NotEnoughData(
1788 "encoded length is longer than data",
1789 klass=self.__class__,
1790 decode_path=decode_path,
1794 raise NotEnoughData(
1796 klass=self.__class__,
1797 decode_path=decode_path,
1800 v, tail = v[:l], v[l:]
1801 first_octet = byte2int(v)
1803 second_octet = byte2int(v[1:])
1805 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1806 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1809 "non normalized integer",
1810 klass=self.__class__,
1811 decode_path=decode_path,
1816 if first_octet & 0x80 > 0:
1817 octets = bytearray()
1818 for octet in bytearray(v):
1819 octets.append(octet ^ 0xFF)
1820 for octet in octets:
1821 value = (value << 8) | octet
1825 for octet in bytearray(v):
1826 value = (value << 8) | octet
1828 value = int.from_bytes(v, byteorder="big", signed=True)
1830 obj = self.__class__(
1832 bounds=(self._bound_min, self._bound_max),
1835 default=self.default,
1836 optional=self.optional,
1838 _decoded=(offset, llen, l),
1840 except BoundsError as err:
1843 klass=self.__class__,
1844 decode_path=decode_path,
1850 return pp_console_row(next(self.pps()))
1852 def pps(self, decode_path=()):
1854 asn1_type_name=self.asn1_type_name,
1855 obj_name=self.__class__.__name__,
1856 decode_path=decode_path,
1857 value=(self.named or str(self._value)) if self.ready else None,
1858 optional=self.optional,
1859 default=self == self.default,
1860 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1861 expl=None if self._expl is None else tag_decode(self._expl),
1866 expl_offset=self.expl_offset if self.expled else None,
1867 expl_tlen=self.expl_tlen if self.expled else None,
1868 expl_llen=self.expl_llen if self.expled else None,
1869 expl_vlen=self.expl_vlen if self.expled else None,
1870 expl_lenindef=self.expl_lenindef,
1874 class BitString(Obj):
1875 """``BIT STRING`` bit string type
1877 >>> BitString(b"hello world")
1878 BIT STRING 88 bits 68656c6c6f20776f726c64
1881 >>> b == b"hello world"
1886 >>> BitString("'0A3B5F291CD'H")
1887 BIT STRING 44 bits 0a3b5f291cd0
1888 >>> b = BitString("'010110000000'B")
1889 BIT STRING 12 bits 5800
1892 >>> b[0], b[1], b[2], b[3]
1893 (False, True, False, True)
1897 [False, True, False, True, True, False, False, False, False, False, False, False]
1901 class KeyUsage(BitString):
1903 ("digitalSignature", 0),
1904 ("nonRepudiation", 1),
1905 ("keyEncipherment", 2),
1908 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1909 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1911 ['nonRepudiation', 'keyEncipherment']
1913 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1915 __slots__ = ("tag_constructed", "specs", "defined")
1916 tag_default = tag_encode(3)
1917 asn1_type_name = "BIT STRING"
1930 :param value: set the value. Either binary type, tuple of named
1931 values (if ``schema`` is specified in the class),
1932 string in ``'XXX...'B`` form, or
1933 :py:class:`pyderasn.BitString` object
1934 :param bytes impl: override default tag with ``IMPLICIT`` one
1935 :param bytes expl: override default tag with ``EXPLICIT`` one
1936 :param default: set default value. Type same as in ``value``
1937 :param bool optional: is object ``OPTIONAL`` in sequence
1939 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1940 specs = getattr(self, "schema", {}) if _specs is None else _specs
1941 self.specs = specs if isinstance(specs, dict) else dict(specs)
1942 self._value = None if value is None else self._value_sanitize(value)
1943 if default is not None:
1944 default = self._value_sanitize(default)
1945 self.default = self.__class__(
1951 self._value = default
1953 tag_klass, _, tag_num = tag_decode(self.tag)
1954 self.tag_constructed = tag_encode(
1956 form=TagFormConstructed,
1960 def _bits2octets(self, bits):
1961 if len(self.specs) > 0:
1962 bits = bits.rstrip("0")
1964 bits += "0" * ((8 - (bit_len % 8)) % 8)
1965 octets = bytearray(len(bits) // 8)
1966 for i in six_xrange(len(octets)):
1967 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1968 return bit_len, bytes(octets)
1970 def _value_sanitize(self, value):
1971 if issubclass(value.__class__, BitString):
1973 if isinstance(value, (string_types, binary_type)):
1975 isinstance(value, string_types) and
1976 value.startswith("'")
1978 if value.endswith("'B"):
1980 if not set(value) <= set(("0", "1")):
1981 raise ValueError("B's coding contains unacceptable chars")
1982 return self._bits2octets(value)
1983 elif value.endswith("'H"):
1987 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
1990 raise InvalidValueType((self.__class__, string_types, binary_type))
1991 elif isinstance(value, binary_type):
1992 return (len(value) * 8, value)
1994 raise InvalidValueType((self.__class__, string_types, binary_type))
1995 if isinstance(value, tuple):
1998 isinstance(value[0], integer_types) and
1999 isinstance(value[1], binary_type)
2004 bit = self.specs.get(name)
2006 raise ObjUnknown("BitString value: %s" % name)
2009 return self._bits2octets("")
2011 return self._bits2octets("".join(
2012 ("1" if bit in bits else "0")
2013 for bit in six_xrange(max(bits) + 1)
2015 raise InvalidValueType((self.__class__, binary_type, string_types))
2019 return self._value is not None
2022 obj = self.__class__(_specs=self.specs)
2024 if value is not None:
2025 value = (value[0], value[1])
2028 obj._expl = self._expl
2029 obj.default = self.default
2030 obj.optional = self.optional
2031 obj.offset = self.offset
2032 obj.llen = self.llen
2033 obj.vlen = self.vlen
2037 self._assert_ready()
2038 for i in six_xrange(self._value[0]):
2043 self._assert_ready()
2044 return self._value[0]
2046 def __bytes__(self):
2047 self._assert_ready()
2048 return self._value[1]
2050 def __eq__(self, their):
2051 if isinstance(their, bytes):
2052 return self._value[1] == their
2053 if not issubclass(their.__class__, BitString):
2056 self._value == their._value and
2057 self.tag == their.tag and
2058 self._expl == their._expl
2063 return [name for name, bit in self.specs.items() if self[bit]]
2073 return self.__class__(
2075 impl=self.tag if impl is None else impl,
2076 expl=self._expl if expl is None else expl,
2077 default=self.default if default is None else default,
2078 optional=self.optional if optional is None else optional,
2082 def __getitem__(self, key):
2083 if isinstance(key, int):
2084 bit_len, octets = self._value
2088 byte2int(memoryview(octets)[key // 8:]) >>
2091 if isinstance(key, string_types):
2092 value = self.specs.get(key)
2094 raise ObjUnknown("BitString value: %s" % key)
2096 raise InvalidValueType((int, str))
2099 self._assert_ready()
2100 bit_len, octets = self._value
2103 len_encode(len(octets) + 1),
2104 int2byte((8 - bit_len % 8) % 8),
2108 def _decode_chunk(self, lv, offset, decode_path, ctx):
2110 l, llen, v = len_decode(lv)
2111 except DecodeError as err:
2112 raise err.__class__(
2114 klass=self.__class__,
2115 decode_path=decode_path,
2119 raise NotEnoughData(
2120 "encoded length is longer than data",
2121 klass=self.__class__,
2122 decode_path=decode_path,
2126 raise NotEnoughData(
2128 klass=self.__class__,
2129 decode_path=decode_path,
2132 pad_size = byte2int(v)
2133 if l == 1 and pad_size != 0:
2135 "invalid empty value",
2136 klass=self.__class__,
2137 decode_path=decode_path,
2143 klass=self.__class__,
2144 decode_path=decode_path,
2147 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2150 klass=self.__class__,
2151 decode_path=decode_path,
2154 v, tail = v[:l], v[l:]
2155 obj = self.__class__(
2156 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2159 default=self.default,
2160 optional=self.optional,
2162 _decoded=(offset, llen, l),
2166 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2168 t, tlen, lv = tag_strip(tlv)
2169 except DecodeError as err:
2170 raise err.__class__(
2172 klass=self.__class__,
2173 decode_path=decode_path,
2179 return self._decode_chunk(lv, offset, decode_path, ctx)
2180 if t == self.tag_constructed:
2181 if not ctx.get("bered", False):
2183 msg="unallowed BER constructed encoding",
2184 decode_path=decode_path,
2191 l, llen, v = len_decode(lv)
2192 except LenIndefForm:
2193 llen, l, v = 1, 0, lv[1:]
2195 except DecodeError as err:
2196 raise err.__class__(
2198 klass=self.__class__,
2199 decode_path=decode_path,
2202 if l > 0 and l > len(v):
2203 raise NotEnoughData(
2204 "encoded length is longer than data",
2205 klass=self.__class__,
2206 decode_path=decode_path,
2209 if not lenindef and l == 0:
2210 raise NotEnoughData(
2212 klass=self.__class__,
2213 decode_path=decode_path,
2217 sub_offset = offset + tlen + llen
2221 if v[:EOC_LEN].tobytes() == EOC:
2228 msg="chunk out of bounds",
2229 decode_path=len(chunks) - 1,
2230 offset=chunks[-1].offset,
2232 sub_decode_path = decode_path + (str(len(chunks)),)
2234 chunk, v_tail = BitString().decode(
2237 decode_path=sub_decode_path,
2243 msg="expected BitString encoded chunk",
2244 decode_path=sub_decode_path,
2247 chunks.append(chunk)
2248 sub_offset += chunk.tlvlen
2249 vlen += chunk.tlvlen
2251 if len(chunks) == 0:
2254 decode_path=decode_path,
2259 for chunk_i, chunk in enumerate(chunks[:-1]):
2260 if chunk.bit_len % 8 != 0:
2262 msg="BitString chunk is not multiple of 8 bit",
2263 decode_path=decode_path + (str(chunk_i),),
2264 offset=chunk.offset,
2266 values.append(bytes(chunk))
2267 bit_len += chunk.bit_len
2268 chunk_last = chunks[-1]
2269 values.append(bytes(chunk_last))
2270 bit_len += chunk_last.bit_len
2271 obj = self.__class__(
2272 value=(bit_len, b"".join(values)),
2275 default=self.default,
2276 optional=self.optional,
2278 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2280 obj.lenindef = lenindef
2282 return obj, (v[EOC_LEN:] if lenindef else v)
2284 klass=self.__class__,
2285 decode_path=decode_path,
2290 return pp_console_row(next(self.pps()))
2292 def pps(self, decode_path=()):
2296 bit_len, blob = self._value
2297 value = "%d bits" % bit_len
2298 if len(self.specs) > 0:
2299 blob = tuple(self.named)
2301 asn1_type_name=self.asn1_type_name,
2302 obj_name=self.__class__.__name__,
2303 decode_path=decode_path,
2306 optional=self.optional,
2307 default=self == self.default,
2308 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2309 expl=None if self._expl is None else tag_decode(self._expl),
2314 expl_offset=self.expl_offset if self.expled else None,
2315 expl_tlen=self.expl_tlen if self.expled else None,
2316 expl_llen=self.expl_llen if self.expled else None,
2317 expl_vlen=self.expl_vlen if self.expled else None,
2318 expl_lenindef=self.expl_lenindef,
2319 lenindef=self.lenindef,
2322 defined_by, defined = self.defined or (None, None)
2323 if defined_by is not None:
2325 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2329 class OctetString(Obj):
2330 """``OCTET STRING`` binary string type
2332 >>> s = OctetString(b"hello world")
2333 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2334 >>> s == OctetString(b"hello world")
2339 >>> OctetString(b"hello", bounds=(4, 4))
2340 Traceback (most recent call last):
2341 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2342 >>> OctetString(b"hell", bounds=(4, 4))
2343 OCTET STRING 4 bytes 68656c6c
2345 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2346 tag_default = tag_encode(4)
2347 asn1_type_name = "OCTET STRING"
2360 :param value: set the value. Either binary type, or
2361 :py:class:`pyderasn.OctetString` object
2362 :param bounds: set ``(MIN, MAX)`` value size constraint.
2363 (-inf, +inf) by default
2364 :param bytes impl: override default tag with ``IMPLICIT`` one
2365 :param bytes expl: override default tag with ``EXPLICIT`` one
2366 :param default: set default value. Type same as in ``value``
2367 :param bool optional: is object ``OPTIONAL`` in sequence
2369 super(OctetString, self).__init__(
2377 self._bound_min, self._bound_max = getattr(
2381 ) if bounds is None else bounds
2382 if value is not None:
2383 self._value = self._value_sanitize(value)
2384 if default is not None:
2385 default = self._value_sanitize(default)
2386 self.default = self.__class__(
2391 if self._value is None:
2392 self._value = default
2394 tag_klass, _, tag_num = tag_decode(self.tag)
2395 self.tag_constructed = tag_encode(
2397 form=TagFormConstructed,
2401 def _value_sanitize(self, value):
2402 if issubclass(value.__class__, OctetString):
2403 value = value._value
2404 elif isinstance(value, binary_type):
2407 raise InvalidValueType((self.__class__, bytes))
2408 if not self._bound_min <= len(value) <= self._bound_max:
2409 raise BoundsError(self._bound_min, len(value), self._bound_max)
2414 return self._value is not None
2417 obj = self.__class__()
2418 obj._value = self._value
2419 obj._bound_min = self._bound_min
2420 obj._bound_max = self._bound_max
2422 obj._expl = self._expl
2423 obj.default = self.default
2424 obj.optional = self.optional
2425 obj.offset = self.offset
2426 obj.llen = self.llen
2427 obj.vlen = self.vlen
2430 def __bytes__(self):
2431 self._assert_ready()
2434 def __eq__(self, their):
2435 if isinstance(their, binary_type):
2436 return self._value == their
2437 if not issubclass(their.__class__, OctetString):
2440 self._value == their._value and
2441 self.tag == their.tag and
2442 self._expl == their._expl
2445 def __lt__(self, their):
2446 return self._value < their._value
2457 return self.__class__(
2460 (self._bound_min, self._bound_max)
2461 if bounds is None else bounds
2463 impl=self.tag if impl is None else impl,
2464 expl=self._expl if expl is None else expl,
2465 default=self.default if default is None else default,
2466 optional=self.optional if optional is None else optional,
2470 self._assert_ready()
2473 len_encode(len(self._value)),
2477 def _decode_chunk(self, lv, offset, decode_path, ctx):
2479 l, llen, v = len_decode(lv)
2480 except DecodeError as err:
2481 raise err.__class__(
2483 klass=self.__class__,
2484 decode_path=decode_path,
2488 raise NotEnoughData(
2489 "encoded length is longer than data",
2490 klass=self.__class__,
2491 decode_path=decode_path,
2494 v, tail = v[:l], v[l:]
2496 obj = self.__class__(
2498 bounds=(self._bound_min, self._bound_max),
2501 default=self.default,
2502 optional=self.optional,
2503 _decoded=(offset, llen, l),
2505 except DecodeError as err:
2508 klass=self.__class__,
2509 decode_path=decode_path,
2512 except BoundsError as err:
2515 klass=self.__class__,
2516 decode_path=decode_path,
2521 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2523 t, tlen, lv = tag_strip(tlv)
2524 except DecodeError as err:
2525 raise err.__class__(
2527 klass=self.__class__,
2528 decode_path=decode_path,
2534 return self._decode_chunk(lv, offset, decode_path, ctx)
2535 if t == self.tag_constructed:
2536 if not ctx.get("bered", False):
2538 msg="unallowed BER constructed encoding",
2539 decode_path=decode_path,
2546 l, llen, v = len_decode(lv)
2547 except LenIndefForm:
2548 llen, l, v = 1, 0, lv[1:]
2550 except DecodeError as err:
2551 raise err.__class__(
2553 klass=self.__class__,
2554 decode_path=decode_path,
2557 if l > 0 and l > len(v):
2558 raise NotEnoughData(
2559 "encoded length is longer than data",
2560 klass=self.__class__,
2561 decode_path=decode_path,
2564 if not lenindef and l == 0:
2565 raise NotEnoughData(
2567 klass=self.__class__,
2568 decode_path=decode_path,
2572 sub_offset = offset + tlen + llen
2576 if v[:EOC_LEN].tobytes() == EOC:
2583 msg="chunk out of bounds",
2584 decode_path=len(chunks) - 1,
2585 offset=chunks[-1].offset,
2587 sub_decode_path = decode_path + (str(len(chunks)),)
2589 chunk, v_tail = OctetString().decode(
2592 decode_path=sub_decode_path,
2598 msg="expected OctetString encoded chunk",
2599 decode_path=sub_decode_path,
2602 chunks.append(chunk)
2603 sub_offset += chunk.tlvlen
2604 vlen += chunk.tlvlen
2606 if len(chunks) == 0:
2609 decode_path=decode_path,
2613 obj = self.__class__(
2614 value=b"".join(bytes(chunk) for chunk in chunks),
2615 bounds=(self._bound_min, self._bound_max),
2618 default=self.default,
2619 optional=self.optional,
2620 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2622 except DecodeError as err:
2625 klass=self.__class__,
2626 decode_path=decode_path,
2629 except BoundsError as err:
2632 klass=self.__class__,
2633 decode_path=decode_path,
2636 obj.lenindef = lenindef
2638 return obj, (v[EOC_LEN:] if lenindef else v)
2640 klass=self.__class__,
2641 decode_path=decode_path,
2646 return pp_console_row(next(self.pps()))
2648 def pps(self, decode_path=()):
2650 asn1_type_name=self.asn1_type_name,
2651 obj_name=self.__class__.__name__,
2652 decode_path=decode_path,
2653 value=("%d bytes" % len(self._value)) if self.ready else None,
2654 blob=self._value if self.ready else None,
2655 optional=self.optional,
2656 default=self == self.default,
2657 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2658 expl=None if self._expl is None else tag_decode(self._expl),
2663 expl_offset=self.expl_offset if self.expled else None,
2664 expl_tlen=self.expl_tlen if self.expled else None,
2665 expl_llen=self.expl_llen if self.expled else None,
2666 expl_vlen=self.expl_vlen if self.expled else None,
2667 expl_lenindef=self.expl_lenindef,
2668 lenindef=self.lenindef,
2671 defined_by, defined = self.defined or (None, None)
2672 if defined_by is not None:
2674 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2679 """``NULL`` null object
2687 tag_default = tag_encode(5)
2688 asn1_type_name = "NULL"
2692 value=None, # unused, but Sequence passes it
2699 :param bytes impl: override default tag with ``IMPLICIT`` one
2700 :param bytes expl: override default tag with ``EXPLICIT`` one
2701 :param bool optional: is object ``OPTIONAL`` in sequence
2703 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2711 obj = self.__class__()
2713 obj._expl = self._expl
2714 obj.default = self.default
2715 obj.optional = self.optional
2716 obj.offset = self.offset
2717 obj.llen = self.llen
2718 obj.vlen = self.vlen
2721 def __eq__(self, their):
2722 if not issubclass(their.__class__, Null):
2725 self.tag == their.tag and
2726 self._expl == their._expl
2736 return self.__class__(
2737 impl=self.tag if impl is None else impl,
2738 expl=self._expl if expl is None else expl,
2739 optional=self.optional if optional is None else optional,
2743 return self.tag + len_encode(0)
2745 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2747 t, _, lv = tag_strip(tlv)
2748 except DecodeError as err:
2749 raise err.__class__(
2751 klass=self.__class__,
2752 decode_path=decode_path,
2757 klass=self.__class__,
2758 decode_path=decode_path,
2764 l, _, v = len_decode(lv)
2765 except DecodeError as err:
2766 raise err.__class__(
2768 klass=self.__class__,
2769 decode_path=decode_path,
2773 raise InvalidLength(
2774 "Null must have zero length",
2775 klass=self.__class__,
2776 decode_path=decode_path,
2779 obj = self.__class__(
2782 optional=self.optional,
2783 _decoded=(offset, 1, 0),
2788 return pp_console_row(next(self.pps()))
2790 def pps(self, decode_path=()):
2792 asn1_type_name=self.asn1_type_name,
2793 obj_name=self.__class__.__name__,
2794 decode_path=decode_path,
2795 optional=self.optional,
2796 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2797 expl=None if self._expl is None else tag_decode(self._expl),
2802 expl_offset=self.expl_offset if self.expled else None,
2803 expl_tlen=self.expl_tlen if self.expled else None,
2804 expl_llen=self.expl_llen if self.expled else None,
2805 expl_vlen=self.expl_vlen if self.expled else None,
2806 expl_lenindef=self.expl_lenindef,
2810 class ObjectIdentifier(Obj):
2811 """``OBJECT IDENTIFIER`` OID type
2813 >>> oid = ObjectIdentifier((1, 2, 3))
2814 OBJECT IDENTIFIER 1.2.3
2815 >>> oid == ObjectIdentifier("1.2.3")
2821 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2822 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2824 >>> str(ObjectIdentifier((3, 1)))
2825 Traceback (most recent call last):
2826 pyderasn.InvalidOID: unacceptable first arc value
2828 __slots__ = ("defines",)
2829 tag_default = tag_encode(6)
2830 asn1_type_name = "OBJECT IDENTIFIER"
2843 :param value: set the value. Either tuples of integers,
2844 string of "."-concatenated integers, or
2845 :py:class:`pyderasn.ObjectIdentifier` object
2846 :param defines: sequence of tuples. Each tuple has two elements.
2847 First one is relative to current one decode
2848 path, aiming to the field defined by that OID.
2849 Read about relative path in
2850 :py:func:`pyderasn.abs_decode_path`. Second
2851 tuple element is ``{OID: pyderasn.Obj()}``
2852 dictionary, mapping between current OID value
2853 and structure applied to defined field.
2854 :ref:`Read about DEFINED BY <definedby>`
2855 :param bytes impl: override default tag with ``IMPLICIT`` one
2856 :param bytes expl: override default tag with ``EXPLICIT`` one
2857 :param default: set default value. Type same as in ``value``
2858 :param bool optional: is object ``OPTIONAL`` in sequence
2860 super(ObjectIdentifier, self).__init__(
2868 if value is not None:
2869 self._value = self._value_sanitize(value)
2870 if default is not None:
2871 default = self._value_sanitize(default)
2872 self.default = self.__class__(
2877 if self._value is None:
2878 self._value = default
2879 self.defines = defines
2881 def __add__(self, their):
2882 if isinstance(their, self.__class__):
2883 return self.__class__(self._value + their._value)
2884 if isinstance(their, tuple):
2885 return self.__class__(self._value + their)
2886 raise InvalidValueType((self.__class__, tuple))
2888 def _value_sanitize(self, value):
2889 if issubclass(value.__class__, ObjectIdentifier):
2891 if isinstance(value, string_types):
2893 value = tuple(int(arc) for arc in value.split("."))
2895 raise InvalidOID("unacceptable arcs values")
2896 if isinstance(value, tuple):
2898 raise InvalidOID("less than 2 arcs")
2899 first_arc = value[0]
2900 if first_arc in (0, 1):
2901 if not (0 <= value[1] <= 39):
2902 raise InvalidOID("second arc is too wide")
2903 elif first_arc == 2:
2906 raise InvalidOID("unacceptable first arc value")
2908 raise InvalidValueType((self.__class__, str, tuple))
2912 return self._value is not None
2915 obj = self.__class__()
2916 obj._value = self._value
2917 obj.defines = self.defines
2919 obj._expl = self._expl
2920 obj.default = self.default
2921 obj.optional = self.optional
2922 obj.offset = self.offset
2923 obj.llen = self.llen
2924 obj.vlen = self.vlen
2928 self._assert_ready()
2929 return iter(self._value)
2932 return ".".join(str(arc) for arc in self._value or ())
2935 self._assert_ready()
2938 bytes(self._expl or b"") +
2939 str(self._value).encode("ascii"),
2942 def __eq__(self, their):
2943 if isinstance(their, tuple):
2944 return self._value == their
2945 if not issubclass(their.__class__, ObjectIdentifier):
2948 self.tag == their.tag and
2949 self._expl == their._expl and
2950 self._value == their._value
2953 def __lt__(self, their):
2954 return self._value < their._value
2965 return self.__class__(
2967 defines=self.defines if defines is None else defines,
2968 impl=self.tag if impl is None else impl,
2969 expl=self._expl if expl is None else expl,
2970 default=self.default if default is None else default,
2971 optional=self.optional if optional is None else optional,
2975 self._assert_ready()
2977 first_value = value[1]
2978 first_arc = value[0]
2981 elif first_arc == 1:
2983 elif first_arc == 2:
2985 else: # pragma: no cover
2986 raise RuntimeError("invalid arc is stored")
2987 octets = [zero_ended_encode(first_value)]
2988 for arc in value[2:]:
2989 octets.append(zero_ended_encode(arc))
2990 v = b"".join(octets)
2991 return b"".join((self.tag, len_encode(len(v)), v))
2993 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2995 t, _, lv = tag_strip(tlv)
2996 except DecodeError as err:
2997 raise err.__class__(
2999 klass=self.__class__,
3000 decode_path=decode_path,
3005 klass=self.__class__,
3006 decode_path=decode_path,
3012 l, llen, v = len_decode(lv)
3013 except DecodeError as err:
3014 raise err.__class__(
3016 klass=self.__class__,
3017 decode_path=decode_path,
3021 raise NotEnoughData(
3022 "encoded length is longer than data",
3023 klass=self.__class__,
3024 decode_path=decode_path,
3028 raise NotEnoughData(
3030 klass=self.__class__,
3031 decode_path=decode_path,
3034 v, tail = v[:l], v[l:]
3040 octet = indexbytes(v, i)
3041 arc = (arc << 7) | (octet & 0x7F)
3042 if octet & 0x80 == 0:
3050 klass=self.__class__,
3051 decode_path=decode_path,
3055 second_arc = arcs[0]
3056 if 0 <= second_arc <= 39:
3058 elif 40 <= second_arc <= 79:
3064 obj = self.__class__(
3065 value=tuple([first_arc, second_arc] + arcs[1:]),
3068 default=self.default,
3069 optional=self.optional,
3070 _decoded=(offset, llen, l),
3075 return pp_console_row(next(self.pps()))
3077 def pps(self, decode_path=()):
3079 asn1_type_name=self.asn1_type_name,
3080 obj_name=self.__class__.__name__,
3081 decode_path=decode_path,
3082 value=str(self) if self.ready else None,
3083 optional=self.optional,
3084 default=self == self.default,
3085 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3086 expl=None if self._expl is None else tag_decode(self._expl),
3091 expl_offset=self.expl_offset if self.expled else None,
3092 expl_tlen=self.expl_tlen if self.expled else None,
3093 expl_llen=self.expl_llen if self.expled else None,
3094 expl_vlen=self.expl_vlen if self.expled else None,
3095 expl_lenindef=self.expl_lenindef,
3099 class Enumerated(Integer):
3100 """``ENUMERATED`` integer type
3102 This type is identical to :py:class:`pyderasn.Integer`, but requires
3103 schema to be specified and does not accept values missing from it.
3106 tag_default = tag_encode(10)
3107 asn1_type_name = "ENUMERATED"
3118 bounds=None, # dummy argument, workability for Integer.decode
3120 super(Enumerated, self).__init__(
3129 if len(self.specs) == 0:
3130 raise ValueError("schema must be specified")
3132 def _value_sanitize(self, value):
3133 if isinstance(value, self.__class__):
3134 value = value._value
3135 elif isinstance(value, integer_types):
3136 if value not in list(self.specs.values()):
3138 "unknown integer value: %s" % value,
3139 klass=self.__class__,
3141 elif isinstance(value, string_types):
3142 value = self.specs.get(value)
3144 raise ObjUnknown("integer value: %s" % value)
3146 raise InvalidValueType((self.__class__, int, str))
3150 obj = self.__class__(_specs=self.specs)
3151 obj._value = self._value
3152 obj._bound_min = self._bound_min
3153 obj._bound_max = self._bound_max
3155 obj._expl = self._expl
3156 obj.default = self.default
3157 obj.optional = self.optional
3158 obj.offset = self.offset
3159 obj.llen = self.llen
3160 obj.vlen = self.vlen
3172 return self.__class__(
3174 impl=self.tag if impl is None else impl,
3175 expl=self._expl if expl is None else expl,
3176 default=self.default if default is None else default,
3177 optional=self.optional if optional is None else optional,
3182 class CommonString(OctetString):
3183 """Common class for all strings
3185 Everything resembles :py:class:`pyderasn.OctetString`, except
3186 ability to deal with unicode text strings.
3188 >>> hexenc("привет мир".encode("utf-8"))
3189 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3190 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3192 >>> s = UTF8String("привет мир")
3193 UTF8String UTF8String привет мир
3195 'привет мир'
3196 >>> hexenc(bytes(s))
3197 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3199 >>> PrintableString("привет мир")
3200 Traceback (most recent call last):
3201 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3203 >>> BMPString("ада", bounds=(2, 2))
3204 Traceback (most recent call last):
3205 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3206 >>> s = BMPString("ад", bounds=(2, 2))
3209 >>> hexenc(bytes(s))
3217 * - :py:class:`pyderasn.UTF8String`
3219 * - :py:class:`pyderasn.NumericString`
3221 * - :py:class:`pyderasn.PrintableString`
3223 * - :py:class:`pyderasn.TeletexString`
3225 * - :py:class:`pyderasn.T61String`
3227 * - :py:class:`pyderasn.VideotexString`
3229 * - :py:class:`pyderasn.IA5String`
3231 * - :py:class:`pyderasn.GraphicString`
3233 * - :py:class:`pyderasn.VisibleString`
3235 * - :py:class:`pyderasn.ISO646String`
3237 * - :py:class:`pyderasn.GeneralString`
3239 * - :py:class:`pyderasn.UniversalString`
3241 * - :py:class:`pyderasn.BMPString`
3244 __slots__ = ("encoding",)
3246 def _value_sanitize(self, value):
3248 value_decoded = None
3249 if isinstance(value, self.__class__):
3250 value_raw = value._value
3251 elif isinstance(value, text_type):
3252 value_decoded = value
3253 elif isinstance(value, binary_type):
3256 raise InvalidValueType((self.__class__, text_type, binary_type))
3259 value_decoded.encode(self.encoding)
3260 if value_raw is None else value_raw
3263 value_raw.decode(self.encoding)
3264 if value_decoded is None else value_decoded
3266 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3267 raise DecodeError(str(err))
3268 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3276 def __eq__(self, their):
3277 if isinstance(their, binary_type):
3278 return self._value == their
3279 if isinstance(their, text_type):
3280 return self._value == their.encode(self.encoding)
3281 if not isinstance(their, self.__class__):
3284 self._value == their._value and
3285 self.tag == their.tag and
3286 self._expl == their._expl
3289 def __unicode__(self):
3291 return self._value.decode(self.encoding)
3292 return text_type(self._value)
3295 return pp_console_row(next(self.pps(no_unicode=PY2)))
3297 def pps(self, decode_path=(), no_unicode=False):
3300 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3302 asn1_type_name=self.asn1_type_name,
3303 obj_name=self.__class__.__name__,
3304 decode_path=decode_path,
3306 optional=self.optional,
3307 default=self == self.default,
3308 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3309 expl=None if self._expl is None else tag_decode(self._expl),
3314 expl_offset=self.expl_offset if self.expled else None,
3315 expl_tlen=self.expl_tlen if self.expled else None,
3316 expl_llen=self.expl_llen if self.expled else None,
3317 expl_vlen=self.expl_vlen if self.expled else None,
3318 expl_lenindef=self.expl_lenindef,
3322 class UTF8String(CommonString):
3324 tag_default = tag_encode(12)
3326 asn1_type_name = "UTF8String"
3329 class NumericString(CommonString):
3331 tag_default = tag_encode(18)
3333 asn1_type_name = "NumericString"
3334 allowable_chars = set(digits.encode("ascii"))
3336 def _value_sanitize(self, value):
3337 value = super(NumericString, self)._value_sanitize(value)
3338 if not set(value) <= self.allowable_chars:
3339 raise DecodeError("non-numeric value")
3343 class PrintableString(CommonString):
3345 tag_default = tag_encode(19)
3347 asn1_type_name = "PrintableString"
3350 class TeletexString(CommonString):
3352 tag_default = tag_encode(20)
3354 asn1_type_name = "TeletexString"
3357 class T61String(TeletexString):
3359 asn1_type_name = "T61String"
3362 class VideotexString(CommonString):
3364 tag_default = tag_encode(21)
3365 encoding = "iso-8859-1"
3366 asn1_type_name = "VideotexString"
3369 class IA5String(CommonString):
3371 tag_default = tag_encode(22)
3373 asn1_type_name = "IA5"
3376 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3377 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3378 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3381 class UTCTime(CommonString):
3382 """``UTCTime`` datetime type
3384 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3385 UTCTime UTCTime 2017-09-30T22:07:50
3391 datetime.datetime(2017, 9, 30, 22, 7, 50)
3392 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3393 datetime.datetime(1957, 9, 30, 22, 7, 50)
3396 tag_default = tag_encode(23)
3398 asn1_type_name = "UTCTime"
3400 fmt = "%y%m%d%H%M%SZ"
3410 bounds=None, # dummy argument, workability for OctetString.decode
3413 :param value: set the value. Either datetime type, or
3414 :py:class:`pyderasn.UTCTime` object
3415 :param bytes impl: override default tag with ``IMPLICIT`` one
3416 :param bytes expl: override default tag with ``EXPLICIT`` one
3417 :param default: set default value. Type same as in ``value``
3418 :param bool optional: is object ``OPTIONAL`` in sequence
3420 super(UTCTime, self).__init__(
3428 if value is not None:
3429 self._value = self._value_sanitize(value)
3430 if default is not None:
3431 default = self._value_sanitize(default)
3432 self.default = self.__class__(
3437 if self._value is None:
3438 self._value = default
3440 def _value_sanitize(self, value):
3441 if isinstance(value, self.__class__):
3443 if isinstance(value, datetime):
3444 return value.strftime(self.fmt).encode("ascii")
3445 if isinstance(value, binary_type):
3446 value_decoded = value.decode("ascii")
3447 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3449 datetime.strptime(value_decoded, self.fmt)
3451 raise DecodeError("invalid UTCTime format")
3454 raise DecodeError("invalid UTCTime length")
3455 raise InvalidValueType((self.__class__, datetime))
3457 def __eq__(self, their):
3458 if isinstance(their, binary_type):
3459 return self._value == their
3460 if isinstance(their, datetime):
3461 return self.todatetime() == their
3462 if not isinstance(their, self.__class__):
3465 self._value == their._value and
3466 self.tag == their.tag and
3467 self._expl == their._expl
3470 def todatetime(self):
3471 """Convert to datetime
3475 Pay attention that UTCTime can not hold full year, so all years
3476 having < 50 years are treated as 20xx, 19xx otherwise, according
3477 to X.509 recomendation.
3479 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3480 year = value.year % 100
3482 year=(2000 + year) if year < 50 else (1900 + year),
3486 minute=value.minute,
3487 second=value.second,
3491 return pp_console_row(next(self.pps()))
3493 def pps(self, decode_path=()):
3495 asn1_type_name=self.asn1_type_name,
3496 obj_name=self.__class__.__name__,
3497 decode_path=decode_path,
3498 value=self.todatetime().isoformat() if self.ready else None,
3499 optional=self.optional,
3500 default=self == self.default,
3501 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3502 expl=None if self._expl is None else tag_decode(self._expl),
3507 expl_offset=self.expl_offset if self.expled else None,
3508 expl_tlen=self.expl_tlen if self.expled else None,
3509 expl_llen=self.expl_llen if self.expled else None,
3510 expl_vlen=self.expl_vlen if self.expled else None,
3511 expl_lenindef=self.expl_lenindef,
3515 class GeneralizedTime(UTCTime):
3516 """``GeneralizedTime`` datetime type
3518 This type is similar to :py:class:`pyderasn.UTCTime`.
3520 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3521 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3523 '20170930220750.000123Z'
3524 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3525 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3528 tag_default = tag_encode(24)
3529 asn1_type_name = "GeneralizedTime"
3531 fmt = "%Y%m%d%H%M%SZ"
3532 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3534 def _value_sanitize(self, value):
3535 if isinstance(value, self.__class__):
3537 if isinstance(value, datetime):
3538 return value.strftime(
3539 self.fmt_ms if value.microsecond > 0 else self.fmt
3541 if isinstance(value, binary_type):
3542 value_decoded = value.decode("ascii")
3543 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3545 datetime.strptime(value_decoded, self.fmt)
3548 "invalid GeneralizedTime (without ms) format",
3551 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3553 datetime.strptime(value_decoded, self.fmt_ms)
3556 "invalid GeneralizedTime (with ms) format",
3561 "invalid GeneralizedTime length",
3562 klass=self.__class__,
3564 raise InvalidValueType((self.__class__, datetime))
3566 def todatetime(self):
3567 value = self._value.decode("ascii")
3568 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3569 return datetime.strptime(value, self.fmt)
3570 return datetime.strptime(value, self.fmt_ms)
3573 class GraphicString(CommonString):
3575 tag_default = tag_encode(25)
3576 encoding = "iso-8859-1"
3577 asn1_type_name = "GraphicString"
3580 class VisibleString(CommonString):
3582 tag_default = tag_encode(26)
3584 asn1_type_name = "VisibleString"
3587 class ISO646String(VisibleString):
3589 asn1_type_name = "ISO646String"
3592 class GeneralString(CommonString):
3594 tag_default = tag_encode(27)
3595 encoding = "iso-8859-1"
3596 asn1_type_name = "GeneralString"
3599 class UniversalString(CommonString):
3601 tag_default = tag_encode(28)
3602 encoding = "utf-32-be"
3603 asn1_type_name = "UniversalString"
3606 class BMPString(CommonString):
3608 tag_default = tag_encode(30)
3609 encoding = "utf-16-be"
3610 asn1_type_name = "BMPString"
3614 """``CHOICE`` special type
3618 class GeneralName(Choice):
3620 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3621 ("dNSName", IA5String(impl=tag_ctxp(2))),
3624 >>> gn = GeneralName()
3626 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3627 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3628 >>> gn["dNSName"] = IA5String("bar.baz")
3629 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3630 >>> gn["rfc822Name"]
3633 [2] IA5String IA5 bar.baz
3636 >>> gn.value == gn["dNSName"]
3639 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3641 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3642 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3644 __slots__ = ("specs",)
3646 asn1_type_name = "CHOICE"
3659 :param value: set the value. Either ``(choice, value)`` tuple, or
3660 :py:class:`pyderasn.Choice` object
3661 :param bytes impl: can not be set, do **not** use it
3662 :param bytes expl: override default tag with ``EXPLICIT`` one
3663 :param default: set default value. Type same as in ``value``
3664 :param bool optional: is object ``OPTIONAL`` in sequence
3666 if impl is not None:
3667 raise ValueError("no implicit tag allowed for CHOICE")
3668 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3670 schema = getattr(self, "schema", ())
3671 if len(schema) == 0:
3672 raise ValueError("schema must be specified")
3674 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3677 if value is not None:
3678 self._value = self._value_sanitize(value)
3679 if default is not None:
3680 default_value = self._value_sanitize(default)
3681 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3682 default_obj.specs = self.specs
3683 default_obj._value = default_value
3684 self.default = default_obj
3686 self._value = default_obj.copy()._value
3688 def _value_sanitize(self, value):
3689 if isinstance(value, self.__class__):
3691 if isinstance(value, tuple) and len(value) == 2:
3693 spec = self.specs.get(choice)
3695 raise ObjUnknown(choice)
3696 if not isinstance(obj, spec.__class__):
3697 raise InvalidValueType((spec,))
3698 return (choice, spec(obj))
3699 raise InvalidValueType((self.__class__, tuple))
3703 return self._value is not None and self._value[1].ready
3706 obj = self.__class__(schema=self.specs)
3707 obj._expl = self._expl
3708 obj.default = self.default
3709 obj.optional = self.optional
3710 obj.offset = self.offset
3711 obj.llen = self.llen
3712 obj.vlen = self.vlen
3714 if value is not None:
3715 obj._value = (value[0], value[1].copy())
3718 def __eq__(self, their):
3719 if isinstance(their, tuple) and len(their) == 2:
3720 return self._value == their
3721 if not isinstance(their, self.__class__):
3724 self.specs == their.specs and
3725 self._value == their._value
3735 return self.__class__(
3738 expl=self._expl if expl is None else expl,
3739 default=self.default if default is None else default,
3740 optional=self.optional if optional is None else optional,
3745 self._assert_ready()
3746 return self._value[0]
3750 self._assert_ready()
3751 return self._value[1]
3753 def __getitem__(self, key):
3754 if key not in self.specs:
3755 raise ObjUnknown(key)
3756 if self._value is None:
3758 choice, value = self._value
3763 def __setitem__(self, key, value):
3764 spec = self.specs.get(key)
3766 raise ObjUnknown(key)
3767 if not isinstance(value, spec.__class__):
3768 raise InvalidValueType((spec.__class__,))
3769 self._value = (key, spec(value))
3777 return self._value[1].decoded if self.ready else False
3780 self._assert_ready()
3781 return self._value[1].encode()
3783 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3784 for choice, spec in self.specs.items():
3785 sub_decode_path = decode_path + (choice,)
3791 decode_path=sub_decode_path,
3800 klass=self.__class__,
3801 decode_path=decode_path,
3806 value, tail = spec.decode(
3810 decode_path=sub_decode_path,
3813 obj = self.__class__(
3816 default=self.default,
3817 optional=self.optional,
3818 _decoded=(offset, 0, value.tlvlen),
3820 obj._value = (choice, value)
3824 value = pp_console_row(next(self.pps()))
3826 value = "%s[%r]" % (value, self.value)
3829 def pps(self, decode_path=()):
3831 asn1_type_name=self.asn1_type_name,
3832 obj_name=self.__class__.__name__,
3833 decode_path=decode_path,
3834 value=self.choice if self.ready else None,
3835 optional=self.optional,
3836 default=self == self.default,
3837 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3838 expl=None if self._expl is None else tag_decode(self._expl),
3843 expl_lenindef=self.expl_lenindef,
3846 yield self.value.pps(decode_path=decode_path + (self.choice,))
3849 class PrimitiveTypes(Choice):
3850 """Predefined ``CHOICE`` for all generic primitive types
3852 It could be useful for general decoding of some unspecified values:
3854 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3855 OCTET STRING 3 bytes 666f6f
3856 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3860 schema = tuple((klass.__name__, klass()) for klass in (
3885 """``ANY`` special type
3887 >>> Any(Integer(-123))
3889 >>> a = Any(OctetString(b"hello world").encode())
3890 ANY 040b68656c6c6f20776f726c64
3891 >>> hexenc(bytes(a))
3892 b'0x040x0bhello world'
3894 __slots__ = ("defined",)
3895 tag_default = tag_encode(0)
3896 asn1_type_name = "ANY"
3906 :param value: set the value. Either any kind of pyderasn's
3907 **ready** object, or bytes. Pay attention that
3908 **no** validation is performed is raw binary value
3910 :param bytes expl: override default tag with ``EXPLICIT`` one
3911 :param bool optional: is object ``OPTIONAL`` in sequence
3913 super(Any, self).__init__(None, expl, None, optional, _decoded)
3914 self._value = None if value is None else self._value_sanitize(value)
3917 def _value_sanitize(self, value):
3918 if isinstance(value, self.__class__):
3920 if isinstance(value, Obj):
3921 return value.encode()
3922 if isinstance(value, binary_type):
3924 raise InvalidValueType((self.__class__, Obj, binary_type))
3928 return self._value is not None
3931 obj = self.__class__()
3932 obj._value = self._value
3934 obj._expl = self._expl
3935 obj.optional = self.optional
3936 obj.offset = self.offset
3937 obj.llen = self.llen
3938 obj.vlen = self.vlen
3941 def __eq__(self, their):
3942 if isinstance(their, binary_type):
3943 return self._value == their
3944 if issubclass(their.__class__, Any):
3945 return self._value == their._value
3954 return self.__class__(
3956 expl=self._expl if expl is None else expl,
3957 optional=self.optional if optional is None else optional,
3960 def __bytes__(self):
3961 self._assert_ready()
3969 self._assert_ready()
3972 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3974 t, tlen, lv = tag_strip(tlv)
3975 except DecodeError as err:
3976 raise err.__class__(
3978 klass=self.__class__,
3979 decode_path=decode_path,
3983 l, llen, v = len_decode(lv)
3984 except LenIndefForm as err:
3985 if not ctx.get("bered", False):
3986 raise err.__class__(
3988 klass=self.__class__,
3989 decode_path=decode_path,
3992 llen, vlen, v = 1, 0, lv[1:]
3993 sub_offset = offset + tlen + llen
3996 if v[:EOC_LEN].tobytes() == EOC:
3997 tlvlen = tlen + llen + vlen + EOC_LEN
3998 obj = self.__class__(
3999 value=tlv[:tlvlen].tobytes(),
4001 optional=self.optional,
4002 _decoded=(offset, 0, tlvlen),
4006 return obj, v[EOC_LEN:]
4008 chunk, v = Any().decode(
4011 decode_path=decode_path + (str(chunk_i),),
4015 vlen += chunk.tlvlen
4016 sub_offset += chunk.tlvlen
4018 except DecodeError as err:
4019 raise err.__class__(
4021 klass=self.__class__,
4022 decode_path=decode_path,
4026 raise NotEnoughData(
4027 "encoded length is longer than data",
4028 klass=self.__class__,
4029 decode_path=decode_path,
4032 tlvlen = tlen + llen + l
4033 v, tail = tlv[:tlvlen], v[l:]
4034 obj = self.__class__(
4037 optional=self.optional,
4038 _decoded=(offset, 0, tlvlen),
4044 return pp_console_row(next(self.pps()))
4046 def pps(self, decode_path=()):
4048 asn1_type_name=self.asn1_type_name,
4049 obj_name=self.__class__.__name__,
4050 decode_path=decode_path,
4051 blob=self._value if self.ready else None,
4052 optional=self.optional,
4053 default=self == self.default,
4054 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4055 expl=None if self._expl is None else tag_decode(self._expl),
4060 expl_offset=self.expl_offset if self.expled else None,
4061 expl_tlen=self.expl_tlen if self.expled else None,
4062 expl_llen=self.expl_llen if self.expled else None,
4063 expl_vlen=self.expl_vlen if self.expled else None,
4064 expl_lenindef=self.expl_lenindef,
4065 lenindef=self.lenindef,
4067 defined_by, defined = self.defined or (None, None)
4068 if defined_by is not None:
4070 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4074 ########################################################################
4075 # ASN.1 constructed types
4076 ########################################################################
4078 def get_def_by_path(defines_by_path, sub_decode_path):
4079 """Get define by decode path
4081 for path, define in defines_by_path:
4082 if len(path) != len(sub_decode_path):
4084 for p1, p2 in zip(path, sub_decode_path):
4085 if (p1 != any) and (p1 != p2):
4091 def abs_decode_path(decode_path, rel_path):
4092 """Create an absolute decode path from current and relative ones
4094 :param decode_path: current decode path, starting point.
4096 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4097 If first tuple's element is "/", then treat it as
4098 an absolute path, ignoring ``decode_path`` as
4099 starting point. Also this tuple can contain ".."
4100 elements, stripping the leading element from
4103 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4104 ("foo", "bar", "baz", "whatever")
4105 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4107 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4110 if rel_path[0] == "/":
4112 if rel_path[0] == "..":
4113 return abs_decode_path(decode_path[:-1], rel_path[1:])
4114 return decode_path + rel_path
4117 class Sequence(Obj):
4118 """``SEQUENCE`` structure type
4120 You have to make specification of sequence::
4122 class Extension(Sequence):
4124 ("extnID", ObjectIdentifier()),
4125 ("critical", Boolean(default=False)),
4126 ("extnValue", OctetString()),
4129 Then, you can work with it as with dictionary.
4131 >>> ext = Extension()
4132 >>> Extension().specs
4134 ('extnID', OBJECT IDENTIFIER),
4135 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4136 ('extnValue', OCTET STRING),
4138 >>> ext["extnID"] = "1.2.3"
4139 Traceback (most recent call last):
4140 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4141 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4143 You can determine if sequence is ready to be encoded:
4148 Traceback (most recent call last):
4149 pyderasn.ObjNotReady: object is not ready: extnValue
4150 >>> ext["extnValue"] = OctetString(b"foobar")
4154 Value you want to assign, must have the same **type** as in
4155 corresponding specification, but it can have different tags,
4156 optional/default attributes -- they will be taken from specification
4159 class TBSCertificate(Sequence):
4161 ("version", Version(expl=tag_ctxc(0), default="v1")),
4164 >>> tbs = TBSCertificate()
4165 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4167 Assign ``None`` to remove value from sequence.
4169 You can set values in Sequence during its initialization:
4171 >>> AlgorithmIdentifier((
4172 ("algorithm", ObjectIdentifier("1.2.3")),
4173 ("parameters", Any(Null()))
4175 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4177 You can determine if value exists/set in the sequence and take its value:
4179 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4182 OBJECT IDENTIFIER 1.2.3
4184 But pay attention that if value has default, then it won't be (not
4185 in) in the sequence (because ``DEFAULT`` must not be encoded in
4186 DER), but you can read its value:
4188 >>> "critical" in ext, ext["critical"]
4189 (False, BOOLEAN False)
4190 >>> ext["critical"] = Boolean(True)
4191 >>> "critical" in ext, ext["critical"]
4192 (True, BOOLEAN True)
4194 All defaulted values are always optional.
4196 .. _strict_default_existence_ctx:
4200 When decoded DER contains defaulted value inside, then
4201 technically this is not valid DER encoding. But we allow and pass
4202 it **by default**. Of course reencoding of that kind of DER will
4203 result in different binary representation (validly without
4204 defaulted value inside). You can enable strict defaulted values
4205 existence validation by setting ``"strict_default_existence":
4206 True`` :ref:`context <ctx>` option -- decoding process will raise
4207 an exception if defaulted value is met.
4209 Two sequences are equal if they have equal specification (schema),
4210 implicit/explicit tagging and the same values.
4212 __slots__ = ("specs",)
4213 tag_default = tag_encode(form=TagFormConstructed, num=16)
4214 asn1_type_name = "SEQUENCE"
4226 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4228 schema = getattr(self, "schema", ())
4230 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4233 if value is not None:
4234 if issubclass(value.__class__, Sequence):
4235 self._value = value._value
4236 elif hasattr(value, "__iter__"):
4237 for seq_key, seq_value in value:
4238 self[seq_key] = seq_value
4240 raise InvalidValueType((Sequence,))
4241 if default is not None:
4242 if not issubclass(default.__class__, Sequence):
4243 raise InvalidValueType((Sequence,))
4244 default_value = default._value
4245 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4246 default_obj.specs = self.specs
4247 default_obj._value = default_value
4248 self.default = default_obj
4250 self._value = default_obj.copy()._value
4254 for name, spec in self.specs.items():
4255 value = self._value.get(name)
4266 obj = self.__class__(schema=self.specs)
4268 obj._expl = self._expl
4269 obj.default = self.default
4270 obj.optional = self.optional
4271 obj.offset = self.offset
4272 obj.llen = self.llen
4273 obj.vlen = self.vlen
4274 obj._value = {k: v.copy() for k, v in self._value.items()}
4277 def __eq__(self, their):
4278 if not isinstance(their, self.__class__):
4281 self.specs == their.specs and
4282 self.tag == their.tag and
4283 self._expl == their._expl and
4284 self._value == their._value
4295 return self.__class__(
4298 impl=self.tag if impl is None else impl,
4299 expl=self._expl if expl is None else expl,
4300 default=self.default if default is None else default,
4301 optional=self.optional if optional is None else optional,
4304 def __contains__(self, key):
4305 return key in self._value
4307 def __setitem__(self, key, value):
4308 spec = self.specs.get(key)
4310 raise ObjUnknown(key)
4312 self._value.pop(key, None)
4314 if not isinstance(value, spec.__class__):
4315 raise InvalidValueType((spec.__class__,))
4316 value = spec(value=value)
4317 if spec.default is not None and value == spec.default:
4318 self._value.pop(key, None)
4320 self._value[key] = value
4322 def __getitem__(self, key):
4323 value = self._value.get(key)
4324 if value is not None:
4326 spec = self.specs.get(key)
4328 raise ObjUnknown(key)
4329 if spec.default is not None:
4333 def _encoded_values(self):
4335 for name, spec in self.specs.items():
4336 value = self._value.get(name)
4340 raise ObjNotReady(name)
4341 raws.append(value.encode())
4345 v = b"".join(self._encoded_values())
4346 return b"".join((self.tag, len_encode(len(v)), v))
4348 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4350 t, tlen, lv = tag_strip(tlv)
4351 except DecodeError as err:
4352 raise err.__class__(
4354 klass=self.__class__,
4355 decode_path=decode_path,
4360 klass=self.__class__,
4361 decode_path=decode_path,
4368 l, llen, v = len_decode(lv)
4369 except LenIndefForm as err:
4370 if not ctx.get("bered", False):
4371 raise err.__class__(
4373 klass=self.__class__,
4374 decode_path=decode_path,
4377 l, llen, v = 0, 1, lv[1:]
4379 except DecodeError as err:
4380 raise err.__class__(
4382 klass=self.__class__,
4383 decode_path=decode_path,
4387 raise NotEnoughData(
4388 "encoded length is longer than data",
4389 klass=self.__class__,
4390 decode_path=decode_path,
4394 v, tail = v[:l], v[l:]
4396 sub_offset = offset + tlen + llen
4398 for name, spec in self.specs.items():
4399 if spec.optional and (
4400 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4404 sub_decode_path = decode_path + (name,)
4406 value, v_tail = spec.decode(
4410 decode_path=sub_decode_path,
4418 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4419 if defined is not None:
4420 defined_by, defined_spec = defined
4421 if issubclass(value.__class__, SequenceOf):
4422 for i, _value in enumerate(value):
4423 sub_sub_decode_path = sub_decode_path + (
4425 DecodePathDefBy(defined_by),
4427 defined_value, defined_tail = defined_spec.decode(
4428 memoryview(bytes(_value)),
4430 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4431 if value.expled else (value.tlen + value.llen)
4434 decode_path=sub_sub_decode_path,
4437 if len(defined_tail) > 0:
4440 klass=self.__class__,
4441 decode_path=sub_sub_decode_path,
4444 _value.defined = (defined_by, defined_value)
4446 defined_value, defined_tail = defined_spec.decode(
4447 memoryview(bytes(value)),
4449 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4450 if value.expled else (value.tlen + value.llen)
4453 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4456 if len(defined_tail) > 0:
4459 klass=self.__class__,
4460 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4463 value.defined = (defined_by, defined_value)
4465 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4467 sub_offset += value_len
4469 if spec.default is not None and value == spec.default:
4470 if ctx.get("strict_default_existence", False):
4472 "DEFAULT value met",
4473 klass=self.__class__,
4474 decode_path=sub_decode_path,
4479 values[name] = value
4481 spec_defines = getattr(spec, "defines", ())
4482 if len(spec_defines) == 0:
4483 defines_by_path = ctx.get("defines_by_path", ())
4484 if len(defines_by_path) > 0:
4485 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4486 if spec_defines is not None and len(spec_defines) > 0:
4487 for rel_path, schema in spec_defines:
4488 defined = schema.get(value, None)
4489 if defined is not None:
4490 ctx.setdefault("defines", []).append((
4491 abs_decode_path(sub_decode_path[:-1], rel_path),
4495 if v[:EOC_LEN].tobytes() != EOC:
4498 klass=self.__class__,
4499 decode_path=decode_path,
4507 klass=self.__class__,
4508 decode_path=decode_path,
4511 obj = self.__class__(
4515 default=self.default,
4516 optional=self.optional,
4517 _decoded=(offset, llen, vlen),
4520 obj.lenindef = lenindef
4524 value = pp_console_row(next(self.pps()))
4526 for name in self.specs:
4527 _value = self._value.get(name)
4530 cols.append(repr(_value))
4531 return "%s[%s]" % (value, ", ".join(cols))
4533 def pps(self, decode_path=()):
4535 asn1_type_name=self.asn1_type_name,
4536 obj_name=self.__class__.__name__,
4537 decode_path=decode_path,
4538 optional=self.optional,
4539 default=self == self.default,
4540 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4541 expl=None if self._expl is None else tag_decode(self._expl),
4546 expl_offset=self.expl_offset if self.expled else None,
4547 expl_tlen=self.expl_tlen if self.expled else None,
4548 expl_llen=self.expl_llen if self.expled else None,
4549 expl_vlen=self.expl_vlen if self.expled else None,
4550 expl_lenindef=self.expl_lenindef,
4551 lenindef=self.lenindef,
4553 for name in self.specs:
4554 value = self._value.get(name)
4557 yield value.pps(decode_path=decode_path + (name,))
4560 class Set(Sequence):
4561 """``SET`` structure type
4563 Its usage is identical to :py:class:`pyderasn.Sequence`.
4566 tag_default = tag_encode(form=TagFormConstructed, num=17)
4567 asn1_type_name = "SET"
4570 raws = self._encoded_values()
4573 return b"".join((self.tag, len_encode(len(v)), v))
4575 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4577 t, tlen, lv = tag_strip(tlv)
4578 except DecodeError as err:
4579 raise err.__class__(
4581 klass=self.__class__,
4582 decode_path=decode_path,
4587 klass=self.__class__,
4588 decode_path=decode_path,
4595 l, llen, v = len_decode(lv)
4596 except LenIndefForm as err:
4597 if not ctx.get("bered", False):
4598 raise err.__class__(
4600 klass=self.__class__,
4601 decode_path=decode_path,
4604 l, llen, v = 0, 1, lv[1:]
4606 except DecodeError as err:
4607 raise err.__class__(
4609 klass=self.__class__,
4610 decode_path=decode_path,
4614 raise NotEnoughData(
4615 "encoded length is longer than data",
4616 klass=self.__class__,
4620 v, tail = v[:l], v[l:]
4622 sub_offset = offset + tlen + llen
4624 specs_items = self.specs.items
4626 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4628 for name, spec in specs_items():
4629 sub_decode_path = decode_path + (name,)
4635 decode_path=sub_decode_path,
4644 klass=self.__class__,
4645 decode_path=decode_path,
4648 value, v_tail = spec.decode(
4652 decode_path=sub_decode_path,
4655 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4656 sub_offset += value_len
4659 if spec.default is None or value != spec.default: # pragma: no cover
4660 # SeqMixing.test_encoded_default_accepted covers that place
4661 values[name] = value
4662 obj = self.__class__(
4666 default=self.default,
4667 optional=self.optional,
4668 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4673 msg="not all values are ready",
4674 klass=self.__class__,
4675 decode_path=decode_path,
4678 obj.lenindef = lenindef
4679 return obj, (v[EOC_LEN:] if lenindef else tail)
4682 class SequenceOf(Obj):
4683 """``SEQUENCE OF`` sequence type
4685 For that kind of type you must specify the object it will carry on
4686 (bounds are for example here, not required)::
4688 class Ints(SequenceOf):
4693 >>> ints.append(Integer(123))
4694 >>> ints.append(Integer(234))
4696 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4697 >>> [int(i) for i in ints]
4699 >>> ints.append(Integer(345))
4700 Traceback (most recent call last):
4701 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4704 >>> ints[1] = Integer(345)
4706 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4708 Also you can initialize sequence with preinitialized values:
4710 >>> ints = Ints([Integer(123), Integer(234)])
4712 __slots__ = ("spec", "_bound_min", "_bound_max")
4713 tag_default = tag_encode(form=TagFormConstructed, num=16)
4714 asn1_type_name = "SEQUENCE OF"
4727 super(SequenceOf, self).__init__(
4735 schema = getattr(self, "schema", None)
4737 raise ValueError("schema must be specified")
4739 self._bound_min, self._bound_max = getattr(
4743 ) if bounds is None else bounds
4745 if value is not None:
4746 self._value = self._value_sanitize(value)
4747 if default is not None:
4748 default_value = self._value_sanitize(default)
4749 default_obj = self.__class__(
4754 default_obj._value = default_value
4755 self.default = default_obj
4757 self._value = default_obj.copy()._value
4759 def _value_sanitize(self, value):
4760 if issubclass(value.__class__, SequenceOf):
4761 value = value._value
4762 elif hasattr(value, "__iter__"):
4765 raise InvalidValueType((self.__class__, iter))
4766 if not self._bound_min <= len(value) <= self._bound_max:
4767 raise BoundsError(self._bound_min, len(value), self._bound_max)
4769 if not isinstance(v, self.spec.__class__):
4770 raise InvalidValueType((self.spec.__class__,))
4775 return all(v.ready for v in self._value)
4778 obj = self.__class__(schema=self.spec)
4779 obj._bound_min = self._bound_min
4780 obj._bound_max = self._bound_max
4782 obj._expl = self._expl
4783 obj.default = self.default
4784 obj.optional = self.optional
4785 obj.offset = self.offset
4786 obj.llen = self.llen
4787 obj.vlen = self.vlen
4788 obj._value = [v.copy() for v in self._value]
4791 def __eq__(self, their):
4792 if isinstance(their, self.__class__):
4794 self.spec == their.spec and
4795 self.tag == their.tag and
4796 self._expl == their._expl and
4797 self._value == their._value
4799 if hasattr(their, "__iter__"):
4800 return self._value == list(their)
4812 return self.__class__(
4816 (self._bound_min, self._bound_max)
4817 if bounds is None else bounds
4819 impl=self.tag if impl is None else impl,
4820 expl=self._expl if expl is None else expl,
4821 default=self.default if default is None else default,
4822 optional=self.optional if optional is None else optional,
4825 def __contains__(self, key):
4826 return key in self._value
4828 def append(self, value):
4829 if not isinstance(value, self.spec.__class__):
4830 raise InvalidValueType((self.spec.__class__,))
4831 if len(self._value) + 1 > self._bound_max:
4834 len(self._value) + 1,
4837 self._value.append(value)
4840 self._assert_ready()
4841 return iter(self._value)
4844 self._assert_ready()
4845 return len(self._value)
4847 def __setitem__(self, key, value):
4848 if not isinstance(value, self.spec.__class__):
4849 raise InvalidValueType((self.spec.__class__,))
4850 self._value[key] = self.spec(value=value)
4852 def __getitem__(self, key):
4853 return self._value[key]
4855 def _encoded_values(self):
4856 return [v.encode() for v in self._value]
4859 v = b"".join(self._encoded_values())
4860 return b"".join((self.tag, len_encode(len(v)), v))
4862 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4864 t, tlen, lv = tag_strip(tlv)
4865 except DecodeError as err:
4866 raise err.__class__(
4868 klass=self.__class__,
4869 decode_path=decode_path,
4874 klass=self.__class__,
4875 decode_path=decode_path,
4882 l, llen, v = len_decode(lv)
4883 except LenIndefForm as err:
4884 if not ctx.get("bered", False):
4885 raise err.__class__(
4887 klass=self.__class__,
4888 decode_path=decode_path,
4891 l, llen, v = 0, 1, lv[1:]
4893 except DecodeError as err:
4894 raise err.__class__(
4896 klass=self.__class__,
4897 decode_path=decode_path,
4901 raise NotEnoughData(
4902 "encoded length is longer than data",
4903 klass=self.__class__,
4904 decode_path=decode_path,
4908 v, tail = v[:l], v[l:]
4910 sub_offset = offset + tlen + llen
4914 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4916 value, v_tail = spec.decode(
4920 decode_path=decode_path + (str(len(_value)),),
4923 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4924 sub_offset += value_len
4927 _value.append(value)
4928 obj = self.__class__(
4931 bounds=(self._bound_min, self._bound_max),
4934 default=self.default,
4935 optional=self.optional,
4936 _decoded=(offset, llen, vlen),
4938 obj.lenindef = lenindef
4939 return obj, (v[EOC_LEN:] if lenindef else tail)
4943 pp_console_row(next(self.pps())),
4944 ", ".join(repr(v) for v in self._value),
4947 def pps(self, decode_path=()):
4949 asn1_type_name=self.asn1_type_name,
4950 obj_name=self.__class__.__name__,
4951 decode_path=decode_path,
4952 optional=self.optional,
4953 default=self == self.default,
4954 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4955 expl=None if self._expl is None else tag_decode(self._expl),
4960 expl_offset=self.expl_offset if self.expled else None,
4961 expl_tlen=self.expl_tlen if self.expled else None,
4962 expl_llen=self.expl_llen if self.expled else None,
4963 expl_vlen=self.expl_vlen if self.expled else None,
4964 expl_lenindef=self.expl_lenindef,
4965 lenindef=self.lenindef,
4967 for i, value in enumerate(self._value):
4968 yield value.pps(decode_path=decode_path + (str(i),))
4971 class SetOf(SequenceOf):
4972 """``SET OF`` sequence type
4974 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4977 tag_default = tag_encode(form=TagFormConstructed, num=17)
4978 asn1_type_name = "SET OF"
4981 raws = self._encoded_values()
4984 return b"".join((self.tag, len_encode(len(v)), v))
4987 def obj_by_path(pypath): # pragma: no cover
4988 """Import object specified as string Python path
4990 Modules must be separated from classes/functions with ``:``.
4992 >>> obj_by_path("foo.bar:Baz")
4993 <class 'foo.bar.Baz'>
4994 >>> obj_by_path("foo.bar:Baz.boo")
4995 <classmethod 'foo.bar.Baz.boo'>
4997 mod, objs = pypath.rsplit(":", 1)
4998 from importlib import import_module
4999 obj = import_module(mod)
5000 for obj_name in objs.split("."):
5001 obj = getattr(obj, obj_name)
5005 def generic_decoder(): # pragma: no cover
5006 # All of this below is a big hack with self references
5007 choice = PrimitiveTypes()
5008 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5009 choice.specs["SetOf"] = SetOf(schema=choice)
5011 choice.specs["SequenceOf%d" % i] = SequenceOf(
5015 choice.specs["Any"] = Any()
5017 # Class name equals to type name, to omit it from output
5018 class SEQUENCEOF(SequenceOf):
5022 def pprint_any(obj, oids=None, with_colours=False):
5023 def _pprint_pps(pps):
5025 if hasattr(pp, "_fields"):
5026 if pp.asn1_type_name == Choice.asn1_type_name:
5028 pp_kwargs = pp._asdict()
5029 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5030 pp = _pp(**pp_kwargs)
5031 yield pp_console_row(
5036 with_colours=with_colours,
5038 for row in pp_console_blob(pp):
5041 for row in _pprint_pps(pp):
5043 return "\n".join(_pprint_pps(obj.pps()))
5044 return SEQUENCEOF(), pprint_any
5047 def main(): # pragma: no cover
5049 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5050 parser.add_argument(
5054 help="Skip that number of bytes from the beginning",
5056 parser.add_argument(
5058 help="Python path to dictionary with OIDs",
5060 parser.add_argument(
5062 help="Python path to schema definition to use",
5064 parser.add_argument(
5065 "--defines-by-path",
5066 help="Python path to decoder's defines_by_path",
5068 parser.add_argument(
5070 action='store_true',
5071 help="Disallow BER encoding",
5073 parser.add_argument(
5075 type=argparse.FileType("rb"),
5076 help="Path to DER file you want to decode",
5078 args = parser.parse_args()
5079 args.DERFile.seek(args.skip)
5080 der = memoryview(args.DERFile.read())
5081 args.DERFile.close()
5082 oids = obj_by_path(args.oids) if args.oids else {}
5084 schema = obj_by_path(args.schema)
5085 from functools import partial
5086 pprinter = partial(pprint, big_blobs=True)
5088 schema, pprinter = generic_decoder()
5089 ctx = {"bered": not args.nobered}
5090 if args.defines_by_path is not None:
5091 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5092 obj, tail = schema().decode(der, ctx=ctx)
5096 with_colours=True if environ.get("NO_COLOR") is None else False,
5099 print("\nTrailing data: %s" % hexenc(tail))
5102 if __name__ == "__main__":