3 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
4 # Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 """Python ASN.1 DER/BER codec with abstract structures
21 This library allows you to marshal various structures in ASN.1 DER
22 format, unmarshal them in BER/CER/DER ones.
26 >>> Integer().decode(raw) == i
29 There are primitive types, holding single values
30 (:py:class:`pyderasn.BitString`,
31 :py:class:`pyderasn.Boolean`,
32 :py:class:`pyderasn.Enumerated`,
33 :py:class:`pyderasn.GeneralizedTime`,
34 :py:class:`pyderasn.Integer`,
35 :py:class:`pyderasn.Null`,
36 :py:class:`pyderasn.ObjectIdentifier`,
37 :py:class:`pyderasn.OctetString`,
38 :py:class:`pyderasn.UTCTime`,
39 :py:class:`various strings <pyderasn.CommonString>`
40 (:py:class:`pyderasn.BMPString`,
41 :py:class:`pyderasn.GeneralString`,
42 :py:class:`pyderasn.GraphicString`,
43 :py:class:`pyderasn.IA5String`,
44 :py:class:`pyderasn.ISO646String`,
45 :py:class:`pyderasn.NumericString`,
46 :py:class:`pyderasn.PrintableString`,
47 :py:class:`pyderasn.T61String`,
48 :py:class:`pyderasn.TeletexString`,
49 :py:class:`pyderasn.UniversalString`,
50 :py:class:`pyderasn.UTF8String`,
51 :py:class:`pyderasn.VideotexString`,
52 :py:class:`pyderasn.VisibleString`)),
53 constructed types, holding multiple primitive types
54 (:py:class:`pyderasn.Sequence`,
55 :py:class:`pyderasn.SequenceOf`,
56 :py:class:`pyderasn.Set`,
57 :py:class:`pyderasn.SetOf`),
58 and special types like
59 :py:class:`pyderasn.Any` and
60 :py:class:`pyderasn.Choice`.
68 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
69 the default tag used during coding process. You can override it with
70 either ``IMPLICIT`` (using ``impl`` keyword argument), or
71 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments take
72 raw binary string, containing that tag. You can **not** set implicit and
73 explicit tags simultaneously.
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
78 number. Pay attention that explicit tags always have *constructed* tag
79 (``tag_ctxc``), but implicit tags for primitive types are primitive
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two objects of the same type, but with different implicit/explicit tags
94 You can get object's effective tag (either default or implicited) through
95 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
112 Many objects in sequences could be ``OPTIONAL`` and could have
113 ``DEFAULT`` value. You can specify that object's property using
114 corresponding keyword arguments.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
119 Those specifications do not play any role in primitive value encoding,
120 but are taken into account when dealing with sequences holding them. For
121 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
143 Some objects give ability to set value size constraints. This is either
144 possible integer value, or allowed length of various strings and
145 sequences. Constraints are set in the following way::
150 And values satisfaction is checked as: ``MIN <= X <= MAX``.
152 For simplicity you can also set bounds the following way::
154 bounded_x = X(bounds=(MIN, MAX))
156 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
162 All objects have ``ready`` boolean property, that tells if object is
163 ready to be encoded. If that kind of action is performed on unready
164 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
166 All objects have ``copy()`` method, that returns their copy, that can be
174 Decoding is performed using ``decode()`` method. ``offset`` optional
175 argument could be used to set initial object's offset in the binary
176 data, for convenience. It returns decoded object and remaining
177 unmarshalled data (tail). Internally all work is done on
178 ``memoryview(data)``, and you can leave returning tail as a memoryview,
179 by specifying ``leavemm=True`` argument.
181 When object is decoded, ``decoded`` property is true and you can safely
182 use following properties:
184 * ``offset`` -- position including initial offset where object's tag starts
185 * ``tlen`` -- length of object's tag
186 * ``llen`` -- length of object's length value
187 * ``vlen`` -- length of object's value
188 * ``tlvlen`` -- length of the whole object
190 Pay attention that those values do **not** include anything related to
191 explicit tag. If you want to know information about it, then use:
192 ``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
193 lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
194 (that actually equals to ordinary ``tlvlen``).
196 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
203 You can specify so called context keyword argument during ``decode()``
204 invocation. It is dictionary containing various options governing
207 Currently available context options:
209 * :ref:`bered <bered_ctx>`
210 * :ref:`defines_by_path <defines_by_path_ctx>`
211 * :ref:`strict_default_existence <strict_default_existence_ctx>`
218 All objects have ``pps()`` method, that is a generator of
219 :py:class:`pyderasn.PP` namedtuple, holding various raw information
220 about the object. If ``pps`` is called on sequences, then all underlying
221 ``PP`` will be yielded.
223 You can use :py:func:`pyderasn.pp_console_row` function, converting
224 those ``PP`` to human readable string. Actually exactly it is used for
225 all object ``repr``. But it is easy to write custom formatters.
227 >>> from pyderasn import pprint
228 >>> encoded = Integer(-12345).encode()
229 >>> obj, tail = Integer().decode(encoded)
230 >>> print(pprint(obj))
231 0 [1,1, 2] INTEGER -12345
238 ASN.1 structures often have ANY and OCTET STRING fields, that are
239 DEFINED BY some previously met ObjectIdentifier. This library provides
240 ability to specify mapping between some OID and field that must be
241 decoded with specific specification.
246 :py:class:`pyderasn.ObjectIdentifier` field inside
247 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
248 necessary for decoding structures. For example, CMS (:rfc:`5652`)
251 class ContentInfo(Sequence):
253 ("contentType", ContentType(defines=((("content",), {
254 id_digestedData: DigestedData(),
255 id_signedData: SignedData(),
257 ("content", Any(expl=tag_ctxc(0))),
260 ``contentType`` field tells that it defines that ``content`` must be
261 decoded with ``SignedData`` specification, if ``contentType`` equals to
262 ``id-signedData``. The same applies to ``DigestedData``. If
263 ``contentType`` contains unknown OID, then no automatic decoding is
266 You can specify multiple fields, that will be autodecoded -- that is why
267 ``defines`` kwarg is a sequence. You can specify defined field
268 relatively or absolutely to current decode path. For example ``defines``
269 for AlgorithmIdentifier of X.509's
270 ``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
274 id_ecPublicKey: ECParameters(),
275 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
277 (("..", "subjectPublicKey"), {
278 id_rsaEncryption: RSAPublicKey(),
279 id_GostR3410_2001: OctetString(),
283 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
284 autodecode its parameters inside SPKI's algorithm and its public key
287 Following types can be automatically decoded (DEFINED BY):
289 * :py:class:`pyderasn.Any`
290 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
291 * :py:class:`pyderasn.OctetString`
292 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
293 ``Any``/``BitString``/``OctetString``-s
295 When any of those fields is automatically decoded, then ``.defined``
296 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
297 was defined, ``value`` contains corresponding decoded value. For example
298 above, ``content_info["content"].defined == (id_signedData,
301 .. _defines_by_path_ctx:
303 defines_by_path context option
304 ______________________________
306 Sometimes you either can not or do not want to explicitly set *defines*
307 in the scheme. You can dynamically apply those definitions when calling
308 ``.decode()`` method.
310 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
311 value must be sequence of following tuples::
313 (decode_path, defines)
315 where ``decode_path`` is a tuple holding so-called decode path to the
316 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
317 ``defines``, holding exactly the same value as accepted in its keyword
320 For example, again for CMS, you want to automatically decode
321 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
322 structures it may hold. Also, automatically decode ``controlSequence``
325 content_info, tail = ContentInfo().decode(data, defines_by_path=(
328 ((("content",), {id_signedData: SignedData()}),),
333 DecodePathDefBy(id_signedData),
338 id_cct_PKIData: PKIData(),
339 id_cct_PKIResponse: PKIResponse(),
345 DecodePathDefBy(id_signedData),
348 DecodePathDefBy(id_cct_PKIResponse),
354 id_cmc_recipientNonce: RecipientNonce(),
355 id_cmc_senderNonce: SenderNonce(),
356 id_cmc_statusInfoV2: CMCStatusInfoV2(),
357 id_cmc_transactionId: TransactionId(),
362 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
363 First function is useful for path construction when some automatic
364 decoding is already done. ``any`` means literally any value it meet --
365 useful for SEQUENCE/SET OF-s.
374 Currently BER support is not extensively tested.
376 By default PyDERASN accepts only DER encoded data. It always encodes to
377 DER. But you can optionally enable BER decoding with setting ``bered``
378 :ref:`context <ctx>` argument to True. Indefinite lengths and
379 constructed primitive types should be parsed successfully.
381 * If object is encoded in BER form (not the DER one), then ``bered``
382 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
383 STRING`` can contain it.
384 * If object has an indefinite length encoding, then its ``lenindef``
385 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
386 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
388 * If object has an indefinite length encoded explicit tag, then
389 ``expl_lenindef`` is set to True.
391 EOC (end-of-contents) token's length is taken in advance in object's
399 .. autoclass:: pyderasn.Boolean
404 .. autoclass:: pyderasn.Integer
409 .. autoclass:: pyderasn.BitString
414 .. autoclass:: pyderasn.OctetString
419 .. autoclass:: pyderasn.Null
424 .. autoclass:: pyderasn.ObjectIdentifier
429 .. autoclass:: pyderasn.Enumerated
433 .. autoclass:: pyderasn.CommonString
437 .. autoclass:: pyderasn.NumericString
441 .. autoclass:: pyderasn.UTCTime
442 :members: __init__, todatetime
446 .. autoclass:: pyderasn.GeneralizedTime
453 .. autoclass:: pyderasn.Choice
458 .. autoclass:: PrimitiveTypes
462 .. autoclass:: pyderasn.Any
470 .. autoclass:: pyderasn.Sequence
475 .. autoclass:: pyderasn.Set
480 .. autoclass:: pyderasn.SequenceOf
485 .. autoclass:: pyderasn.SetOf
491 .. autofunction:: pyderasn.abs_decode_path
492 .. autofunction:: pyderasn.hexenc
493 .. autofunction:: pyderasn.hexdec
494 .. autofunction:: pyderasn.tag_encode
495 .. autofunction:: pyderasn.tag_decode
496 .. autofunction:: pyderasn.tag_ctxp
497 .. autofunction:: pyderasn.tag_ctxc
498 .. autoclass:: pyderasn.Obj
499 .. autoclass:: pyderasn.DecodeError
501 .. autoclass:: pyderasn.NotEnoughData
502 .. autoclass:: pyderasn.LenIndefForm
503 .. autoclass:: pyderasn.TagMismatch
504 .. autoclass:: pyderasn.InvalidLength
505 .. autoclass:: pyderasn.InvalidOID
506 .. autoclass:: pyderasn.ObjUnknown
507 .. autoclass:: pyderasn.ObjNotReady
508 .. autoclass:: pyderasn.InvalidValueType
509 .. autoclass:: pyderasn.BoundsError
512 from codecs import getdecoder
513 from codecs import getencoder
514 from collections import namedtuple
515 from collections import OrderedDict
516 from datetime import datetime
517 from math import ceil
518 from os import environ
519 from string import digits
521 from six import add_metaclass
522 from six import binary_type
523 from six import byte2int
524 from six import indexbytes
525 from six import int2byte
526 from six import integer_types
527 from six import iterbytes
529 from six import string_types
530 from six import text_type
531 from six.moves import xrange as six_xrange
535 from termcolor import colored
537 def colored(what, *args):
580 "TagClassApplication",
584 "TagFormConstructed",
595 TagClassUniversal = 0
596 TagClassApplication = 1 << 6
597 TagClassContext = 1 << 7
598 TagClassPrivate = 1 << 6 | 1 << 7
600 TagFormConstructed = 1 << 5
603 TagClassApplication: "APPLICATION ",
604 TagClassPrivate: "PRIVATE ",
605 TagClassUniversal: "UNIV ",
611 ########################################################################
613 ########################################################################
615 class DecodeError(Exception):
616 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
618 :param str msg: reason of decode failing
619 :param klass: optional exact DecodeError inherited class (like
620 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
621 :py:exc:`InvalidLength`)
622 :param decode_path: tuple of strings. It contains human
623 readable names of the fields through which
624 decoding process has passed
625 :param int offset: binary offset where failure happened
627 super(DecodeError, self).__init__()
630 self.decode_path = decode_path
636 "" if self.klass is None else self.klass.__name__,
638 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
639 if len(self.decode_path) > 0 else ""
641 ("(at %d)" % self.offset) if self.offset > 0 else "",
647 return "%s(%s)" % (self.__class__.__name__, self)
650 class NotEnoughData(DecodeError):
654 class LenIndefForm(DecodeError):
658 class TagMismatch(DecodeError):
662 class InvalidLength(DecodeError):
666 class InvalidOID(DecodeError):
670 class ObjUnknown(ValueError):
671 def __init__(self, name):
672 super(ObjUnknown, self).__init__()
676 return "object is unknown: %s" % self.name
679 return "%s(%s)" % (self.__class__.__name__, self)
682 class ObjNotReady(ValueError):
683 def __init__(self, name):
684 super(ObjNotReady, self).__init__()
688 return "object is not ready: %s" % self.name
691 return "%s(%s)" % (self.__class__.__name__, self)
694 class InvalidValueType(ValueError):
695 def __init__(self, expected_types):
696 super(InvalidValueType, self).__init__()
697 self.expected_types = expected_types
700 return "invalid value type, expected: %s" % ", ".join(
701 [repr(t) for t in self.expected_types]
705 return "%s(%s)" % (self.__class__.__name__, self)
708 class BoundsError(ValueError):
709 def __init__(self, bound_min, value, bound_max):
710 super(BoundsError, self).__init__()
711 self.bound_min = bound_min
713 self.bound_max = bound_max
716 return "unsatisfied bounds: %s <= %s <= %s" % (
723 return "%s(%s)" % (self.__class__.__name__, self)
726 ########################################################################
728 ########################################################################
730 _hexdecoder = getdecoder("hex")
731 _hexencoder = getencoder("hex")
735 """Binary data to hexadecimal string convert
737 return _hexdecoder(data)[0]
741 """Hexadecimal string to binary data convert
743 return _hexencoder(data)[0].decode("ascii")
746 def int_bytes_len(num, byte_len=8):
749 return int(ceil(float(num.bit_length()) / byte_len))
752 def zero_ended_encode(num):
753 octets = bytearray(int_bytes_len(num, 7))
755 octets[i] = num & 0x7F
759 octets[i] = 0x80 | (num & 0x7F)
765 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
766 """Encode tag to binary form
768 :param int num: tag's number
769 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
770 :py:data:`pyderasn.TagClassContext`,
771 :py:data:`pyderasn.TagClassApplication`,
772 :py:data:`pyderasn.TagClassPrivate`)
773 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
774 :py:data:`pyderasn.TagFormConstructed`)
778 return int2byte(klass | form | num)
779 # [XX|X|11111][1.......][1.......] ... [0.......]
780 return int2byte(klass | form | 31) + zero_ended_encode(num)
784 """Decode tag from binary form
788 No validation is performed, assuming that it has already passed.
790 It returns tuple with three integers, as
791 :py:func:`pyderasn.tag_encode` accepts.
793 first_octet = byte2int(tag)
794 klass = first_octet & 0xC0
795 form = first_octet & 0x20
796 if first_octet & 0x1F < 0x1F:
797 return (klass, form, first_octet & 0x1F)
799 for octet in iterbytes(tag[1:]):
802 return (klass, form, num)
806 """Create CONTEXT PRIMITIVE tag
808 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
812 """Create CONTEXT CONSTRUCTED tag
814 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
818 """Take off tag from the data
820 :returns: (encoded tag, tag length, remaining data)
823 raise NotEnoughData("no data at all")
824 if byte2int(data) & 0x1F < 31:
825 return data[:1], 1, data[1:]
830 raise DecodeError("unfinished tag")
831 if indexbytes(data, i) & 0x80 == 0:
834 return data[:i], i, data[i:]
840 octets = bytearray(int_bytes_len(l) + 1)
841 octets[0] = 0x80 | (len(octets) - 1)
842 for i in six_xrange(len(octets) - 1, 0, -1):
848 def len_decode(data):
851 :returns: (decoded length, length's length, remaining data)
852 :raises LenIndefForm: if indefinite form encoding is met
855 raise NotEnoughData("no data at all")
856 first_octet = byte2int(data)
857 if first_octet & 0x80 == 0:
858 return first_octet, 1, data[1:]
859 octets_num = first_octet & 0x7F
860 if octets_num + 1 > len(data):
861 raise NotEnoughData("encoded length is longer than data")
864 if byte2int(data[1:]) == 0:
865 raise DecodeError("leading zeros")
867 for v in iterbytes(data[1:1 + octets_num]):
870 raise DecodeError("long form instead of short one")
871 return l, 1 + octets_num, data[1 + octets_num:]
874 ########################################################################
876 ########################################################################
878 class AutoAddSlots(type):
879 def __new__(mcs, name, bases, _dict):
880 _dict["__slots__"] = _dict.get("__slots__", ())
881 return type.__new__(mcs, name, bases, _dict)
884 @add_metaclass(AutoAddSlots)
886 """Common ASN.1 object class
888 All ASN.1 types are inherited from it. It has metaclass that
889 automatically adds ``__slots__`` to all inherited classes.
913 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
914 self._expl = getattr(self, "expl", None) if expl is None else expl
915 if self.tag != self.tag_default and self._expl is not None:
917 "implicit and explicit tags can not be set simultaneously"
919 if default is not None:
921 self.optional = optional
922 self.offset, self.llen, self.vlen = _decoded
924 self.expl_lenindef = False
925 self.lenindef = False
929 def ready(self): # pragma: no cover
930 """Is object ready to be encoded?
932 raise NotImplementedError()
934 def _assert_ready(self):
936 raise ObjNotReady(self.__class__.__name__)
940 """Is object decoded?
942 return (self.llen + self.vlen) > 0
944 def copy(self): # pragma: no cover
945 """Make a copy of object, safe to be mutated
947 raise NotImplementedError()
955 return self.tlen + self.llen + self.vlen
957 def __str__(self): # pragma: no cover
958 return self.__bytes__() if PY2 else self.__unicode__()
960 def __ne__(self, their):
961 return not(self == their)
963 def __gt__(self, their): # pragma: no cover
964 return not(self < their)
966 def __le__(self, their): # pragma: no cover
967 return (self == their) or (self < their)
969 def __ge__(self, their): # pragma: no cover
970 return (self == their) or (self > their)
972 def _encode(self): # pragma: no cover
973 raise NotImplementedError()
975 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
976 raise NotImplementedError()
980 if self._expl is None:
982 return b"".join((self._expl, len_encode(len(raw)), raw))
995 :param data: either binary or memoryview
996 :param int offset: initial data's offset
997 :param bool leavemm: do we need to leave memoryview of remaining
998 data as is, or convert it to bytes otherwise
999 :param ctx: optional :ref:`context <ctx>` governing decoding process.
1000 :param tag_only: decode only the tag, without length and contents
1001 (used only in Choice and Set structures, trying to
1002 determine if tag satisfies the scheme)
1003 :returns: (Obj, remaining data)
1007 tlv = memoryview(data)
1008 if self._expl is None:
1009 result = self._decode(
1012 decode_path=decode_path,
1021 t, tlen, lv = tag_strip(tlv)
1022 except DecodeError as err:
1023 raise err.__class__(
1025 klass=self.__class__,
1026 decode_path=decode_path,
1031 klass=self.__class__,
1032 decode_path=decode_path,
1036 l, llen, v = len_decode(lv)
1037 except LenIndefForm as err:
1038 if not ctx.get("bered", False):
1039 raise err.__class__(
1041 klass=self.__class__,
1042 decode_path=decode_path,
1046 offset += tlen + llen
1047 result = self._decode(
1050 decode_path=decode_path,
1057 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1058 if eoc_expected.tobytes() != EOC:
1061 decode_path=decode_path,
1065 obj.expl_lenindef = True
1066 except DecodeError as err:
1067 raise err.__class__(
1069 klass=self.__class__,
1070 decode_path=decode_path,
1075 raise NotEnoughData(
1076 "encoded length is longer than data",
1077 klass=self.__class__,
1078 decode_path=decode_path,
1081 result = self._decode(
1083 offset=offset + tlen + llen,
1084 decode_path=decode_path,
1091 return obj, (tail if leavemm else tail.tobytes())
1095 return self._expl is not None
1102 def expl_tlen(self):
1103 return len(self._expl)
1106 def expl_llen(self):
1107 if self.expl_lenindef:
1109 return len(len_encode(self.tlvlen))
1112 def expl_offset(self):
1113 return self.offset - self.expl_tlen - self.expl_llen
1116 def expl_vlen(self):
1120 def expl_tlvlen(self):
1121 return self.expl_tlen + self.expl_llen + self.expl_vlen
1124 class DecodePathDefBy(object):
1125 """DEFINED BY representation inside decode path
1127 __slots__ = ("defined_by",)
1129 def __init__(self, defined_by):
1130 self.defined_by = defined_by
1132 def __ne__(self, their):
1133 return not(self == their)
1135 def __eq__(self, their):
1136 if not isinstance(their, self.__class__):
1138 return self.defined_by == their.defined_by
1141 return "DEFINED BY " + str(self.defined_by)
1144 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1147 ########################################################################
1149 ########################################################################
1151 PP = namedtuple("PP", (
1176 asn1_type_name="unknown",
1193 expl_lenindef=False,
1221 def _colorize(what, colour, with_colours, attrs=("bold",)):
1222 return colored(what, colour, attrs=attrs) if with_colours else what
1237 " " if pp.expl_offset is None else
1238 ("-%d" % (pp.offset - pp.expl_offset))
1241 cols.append(_colorize(col, "red", with_colours, ()))
1242 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1243 col = _colorize(col, "green", with_colours, ())
1245 if pp.expl_lenindef:
1250 " " if ber_deoffset == 0 else
1251 _colorize(("-%d" % ber_deoffset), "red", with_colours)
1254 if len(pp.decode_path) > 0:
1255 cols.append(" ." * (len(pp.decode_path)))
1256 ent = pp.decode_path[-1]
1257 if isinstance(ent, DecodePathDefBy):
1258 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1259 value = str(ent.defined_by)
1261 oids is not None and
1262 ent.defined_by.asn1_type_name ==
1263 ObjectIdentifier.asn1_type_name and
1266 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1268 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1270 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1271 if pp.expl is not None:
1272 klass, _, num = pp.expl
1273 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1274 cols.append(_colorize(col, "blue", with_colours))
1275 if pp.impl is not None:
1276 klass, _, num = pp.impl
1277 col = "[%s%d]" % (TagClassReprs[klass], num)
1278 cols.append(_colorize(col, "blue", with_colours))
1279 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1280 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1282 cols.append(_colorize("BER", "red", with_colours))
1283 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1284 if pp.value is not None:
1286 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1288 oids is not None and
1289 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1292 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1294 if isinstance(pp.blob, binary_type):
1295 cols.append(hexenc(pp.blob))
1296 elif isinstance(pp.blob, tuple):
1297 cols.append(", ".join(pp.blob))
1299 cols.append(_colorize("OPTIONAL", "red", with_colours))
1301 cols.append(_colorize("DEFAULT", "red", with_colours))
1302 return " ".join(cols)
1305 def pp_console_blob(pp):
1306 cols = [" " * len("XXXXXYY [X,X,XXXX]YY")]
1307 if len(pp.decode_path) > 0:
1308 cols.append(" ." * (len(pp.decode_path) + 1))
1309 if isinstance(pp.blob, binary_type):
1310 blob = hexenc(pp.blob).upper()
1311 for i in range(0, len(blob), 32):
1312 chunk = blob[i:i + 32]
1313 yield " ".join(cols + [":".join(
1314 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1316 elif isinstance(pp.blob, tuple):
1317 yield " ".join(cols + [", ".join(pp.blob)])
1320 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1321 """Pretty print object
1323 :param Obj obj: object you want to pretty print
1324 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1325 from it is met, then its humand readable form is printed
1326 :param big_blobs: if large binary objects are met (like OctetString
1327 values), do we need to print them too, on separate
1329 :param with_colours: colourize output, if ``termcolor`` library
1332 def _pprint_pps(pps):
1334 if hasattr(pp, "_fields"):
1336 yield pp_console_row(
1341 with_colours=with_colours,
1343 for row in pp_console_blob(pp):
1346 yield pp_console_row(
1351 with_colours=with_colours,
1354 for row in _pprint_pps(pp):
1356 return "\n".join(_pprint_pps(obj.pps()))
1359 ########################################################################
1360 # ASN.1 primitive types
1361 ########################################################################
1364 """``BOOLEAN`` boolean type
1366 >>> b = Boolean(True)
1368 >>> b == Boolean(True)
1374 tag_default = tag_encode(1)
1375 asn1_type_name = "BOOLEAN"
1387 :param value: set the value. Either boolean type, or
1388 :py:class:`pyderasn.Boolean` object
1389 :param bytes impl: override default tag with ``IMPLICIT`` one
1390 :param bytes expl: override default tag with ``EXPLICIT`` one
1391 :param default: set default value. Type same as in ``value``
1392 :param bool optional: is object ``OPTIONAL`` in sequence
1394 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1395 self._value = None if value is None else self._value_sanitize(value)
1396 if default is not None:
1397 default = self._value_sanitize(default)
1398 self.default = self.__class__(
1404 self._value = default
1406 def _value_sanitize(self, value):
1407 if issubclass(value.__class__, Boolean):
1409 if isinstance(value, bool):
1411 raise InvalidValueType((self.__class__, bool))
1415 return self._value is not None
1418 obj = self.__class__()
1419 obj._value = self._value
1421 obj._expl = self._expl
1422 obj.default = self.default
1423 obj.optional = self.optional
1424 obj.offset = self.offset
1425 obj.llen = self.llen
1426 obj.vlen = self.vlen
1429 def __nonzero__(self):
1430 self._assert_ready()
1434 self._assert_ready()
1437 def __eq__(self, their):
1438 if isinstance(their, bool):
1439 return self._value == their
1440 if not issubclass(their.__class__, Boolean):
1443 self._value == their._value and
1444 self.tag == their.tag and
1445 self._expl == their._expl
1456 return self.__class__(
1458 impl=self.tag if impl is None else impl,
1459 expl=self._expl if expl is None else expl,
1460 default=self.default if default is None else default,
1461 optional=self.optional if optional is None else optional,
1465 self._assert_ready()
1469 (b"\xFF" if self._value else b"\x00"),
1472 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1474 t, _, lv = tag_strip(tlv)
1475 except DecodeError as err:
1476 raise err.__class__(
1478 klass=self.__class__,
1479 decode_path=decode_path,
1484 klass=self.__class__,
1485 decode_path=decode_path,
1491 l, _, v = len_decode(lv)
1492 except DecodeError as err:
1493 raise err.__class__(
1495 klass=self.__class__,
1496 decode_path=decode_path,
1500 raise InvalidLength(
1501 "Boolean's length must be equal to 1",
1502 klass=self.__class__,
1503 decode_path=decode_path,
1507 raise NotEnoughData(
1508 "encoded length is longer than data",
1509 klass=self.__class__,
1510 decode_path=decode_path,
1513 first_octet = byte2int(v)
1515 if first_octet == 0:
1517 elif first_octet == 0xFF:
1519 elif ctx.get("bered", False):
1524 "unacceptable Boolean value",
1525 klass=self.__class__,
1526 decode_path=decode_path,
1529 obj = self.__class__(
1533 default=self.default,
1534 optional=self.optional,
1535 _decoded=(offset, 1, 1),
1541 return pp_console_row(next(self.pps()))
1543 def pps(self, decode_path=()):
1545 asn1_type_name=self.asn1_type_name,
1546 obj_name=self.__class__.__name__,
1547 decode_path=decode_path,
1548 value=str(self._value) if self.ready else None,
1549 optional=self.optional,
1550 default=self == self.default,
1551 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1552 expl=None if self._expl is None else tag_decode(self._expl),
1557 expl_offset=self.expl_offset if self.expled else None,
1558 expl_tlen=self.expl_tlen if self.expled else None,
1559 expl_llen=self.expl_llen if self.expled else None,
1560 expl_vlen=self.expl_vlen if self.expled else None,
1561 expl_lenindef=self.expl_lenindef,
1567 """``INTEGER`` integer type
1569 >>> b = Integer(-123)
1571 >>> b == Integer(-123)
1576 >>> Integer(2, bounds=(1, 3))
1578 >>> Integer(5, bounds=(1, 3))
1579 Traceback (most recent call last):
1580 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1584 class Version(Integer):
1591 >>> v = Version("v1")
1598 {'v3': 2, 'v1': 0, 'v2': 1}
1600 __slots__ = ("specs", "_bound_min", "_bound_max")
1601 tag_default = tag_encode(2)
1602 asn1_type_name = "INTEGER"
1616 :param value: set the value. Either integer type, named value
1617 (if ``schema`` is specified in the class), or
1618 :py:class:`pyderasn.Integer` object
1619 :param bounds: set ``(MIN, MAX)`` value constraint.
1620 (-inf, +inf) by default
1621 :param bytes impl: override default tag with ``IMPLICIT`` one
1622 :param bytes expl: override default tag with ``EXPLICIT`` one
1623 :param default: set default value. Type same as in ``value``
1624 :param bool optional: is object ``OPTIONAL`` in sequence
1626 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1628 specs = getattr(self, "schema", {}) if _specs is None else _specs
1629 self.specs = specs if isinstance(specs, dict) else dict(specs)
1630 self._bound_min, self._bound_max = getattr(
1633 (float("-inf"), float("+inf")),
1634 ) if bounds is None else bounds
1635 if value is not None:
1636 self._value = self._value_sanitize(value)
1637 if default is not None:
1638 default = self._value_sanitize(default)
1639 self.default = self.__class__(
1645 if self._value is None:
1646 self._value = default
1648 def _value_sanitize(self, value):
1649 if issubclass(value.__class__, Integer):
1650 value = value._value
1651 elif isinstance(value, integer_types):
1653 elif isinstance(value, str):
1654 value = self.specs.get(value)
1656 raise ObjUnknown("integer value: %s" % value)
1658 raise InvalidValueType((self.__class__, int, str))
1659 if not self._bound_min <= value <= self._bound_max:
1660 raise BoundsError(self._bound_min, value, self._bound_max)
1665 return self._value is not None
1668 obj = self.__class__(_specs=self.specs)
1669 obj._value = self._value
1670 obj._bound_min = self._bound_min
1671 obj._bound_max = self._bound_max
1673 obj._expl = self._expl
1674 obj.default = self.default
1675 obj.optional = self.optional
1676 obj.offset = self.offset
1677 obj.llen = self.llen
1678 obj.vlen = self.vlen
1682 self._assert_ready()
1683 return int(self._value)
1686 self._assert_ready()
1689 bytes(self._expl or b"") +
1690 str(self._value).encode("ascii"),
1693 def __eq__(self, their):
1694 if isinstance(their, integer_types):
1695 return self._value == their
1696 if not issubclass(their.__class__, Integer):
1699 self._value == their._value and
1700 self.tag == their.tag and
1701 self._expl == their._expl
1704 def __lt__(self, their):
1705 return self._value < their._value
1709 for name, value in self.specs.items():
1710 if value == self._value:
1722 return self.__class__(
1725 (self._bound_min, self._bound_max)
1726 if bounds is None else bounds
1728 impl=self.tag if impl is None else impl,
1729 expl=self._expl if expl is None else expl,
1730 default=self.default if default is None else default,
1731 optional=self.optional if optional is None else optional,
1736 self._assert_ready()
1740 octets = bytearray([0])
1744 octets = bytearray()
1746 octets.append((value & 0xFF) ^ 0xFF)
1748 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1751 octets = bytearray()
1753 octets.append(value & 0xFF)
1755 if octets[-1] & 0x80 > 0:
1758 octets = bytes(octets)
1760 bytes_len = ceil(value.bit_length() / 8) or 1
1763 octets = value.to_bytes(
1768 except OverflowError:
1772 return b"".join((self.tag, len_encode(len(octets)), octets))
1774 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1776 t, _, lv = tag_strip(tlv)
1777 except DecodeError as err:
1778 raise err.__class__(
1780 klass=self.__class__,
1781 decode_path=decode_path,
1786 klass=self.__class__,
1787 decode_path=decode_path,
1793 l, llen, v = len_decode(lv)
1794 except DecodeError as err:
1795 raise err.__class__(
1797 klass=self.__class__,
1798 decode_path=decode_path,
1802 raise NotEnoughData(
1803 "encoded length is longer than data",
1804 klass=self.__class__,
1805 decode_path=decode_path,
1809 raise NotEnoughData(
1811 klass=self.__class__,
1812 decode_path=decode_path,
1815 v, tail = v[:l], v[l:]
1816 first_octet = byte2int(v)
1818 second_octet = byte2int(v[1:])
1820 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1821 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1824 "non normalized integer",
1825 klass=self.__class__,
1826 decode_path=decode_path,
1831 if first_octet & 0x80 > 0:
1832 octets = bytearray()
1833 for octet in bytearray(v):
1834 octets.append(octet ^ 0xFF)
1835 for octet in octets:
1836 value = (value << 8) | octet
1840 for octet in bytearray(v):
1841 value = (value << 8) | octet
1843 value = int.from_bytes(v, byteorder="big", signed=True)
1845 obj = self.__class__(
1847 bounds=(self._bound_min, self._bound_max),
1850 default=self.default,
1851 optional=self.optional,
1853 _decoded=(offset, llen, l),
1855 except BoundsError as err:
1858 klass=self.__class__,
1859 decode_path=decode_path,
1865 return pp_console_row(next(self.pps()))
1867 def pps(self, decode_path=()):
1869 asn1_type_name=self.asn1_type_name,
1870 obj_name=self.__class__.__name__,
1871 decode_path=decode_path,
1872 value=(self.named or str(self._value)) if self.ready else None,
1873 optional=self.optional,
1874 default=self == self.default,
1875 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1876 expl=None if self._expl is None else tag_decode(self._expl),
1881 expl_offset=self.expl_offset if self.expled else None,
1882 expl_tlen=self.expl_tlen if self.expled else None,
1883 expl_llen=self.expl_llen if self.expled else None,
1884 expl_vlen=self.expl_vlen if self.expled else None,
1885 expl_lenindef=self.expl_lenindef,
1889 class BitString(Obj):
1890 """``BIT STRING`` bit string type
1892 >>> BitString(b"hello world")
1893 BIT STRING 88 bits 68656c6c6f20776f726c64
1896 >>> b == b"hello world"
1901 >>> BitString("'0A3B5F291CD'H")
1902 BIT STRING 44 bits 0a3b5f291cd0
1903 >>> b = BitString("'010110000000'B")
1904 BIT STRING 12 bits 5800
1907 >>> b[0], b[1], b[2], b[3]
1908 (False, True, False, True)
1912 [False, True, False, True, True, False, False, False, False, False, False, False]
1916 class KeyUsage(BitString):
1918 ("digitalSignature", 0),
1919 ("nonRepudiation", 1),
1920 ("keyEncipherment", 2),
1923 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1924 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1926 ['nonRepudiation', 'keyEncipherment']
1928 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1930 __slots__ = ("tag_constructed", "specs", "defined")
1931 tag_default = tag_encode(3)
1932 asn1_type_name = "BIT STRING"
1945 :param value: set the value. Either binary type, tuple of named
1946 values (if ``schema`` is specified in the class),
1947 string in ``'XXX...'B`` form, or
1948 :py:class:`pyderasn.BitString` object
1949 :param bytes impl: override default tag with ``IMPLICIT`` one
1950 :param bytes expl: override default tag with ``EXPLICIT`` one
1951 :param default: set default value. Type same as in ``value``
1952 :param bool optional: is object ``OPTIONAL`` in sequence
1954 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1955 specs = getattr(self, "schema", {}) if _specs is None else _specs
1956 self.specs = specs if isinstance(specs, dict) else dict(specs)
1957 self._value = None if value is None else self._value_sanitize(value)
1958 if default is not None:
1959 default = self._value_sanitize(default)
1960 self.default = self.__class__(
1966 self._value = default
1968 tag_klass, _, tag_num = tag_decode(self.tag)
1969 self.tag_constructed = tag_encode(
1971 form=TagFormConstructed,
1975 def _bits2octets(self, bits):
1976 if len(self.specs) > 0:
1977 bits = bits.rstrip("0")
1979 bits += "0" * ((8 - (bit_len % 8)) % 8)
1980 octets = bytearray(len(bits) // 8)
1981 for i in six_xrange(len(octets)):
1982 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1983 return bit_len, bytes(octets)
1985 def _value_sanitize(self, value):
1986 if issubclass(value.__class__, BitString):
1988 if isinstance(value, (string_types, binary_type)):
1990 isinstance(value, string_types) and
1991 value.startswith("'")
1993 if value.endswith("'B"):
1995 if not set(value) <= set(("0", "1")):
1996 raise ValueError("B's coding contains unacceptable chars")
1997 return self._bits2octets(value)
1998 elif value.endswith("'H"):
2002 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2005 raise InvalidValueType((self.__class__, string_types, binary_type))
2006 elif isinstance(value, binary_type):
2007 return (len(value) * 8, value)
2009 raise InvalidValueType((self.__class__, string_types, binary_type))
2010 if isinstance(value, tuple):
2013 isinstance(value[0], integer_types) and
2014 isinstance(value[1], binary_type)
2019 bit = self.specs.get(name)
2021 raise ObjUnknown("BitString value: %s" % name)
2024 return self._bits2octets("")
2026 return self._bits2octets("".join(
2027 ("1" if bit in bits else "0")
2028 for bit in six_xrange(max(bits) + 1)
2030 raise InvalidValueType((self.__class__, binary_type, string_types))
2034 return self._value is not None
2037 obj = self.__class__(_specs=self.specs)
2039 if value is not None:
2040 value = (value[0], value[1])
2043 obj._expl = self._expl
2044 obj.default = self.default
2045 obj.optional = self.optional
2046 obj.offset = self.offset
2047 obj.llen = self.llen
2048 obj.vlen = self.vlen
2052 self._assert_ready()
2053 for i in six_xrange(self._value[0]):
2058 self._assert_ready()
2059 return self._value[0]
2061 def __bytes__(self):
2062 self._assert_ready()
2063 return self._value[1]
2065 def __eq__(self, their):
2066 if isinstance(their, bytes):
2067 return self._value[1] == their
2068 if not issubclass(their.__class__, BitString):
2071 self._value == their._value and
2072 self.tag == their.tag and
2073 self._expl == their._expl
2078 return [name for name, bit in self.specs.items() if self[bit]]
2088 return self.__class__(
2090 impl=self.tag if impl is None else impl,
2091 expl=self._expl if expl is None else expl,
2092 default=self.default if default is None else default,
2093 optional=self.optional if optional is None else optional,
2097 def __getitem__(self, key):
2098 if isinstance(key, int):
2099 bit_len, octets = self._value
2103 byte2int(memoryview(octets)[key // 8:]) >>
2106 if isinstance(key, string_types):
2107 value = self.specs.get(key)
2109 raise ObjUnknown("BitString value: %s" % key)
2111 raise InvalidValueType((int, str))
2114 self._assert_ready()
2115 bit_len, octets = self._value
2118 len_encode(len(octets) + 1),
2119 int2byte((8 - bit_len % 8) % 8),
2123 def _decode_chunk(self, lv, offset, decode_path, ctx):
2125 l, llen, v = len_decode(lv)
2126 except DecodeError as err:
2127 raise err.__class__(
2129 klass=self.__class__,
2130 decode_path=decode_path,
2134 raise NotEnoughData(
2135 "encoded length is longer than data",
2136 klass=self.__class__,
2137 decode_path=decode_path,
2141 raise NotEnoughData(
2143 klass=self.__class__,
2144 decode_path=decode_path,
2147 pad_size = byte2int(v)
2148 if l == 1 and pad_size != 0:
2150 "invalid empty value",
2151 klass=self.__class__,
2152 decode_path=decode_path,
2158 klass=self.__class__,
2159 decode_path=decode_path,
2162 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2165 klass=self.__class__,
2166 decode_path=decode_path,
2169 v, tail = v[:l], v[l:]
2170 obj = self.__class__(
2171 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2174 default=self.default,
2175 optional=self.optional,
2177 _decoded=(offset, llen, l),
2181 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2183 t, tlen, lv = tag_strip(tlv)
2184 except DecodeError as err:
2185 raise err.__class__(
2187 klass=self.__class__,
2188 decode_path=decode_path,
2194 return self._decode_chunk(lv, offset, decode_path, ctx)
2195 if t == self.tag_constructed:
2196 if not ctx.get("bered", False):
2198 msg="unallowed BER constructed encoding",
2199 decode_path=decode_path,
2206 l, llen, v = len_decode(lv)
2207 except LenIndefForm:
2208 llen, l, v = 1, 0, lv[1:]
2210 except DecodeError as err:
2211 raise err.__class__(
2213 klass=self.__class__,
2214 decode_path=decode_path,
2217 if l > 0 and l > len(v):
2218 raise NotEnoughData(
2219 "encoded length is longer than data",
2220 klass=self.__class__,
2221 decode_path=decode_path,
2224 if not lenindef and l == 0:
2225 raise NotEnoughData(
2227 klass=self.__class__,
2228 decode_path=decode_path,
2232 sub_offset = offset + tlen + llen
2236 if v[:EOC_LEN].tobytes() == EOC:
2243 msg="chunk out of bounds",
2244 decode_path=len(chunks) - 1,
2245 offset=chunks[-1].offset,
2247 sub_decode_path = decode_path + (str(len(chunks)),)
2249 chunk, v_tail = BitString().decode(
2252 decode_path=sub_decode_path,
2258 msg="expected BitString encoded chunk",
2259 decode_path=sub_decode_path,
2262 chunks.append(chunk)
2263 sub_offset += chunk.tlvlen
2264 vlen += chunk.tlvlen
2266 if len(chunks) == 0:
2269 decode_path=decode_path,
2274 for chunk_i, chunk in enumerate(chunks[:-1]):
2275 if chunk.bit_len % 8 != 0:
2277 msg="BitString chunk is not multiple of 8 bit",
2278 decode_path=decode_path + (str(chunk_i),),
2279 offset=chunk.offset,
2281 values.append(bytes(chunk))
2282 bit_len += chunk.bit_len
2283 chunk_last = chunks[-1]
2284 values.append(bytes(chunk_last))
2285 bit_len += chunk_last.bit_len
2286 obj = self.__class__(
2287 value=(bit_len, b"".join(values)),
2290 default=self.default,
2291 optional=self.optional,
2293 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2295 obj.lenindef = lenindef
2297 return obj, (v[EOC_LEN:] if lenindef else v)
2299 klass=self.__class__,
2300 decode_path=decode_path,
2305 return pp_console_row(next(self.pps()))
2307 def pps(self, decode_path=()):
2311 bit_len, blob = self._value
2312 value = "%d bits" % bit_len
2313 if len(self.specs) > 0:
2314 blob = tuple(self.named)
2316 asn1_type_name=self.asn1_type_name,
2317 obj_name=self.__class__.__name__,
2318 decode_path=decode_path,
2321 optional=self.optional,
2322 default=self == self.default,
2323 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2324 expl=None if self._expl is None else tag_decode(self._expl),
2329 expl_offset=self.expl_offset if self.expled else None,
2330 expl_tlen=self.expl_tlen if self.expled else None,
2331 expl_llen=self.expl_llen if self.expled else None,
2332 expl_vlen=self.expl_vlen if self.expled else None,
2333 expl_lenindef=self.expl_lenindef,
2334 lenindef=self.lenindef,
2337 defined_by, defined = self.defined or (None, None)
2338 if defined_by is not None:
2340 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2344 class OctetString(Obj):
2345 """``OCTET STRING`` binary string type
2347 >>> s = OctetString(b"hello world")
2348 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2349 >>> s == OctetString(b"hello world")
2354 >>> OctetString(b"hello", bounds=(4, 4))
2355 Traceback (most recent call last):
2356 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2357 >>> OctetString(b"hell", bounds=(4, 4))
2358 OCTET STRING 4 bytes 68656c6c
2360 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2361 tag_default = tag_encode(4)
2362 asn1_type_name = "OCTET STRING"
2375 :param value: set the value. Either binary type, or
2376 :py:class:`pyderasn.OctetString` object
2377 :param bounds: set ``(MIN, MAX)`` value size constraint.
2378 (-inf, +inf) by default
2379 :param bytes impl: override default tag with ``IMPLICIT`` one
2380 :param bytes expl: override default tag with ``EXPLICIT`` one
2381 :param default: set default value. Type same as in ``value``
2382 :param bool optional: is object ``OPTIONAL`` in sequence
2384 super(OctetString, self).__init__(
2392 self._bound_min, self._bound_max = getattr(
2396 ) if bounds is None else bounds
2397 if value is not None:
2398 self._value = self._value_sanitize(value)
2399 if default is not None:
2400 default = self._value_sanitize(default)
2401 self.default = self.__class__(
2406 if self._value is None:
2407 self._value = default
2409 tag_klass, _, tag_num = tag_decode(self.tag)
2410 self.tag_constructed = tag_encode(
2412 form=TagFormConstructed,
2416 def _value_sanitize(self, value):
2417 if issubclass(value.__class__, OctetString):
2418 value = value._value
2419 elif isinstance(value, binary_type):
2422 raise InvalidValueType((self.__class__, bytes))
2423 if not self._bound_min <= len(value) <= self._bound_max:
2424 raise BoundsError(self._bound_min, len(value), self._bound_max)
2429 return self._value is not None
2432 obj = self.__class__()
2433 obj._value = self._value
2434 obj._bound_min = self._bound_min
2435 obj._bound_max = self._bound_max
2437 obj._expl = self._expl
2438 obj.default = self.default
2439 obj.optional = self.optional
2440 obj.offset = self.offset
2441 obj.llen = self.llen
2442 obj.vlen = self.vlen
2445 def __bytes__(self):
2446 self._assert_ready()
2449 def __eq__(self, their):
2450 if isinstance(their, binary_type):
2451 return self._value == their
2452 if not issubclass(their.__class__, OctetString):
2455 self._value == their._value and
2456 self.tag == their.tag and
2457 self._expl == their._expl
2460 def __lt__(self, their):
2461 return self._value < their._value
2472 return self.__class__(
2475 (self._bound_min, self._bound_max)
2476 if bounds is None else bounds
2478 impl=self.tag if impl is None else impl,
2479 expl=self._expl if expl is None else expl,
2480 default=self.default if default is None else default,
2481 optional=self.optional if optional is None else optional,
2485 self._assert_ready()
2488 len_encode(len(self._value)),
2492 def _decode_chunk(self, lv, offset, decode_path, ctx):
2494 l, llen, v = len_decode(lv)
2495 except DecodeError as err:
2496 raise err.__class__(
2498 klass=self.__class__,
2499 decode_path=decode_path,
2503 raise NotEnoughData(
2504 "encoded length is longer than data",
2505 klass=self.__class__,
2506 decode_path=decode_path,
2509 v, tail = v[:l], v[l:]
2511 obj = self.__class__(
2513 bounds=(self._bound_min, self._bound_max),
2516 default=self.default,
2517 optional=self.optional,
2518 _decoded=(offset, llen, l),
2520 except DecodeError as err:
2523 klass=self.__class__,
2524 decode_path=decode_path,
2527 except BoundsError as err:
2530 klass=self.__class__,
2531 decode_path=decode_path,
2536 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2538 t, tlen, lv = tag_strip(tlv)
2539 except DecodeError as err:
2540 raise err.__class__(
2542 klass=self.__class__,
2543 decode_path=decode_path,
2549 return self._decode_chunk(lv, offset, decode_path, ctx)
2550 if t == self.tag_constructed:
2551 if not ctx.get("bered", False):
2553 msg="unallowed BER constructed encoding",
2554 decode_path=decode_path,
2561 l, llen, v = len_decode(lv)
2562 except LenIndefForm:
2563 llen, l, v = 1, 0, lv[1:]
2565 except DecodeError as err:
2566 raise err.__class__(
2568 klass=self.__class__,
2569 decode_path=decode_path,
2572 if l > 0 and l > len(v):
2573 raise NotEnoughData(
2574 "encoded length is longer than data",
2575 klass=self.__class__,
2576 decode_path=decode_path,
2579 if not lenindef and l == 0:
2580 raise NotEnoughData(
2582 klass=self.__class__,
2583 decode_path=decode_path,
2587 sub_offset = offset + tlen + llen
2591 if v[:EOC_LEN].tobytes() == EOC:
2598 msg="chunk out of bounds",
2599 decode_path=len(chunks) - 1,
2600 offset=chunks[-1].offset,
2602 sub_decode_path = decode_path + (str(len(chunks)),)
2604 chunk, v_tail = OctetString().decode(
2607 decode_path=sub_decode_path,
2613 msg="expected OctetString encoded chunk",
2614 decode_path=sub_decode_path,
2617 chunks.append(chunk)
2618 sub_offset += chunk.tlvlen
2619 vlen += chunk.tlvlen
2621 if len(chunks) == 0:
2624 decode_path=decode_path,
2628 obj = self.__class__(
2629 value=b"".join(bytes(chunk) for chunk in chunks),
2630 bounds=(self._bound_min, self._bound_max),
2633 default=self.default,
2634 optional=self.optional,
2635 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2637 except DecodeError as err:
2640 klass=self.__class__,
2641 decode_path=decode_path,
2644 except BoundsError as err:
2647 klass=self.__class__,
2648 decode_path=decode_path,
2651 obj.lenindef = lenindef
2653 return obj, (v[EOC_LEN:] if lenindef else v)
2655 klass=self.__class__,
2656 decode_path=decode_path,
2661 return pp_console_row(next(self.pps()))
2663 def pps(self, decode_path=()):
2665 asn1_type_name=self.asn1_type_name,
2666 obj_name=self.__class__.__name__,
2667 decode_path=decode_path,
2668 value=("%d bytes" % len(self._value)) if self.ready else None,
2669 blob=self._value if self.ready else None,
2670 optional=self.optional,
2671 default=self == self.default,
2672 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2673 expl=None if self._expl is None else tag_decode(self._expl),
2678 expl_offset=self.expl_offset if self.expled else None,
2679 expl_tlen=self.expl_tlen if self.expled else None,
2680 expl_llen=self.expl_llen if self.expled else None,
2681 expl_vlen=self.expl_vlen if self.expled else None,
2682 expl_lenindef=self.expl_lenindef,
2683 lenindef=self.lenindef,
2686 defined_by, defined = self.defined or (None, None)
2687 if defined_by is not None:
2689 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2694 """``NULL`` null object
2702 tag_default = tag_encode(5)
2703 asn1_type_name = "NULL"
2707 value=None, # unused, but Sequence passes it
2714 :param bytes impl: override default tag with ``IMPLICIT`` one
2715 :param bytes expl: override default tag with ``EXPLICIT`` one
2716 :param bool optional: is object ``OPTIONAL`` in sequence
2718 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2726 obj = self.__class__()
2728 obj._expl = self._expl
2729 obj.default = self.default
2730 obj.optional = self.optional
2731 obj.offset = self.offset
2732 obj.llen = self.llen
2733 obj.vlen = self.vlen
2736 def __eq__(self, their):
2737 if not issubclass(their.__class__, Null):
2740 self.tag == their.tag and
2741 self._expl == their._expl
2751 return self.__class__(
2752 impl=self.tag if impl is None else impl,
2753 expl=self._expl if expl is None else expl,
2754 optional=self.optional if optional is None else optional,
2758 return self.tag + len_encode(0)
2760 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2762 t, _, lv = tag_strip(tlv)
2763 except DecodeError as err:
2764 raise err.__class__(
2766 klass=self.__class__,
2767 decode_path=decode_path,
2772 klass=self.__class__,
2773 decode_path=decode_path,
2779 l, _, v = len_decode(lv)
2780 except DecodeError as err:
2781 raise err.__class__(
2783 klass=self.__class__,
2784 decode_path=decode_path,
2788 raise InvalidLength(
2789 "Null must have zero length",
2790 klass=self.__class__,
2791 decode_path=decode_path,
2794 obj = self.__class__(
2797 optional=self.optional,
2798 _decoded=(offset, 1, 0),
2803 return pp_console_row(next(self.pps()))
2805 def pps(self, decode_path=()):
2807 asn1_type_name=self.asn1_type_name,
2808 obj_name=self.__class__.__name__,
2809 decode_path=decode_path,
2810 optional=self.optional,
2811 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2812 expl=None if self._expl is None else tag_decode(self._expl),
2817 expl_offset=self.expl_offset if self.expled else None,
2818 expl_tlen=self.expl_tlen if self.expled else None,
2819 expl_llen=self.expl_llen if self.expled else None,
2820 expl_vlen=self.expl_vlen if self.expled else None,
2821 expl_lenindef=self.expl_lenindef,
2825 class ObjectIdentifier(Obj):
2826 """``OBJECT IDENTIFIER`` OID type
2828 >>> oid = ObjectIdentifier((1, 2, 3))
2829 OBJECT IDENTIFIER 1.2.3
2830 >>> oid == ObjectIdentifier("1.2.3")
2836 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2837 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2839 >>> str(ObjectIdentifier((3, 1)))
2840 Traceback (most recent call last):
2841 pyderasn.InvalidOID: unacceptable first arc value
2843 __slots__ = ("defines",)
2844 tag_default = tag_encode(6)
2845 asn1_type_name = "OBJECT IDENTIFIER"
2858 :param value: set the value. Either tuples of integers,
2859 string of "."-concatenated integers, or
2860 :py:class:`pyderasn.ObjectIdentifier` object
2861 :param defines: sequence of tuples. Each tuple has two elements.
2862 First one is relative to current one decode
2863 path, aiming to the field defined by that OID.
2864 Read about relative path in
2865 :py:func:`pyderasn.abs_decode_path`. Second
2866 tuple element is ``{OID: pyderasn.Obj()}``
2867 dictionary, mapping between current OID value
2868 and structure applied to defined field.
2869 :ref:`Read about DEFINED BY <definedby>`
2870 :param bytes impl: override default tag with ``IMPLICIT`` one
2871 :param bytes expl: override default tag with ``EXPLICIT`` one
2872 :param default: set default value. Type same as in ``value``
2873 :param bool optional: is object ``OPTIONAL`` in sequence
2875 super(ObjectIdentifier, self).__init__(
2883 if value is not None:
2884 self._value = self._value_sanitize(value)
2885 if default is not None:
2886 default = self._value_sanitize(default)
2887 self.default = self.__class__(
2892 if self._value is None:
2893 self._value = default
2894 self.defines = defines
2896 def __add__(self, their):
2897 if isinstance(their, self.__class__):
2898 return self.__class__(self._value + their._value)
2899 if isinstance(their, tuple):
2900 return self.__class__(self._value + their)
2901 raise InvalidValueType((self.__class__, tuple))
2903 def _value_sanitize(self, value):
2904 if issubclass(value.__class__, ObjectIdentifier):
2906 if isinstance(value, string_types):
2908 value = tuple(int(arc) for arc in value.split("."))
2910 raise InvalidOID("unacceptable arcs values")
2911 if isinstance(value, tuple):
2913 raise InvalidOID("less than 2 arcs")
2914 first_arc = value[0]
2915 if first_arc in (0, 1):
2916 if not (0 <= value[1] <= 39):
2917 raise InvalidOID("second arc is too wide")
2918 elif first_arc == 2:
2921 raise InvalidOID("unacceptable first arc value")
2923 raise InvalidValueType((self.__class__, str, tuple))
2927 return self._value is not None
2930 obj = self.__class__()
2931 obj._value = self._value
2932 obj.defines = self.defines
2934 obj._expl = self._expl
2935 obj.default = self.default
2936 obj.optional = self.optional
2937 obj.offset = self.offset
2938 obj.llen = self.llen
2939 obj.vlen = self.vlen
2943 self._assert_ready()
2944 return iter(self._value)
2947 return ".".join(str(arc) for arc in self._value or ())
2950 self._assert_ready()
2953 bytes(self._expl or b"") +
2954 str(self._value).encode("ascii"),
2957 def __eq__(self, their):
2958 if isinstance(their, tuple):
2959 return self._value == their
2960 if not issubclass(their.__class__, ObjectIdentifier):
2963 self.tag == their.tag and
2964 self._expl == their._expl and
2965 self._value == their._value
2968 def __lt__(self, their):
2969 return self._value < their._value
2980 return self.__class__(
2982 defines=self.defines if defines is None else defines,
2983 impl=self.tag if impl is None else impl,
2984 expl=self._expl if expl is None else expl,
2985 default=self.default if default is None else default,
2986 optional=self.optional if optional is None else optional,
2990 self._assert_ready()
2992 first_value = value[1]
2993 first_arc = value[0]
2996 elif first_arc == 1:
2998 elif first_arc == 2:
3000 else: # pragma: no cover
3001 raise RuntimeError("invalid arc is stored")
3002 octets = [zero_ended_encode(first_value)]
3003 for arc in value[2:]:
3004 octets.append(zero_ended_encode(arc))
3005 v = b"".join(octets)
3006 return b"".join((self.tag, len_encode(len(v)), v))
3008 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3010 t, _, lv = tag_strip(tlv)
3011 except DecodeError as err:
3012 raise err.__class__(
3014 klass=self.__class__,
3015 decode_path=decode_path,
3020 klass=self.__class__,
3021 decode_path=decode_path,
3027 l, llen, v = len_decode(lv)
3028 except DecodeError as err:
3029 raise err.__class__(
3031 klass=self.__class__,
3032 decode_path=decode_path,
3036 raise NotEnoughData(
3037 "encoded length is longer than data",
3038 klass=self.__class__,
3039 decode_path=decode_path,
3043 raise NotEnoughData(
3045 klass=self.__class__,
3046 decode_path=decode_path,
3049 v, tail = v[:l], v[l:]
3055 octet = indexbytes(v, i)
3056 arc = (arc << 7) | (octet & 0x7F)
3057 if octet & 0x80 == 0:
3065 klass=self.__class__,
3066 decode_path=decode_path,
3070 second_arc = arcs[0]
3071 if 0 <= second_arc <= 39:
3073 elif 40 <= second_arc <= 79:
3079 obj = self.__class__(
3080 value=tuple([first_arc, second_arc] + arcs[1:]),
3083 default=self.default,
3084 optional=self.optional,
3085 _decoded=(offset, llen, l),
3090 return pp_console_row(next(self.pps()))
3092 def pps(self, decode_path=()):
3094 asn1_type_name=self.asn1_type_name,
3095 obj_name=self.__class__.__name__,
3096 decode_path=decode_path,
3097 value=str(self) if self.ready else None,
3098 optional=self.optional,
3099 default=self == self.default,
3100 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3101 expl=None if self._expl is None else tag_decode(self._expl),
3106 expl_offset=self.expl_offset if self.expled else None,
3107 expl_tlen=self.expl_tlen if self.expled else None,
3108 expl_llen=self.expl_llen if self.expled else None,
3109 expl_vlen=self.expl_vlen if self.expled else None,
3110 expl_lenindef=self.expl_lenindef,
3114 class Enumerated(Integer):
3115 """``ENUMERATED`` integer type
3117 This type is identical to :py:class:`pyderasn.Integer`, but requires
3118 schema to be specified and does not accept values missing from it.
3121 tag_default = tag_encode(10)
3122 asn1_type_name = "ENUMERATED"
3133 bounds=None, # dummy argument, workability for Integer.decode
3135 super(Enumerated, self).__init__(
3144 if len(self.specs) == 0:
3145 raise ValueError("schema must be specified")
3147 def _value_sanitize(self, value):
3148 if isinstance(value, self.__class__):
3149 value = value._value
3150 elif isinstance(value, integer_types):
3151 if value not in list(self.specs.values()):
3153 "unknown integer value: %s" % value,
3154 klass=self.__class__,
3156 elif isinstance(value, string_types):
3157 value = self.specs.get(value)
3159 raise ObjUnknown("integer value: %s" % value)
3161 raise InvalidValueType((self.__class__, int, str))
3165 obj = self.__class__(_specs=self.specs)
3166 obj._value = self._value
3167 obj._bound_min = self._bound_min
3168 obj._bound_max = self._bound_max
3170 obj._expl = self._expl
3171 obj.default = self.default
3172 obj.optional = self.optional
3173 obj.offset = self.offset
3174 obj.llen = self.llen
3175 obj.vlen = self.vlen
3187 return self.__class__(
3189 impl=self.tag if impl is None else impl,
3190 expl=self._expl if expl is None else expl,
3191 default=self.default if default is None else default,
3192 optional=self.optional if optional is None else optional,
3197 class CommonString(OctetString):
3198 """Common class for all strings
3200 Everything resembles :py:class:`pyderasn.OctetString`, except
3201 ability to deal with unicode text strings.
3203 >>> hexenc("привет мир".encode("utf-8"))
3204 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3205 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3207 >>> s = UTF8String("привет мир")
3208 UTF8String UTF8String привет мир
3210 'привет мир'
3211 >>> hexenc(bytes(s))
3212 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3214 >>> PrintableString("привет мир")
3215 Traceback (most recent call last):
3216 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3218 >>> BMPString("ада", bounds=(2, 2))
3219 Traceback (most recent call last):
3220 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3221 >>> s = BMPString("ад", bounds=(2, 2))
3224 >>> hexenc(bytes(s))
3232 * - :py:class:`pyderasn.UTF8String`
3234 * - :py:class:`pyderasn.NumericString`
3236 * - :py:class:`pyderasn.PrintableString`
3238 * - :py:class:`pyderasn.TeletexString`
3240 * - :py:class:`pyderasn.T61String`
3242 * - :py:class:`pyderasn.VideotexString`
3244 * - :py:class:`pyderasn.IA5String`
3246 * - :py:class:`pyderasn.GraphicString`
3248 * - :py:class:`pyderasn.VisibleString`
3250 * - :py:class:`pyderasn.ISO646String`
3252 * - :py:class:`pyderasn.GeneralString`
3254 * - :py:class:`pyderasn.UniversalString`
3256 * - :py:class:`pyderasn.BMPString`
3259 __slots__ = ("encoding",)
3261 def _value_sanitize(self, value):
3263 value_decoded = None
3264 if isinstance(value, self.__class__):
3265 value_raw = value._value
3266 elif isinstance(value, text_type):
3267 value_decoded = value
3268 elif isinstance(value, binary_type):
3271 raise InvalidValueType((self.__class__, text_type, binary_type))
3274 value_decoded.encode(self.encoding)
3275 if value_raw is None else value_raw
3278 value_raw.decode(self.encoding)
3279 if value_decoded is None else value_decoded
3281 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3282 raise DecodeError(str(err))
3283 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3291 def __eq__(self, their):
3292 if isinstance(their, binary_type):
3293 return self._value == their
3294 if isinstance(their, text_type):
3295 return self._value == their.encode(self.encoding)
3296 if not isinstance(their, self.__class__):
3299 self._value == their._value and
3300 self.tag == their.tag and
3301 self._expl == their._expl
3304 def __unicode__(self):
3306 return self._value.decode(self.encoding)
3307 return text_type(self._value)
3310 return pp_console_row(next(self.pps(no_unicode=PY2)))
3312 def pps(self, decode_path=(), no_unicode=False):
3315 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3317 asn1_type_name=self.asn1_type_name,
3318 obj_name=self.__class__.__name__,
3319 decode_path=decode_path,
3321 optional=self.optional,
3322 default=self == self.default,
3323 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3324 expl=None if self._expl is None else tag_decode(self._expl),
3329 expl_offset=self.expl_offset if self.expled else None,
3330 expl_tlen=self.expl_tlen if self.expled else None,
3331 expl_llen=self.expl_llen if self.expled else None,
3332 expl_vlen=self.expl_vlen if self.expled else None,
3333 expl_lenindef=self.expl_lenindef,
3337 class UTF8String(CommonString):
3339 tag_default = tag_encode(12)
3341 asn1_type_name = "UTF8String"
3344 class NumericString(CommonString):
3347 Its value is properly sanitized: only ASCII digits can be stored.
3350 tag_default = tag_encode(18)
3352 asn1_type_name = "NumericString"
3353 allowable_chars = set(digits.encode("ascii"))
3355 def _value_sanitize(self, value):
3356 value = super(NumericString, self)._value_sanitize(value)
3357 if not set(value) <= self.allowable_chars:
3358 raise DecodeError("non-numeric value")
3362 class PrintableString(CommonString):
3364 tag_default = tag_encode(19)
3366 asn1_type_name = "PrintableString"
3369 class TeletexString(CommonString):
3371 tag_default = tag_encode(20)
3373 asn1_type_name = "TeletexString"
3376 class T61String(TeletexString):
3378 asn1_type_name = "T61String"
3381 class VideotexString(CommonString):
3383 tag_default = tag_encode(21)
3384 encoding = "iso-8859-1"
3385 asn1_type_name = "VideotexString"
3388 class IA5String(CommonString):
3390 tag_default = tag_encode(22)
3392 asn1_type_name = "IA5"
3395 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3396 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3397 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3400 class UTCTime(CommonString):
3401 """``UTCTime`` datetime type
3403 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3404 UTCTime UTCTime 2017-09-30T22:07:50
3410 datetime.datetime(2017, 9, 30, 22, 7, 50)
3411 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3412 datetime.datetime(1957, 9, 30, 22, 7, 50)
3415 tag_default = tag_encode(23)
3417 asn1_type_name = "UTCTime"
3419 fmt = "%y%m%d%H%M%SZ"
3429 bounds=None, # dummy argument, workability for OctetString.decode
3432 :param value: set the value. Either datetime type, or
3433 :py:class:`pyderasn.UTCTime` object
3434 :param bytes impl: override default tag with ``IMPLICIT`` one
3435 :param bytes expl: override default tag with ``EXPLICIT`` one
3436 :param default: set default value. Type same as in ``value``
3437 :param bool optional: is object ``OPTIONAL`` in sequence
3439 super(UTCTime, self).__init__(
3447 if value is not None:
3448 self._value = self._value_sanitize(value)
3449 if default is not None:
3450 default = self._value_sanitize(default)
3451 self.default = self.__class__(
3456 if self._value is None:
3457 self._value = default
3459 def _value_sanitize(self, value):
3460 if isinstance(value, self.__class__):
3462 if isinstance(value, datetime):
3463 return value.strftime(self.fmt).encode("ascii")
3464 if isinstance(value, binary_type):
3465 value_decoded = value.decode("ascii")
3466 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3468 datetime.strptime(value_decoded, self.fmt)
3470 raise DecodeError("invalid UTCTime format")
3473 raise DecodeError("invalid UTCTime length")
3474 raise InvalidValueType((self.__class__, datetime))
3476 def __eq__(self, their):
3477 if isinstance(their, binary_type):
3478 return self._value == their
3479 if isinstance(their, datetime):
3480 return self.todatetime() == their
3481 if not isinstance(their, self.__class__):
3484 self._value == their._value and
3485 self.tag == their.tag and
3486 self._expl == their._expl
3489 def todatetime(self):
3490 """Convert to datetime
3494 Pay attention that UTCTime can not hold full year, so all years
3495 having < 50 years are treated as 20xx, 19xx otherwise, according
3496 to X.509 recomendation.
3498 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3499 year = value.year % 100
3501 year=(2000 + year) if year < 50 else (1900 + year),
3505 minute=value.minute,
3506 second=value.second,
3510 return pp_console_row(next(self.pps()))
3512 def pps(self, decode_path=()):
3514 asn1_type_name=self.asn1_type_name,
3515 obj_name=self.__class__.__name__,
3516 decode_path=decode_path,
3517 value=self.todatetime().isoformat() if self.ready else None,
3518 optional=self.optional,
3519 default=self == self.default,
3520 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3521 expl=None if self._expl is None else tag_decode(self._expl),
3526 expl_offset=self.expl_offset if self.expled else None,
3527 expl_tlen=self.expl_tlen if self.expled else None,
3528 expl_llen=self.expl_llen if self.expled else None,
3529 expl_vlen=self.expl_vlen if self.expled else None,
3530 expl_lenindef=self.expl_lenindef,
3534 class GeneralizedTime(UTCTime):
3535 """``GeneralizedTime`` datetime type
3537 This type is similar to :py:class:`pyderasn.UTCTime`.
3539 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3540 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3542 '20170930220750.000123Z'
3543 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3544 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3547 tag_default = tag_encode(24)
3548 asn1_type_name = "GeneralizedTime"
3550 fmt = "%Y%m%d%H%M%SZ"
3551 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3553 def _value_sanitize(self, value):
3554 if isinstance(value, self.__class__):
3556 if isinstance(value, datetime):
3557 return value.strftime(
3558 self.fmt_ms if value.microsecond > 0 else self.fmt
3560 if isinstance(value, binary_type):
3561 value_decoded = value.decode("ascii")
3562 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3564 datetime.strptime(value_decoded, self.fmt)
3567 "invalid GeneralizedTime (without ms) format",
3570 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3572 datetime.strptime(value_decoded, self.fmt_ms)
3575 "invalid GeneralizedTime (with ms) format",
3580 "invalid GeneralizedTime length",
3581 klass=self.__class__,
3583 raise InvalidValueType((self.__class__, datetime))
3585 def todatetime(self):
3586 value = self._value.decode("ascii")
3587 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3588 return datetime.strptime(value, self.fmt)
3589 return datetime.strptime(value, self.fmt_ms)
3592 class GraphicString(CommonString):
3594 tag_default = tag_encode(25)
3595 encoding = "iso-8859-1"
3596 asn1_type_name = "GraphicString"
3599 class VisibleString(CommonString):
3601 tag_default = tag_encode(26)
3603 asn1_type_name = "VisibleString"
3606 class ISO646String(VisibleString):
3608 asn1_type_name = "ISO646String"
3611 class GeneralString(CommonString):
3613 tag_default = tag_encode(27)
3614 encoding = "iso-8859-1"
3615 asn1_type_name = "GeneralString"
3618 class UniversalString(CommonString):
3620 tag_default = tag_encode(28)
3621 encoding = "utf-32-be"
3622 asn1_type_name = "UniversalString"
3625 class BMPString(CommonString):
3627 tag_default = tag_encode(30)
3628 encoding = "utf-16-be"
3629 asn1_type_name = "BMPString"
3633 """``CHOICE`` special type
3637 class GeneralName(Choice):
3639 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3640 ("dNSName", IA5String(impl=tag_ctxp(2))),
3643 >>> gn = GeneralName()
3645 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3646 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3647 >>> gn["dNSName"] = IA5String("bar.baz")
3648 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3649 >>> gn["rfc822Name"]
3652 [2] IA5String IA5 bar.baz
3655 >>> gn.value == gn["dNSName"]
3658 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3660 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3661 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3663 __slots__ = ("specs",)
3665 asn1_type_name = "CHOICE"
3678 :param value: set the value. Either ``(choice, value)`` tuple, or
3679 :py:class:`pyderasn.Choice` object
3680 :param bytes impl: can not be set, do **not** use it
3681 :param bytes expl: override default tag with ``EXPLICIT`` one
3682 :param default: set default value. Type same as in ``value``
3683 :param bool optional: is object ``OPTIONAL`` in sequence
3685 if impl is not None:
3686 raise ValueError("no implicit tag allowed for CHOICE")
3687 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3689 schema = getattr(self, "schema", ())
3690 if len(schema) == 0:
3691 raise ValueError("schema must be specified")
3693 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3696 if value is not None:
3697 self._value = self._value_sanitize(value)
3698 if default is not None:
3699 default_value = self._value_sanitize(default)
3700 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3701 default_obj.specs = self.specs
3702 default_obj._value = default_value
3703 self.default = default_obj
3705 self._value = default_obj.copy()._value
3707 def _value_sanitize(self, value):
3708 if isinstance(value, self.__class__):
3710 if isinstance(value, tuple) and len(value) == 2:
3712 spec = self.specs.get(choice)
3714 raise ObjUnknown(choice)
3715 if not isinstance(obj, spec.__class__):
3716 raise InvalidValueType((spec,))
3717 return (choice, spec(obj))
3718 raise InvalidValueType((self.__class__, tuple))
3722 return self._value is not None and self._value[1].ready
3725 obj = self.__class__(schema=self.specs)
3726 obj._expl = self._expl
3727 obj.default = self.default
3728 obj.optional = self.optional
3729 obj.offset = self.offset
3730 obj.llen = self.llen
3731 obj.vlen = self.vlen
3733 if value is not None:
3734 obj._value = (value[0], value[1].copy())
3737 def __eq__(self, their):
3738 if isinstance(their, tuple) and len(their) == 2:
3739 return self._value == their
3740 if not isinstance(their, self.__class__):
3743 self.specs == their.specs and
3744 self._value == their._value
3754 return self.__class__(
3757 expl=self._expl if expl is None else expl,
3758 default=self.default if default is None else default,
3759 optional=self.optional if optional is None else optional,
3764 self._assert_ready()
3765 return self._value[0]
3769 self._assert_ready()
3770 return self._value[1]
3772 def __getitem__(self, key):
3773 if key not in self.specs:
3774 raise ObjUnknown(key)
3775 if self._value is None:
3777 choice, value = self._value
3782 def __setitem__(self, key, value):
3783 spec = self.specs.get(key)
3785 raise ObjUnknown(key)
3786 if not isinstance(value, spec.__class__):
3787 raise InvalidValueType((spec.__class__,))
3788 self._value = (key, spec(value))
3796 return self._value[1].decoded if self.ready else False
3799 self._assert_ready()
3800 return self._value[1].encode()
3802 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3803 for choice, spec in self.specs.items():
3804 sub_decode_path = decode_path + (choice,)
3810 decode_path=sub_decode_path,
3819 klass=self.__class__,
3820 decode_path=decode_path,
3825 value, tail = spec.decode(
3829 decode_path=sub_decode_path,
3832 obj = self.__class__(
3835 default=self.default,
3836 optional=self.optional,
3837 _decoded=(offset, 0, value.tlvlen),
3839 obj._value = (choice, value)
3843 value = pp_console_row(next(self.pps()))
3845 value = "%s[%r]" % (value, self.value)
3848 def pps(self, decode_path=()):
3850 asn1_type_name=self.asn1_type_name,
3851 obj_name=self.__class__.__name__,
3852 decode_path=decode_path,
3853 value=self.choice if self.ready else None,
3854 optional=self.optional,
3855 default=self == self.default,
3856 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3857 expl=None if self._expl is None else tag_decode(self._expl),
3862 expl_lenindef=self.expl_lenindef,
3865 yield self.value.pps(decode_path=decode_path + (self.choice,))
3868 class PrimitiveTypes(Choice):
3869 """Predefined ``CHOICE`` for all generic primitive types
3871 It could be useful for general decoding of some unspecified values:
3873 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3874 OCTET STRING 3 bytes 666f6f
3875 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3879 schema = tuple((klass.__name__, klass()) for klass in (
3904 """``ANY`` special type
3906 >>> Any(Integer(-123))
3908 >>> a = Any(OctetString(b"hello world").encode())
3909 ANY 040b68656c6c6f20776f726c64
3910 >>> hexenc(bytes(a))
3911 b'0x040x0bhello world'
3913 __slots__ = ("defined",)
3914 tag_default = tag_encode(0)
3915 asn1_type_name = "ANY"
3925 :param value: set the value. Either any kind of pyderasn's
3926 **ready** object, or bytes. Pay attention that
3927 **no** validation is performed is raw binary value
3929 :param bytes expl: override default tag with ``EXPLICIT`` one
3930 :param bool optional: is object ``OPTIONAL`` in sequence
3932 super(Any, self).__init__(None, expl, None, optional, _decoded)
3933 self._value = None if value is None else self._value_sanitize(value)
3936 def _value_sanitize(self, value):
3937 if isinstance(value, self.__class__):
3939 if isinstance(value, Obj):
3940 return value.encode()
3941 if isinstance(value, binary_type):
3943 raise InvalidValueType((self.__class__, Obj, binary_type))
3947 return self._value is not None
3950 obj = self.__class__()
3951 obj._value = self._value
3953 obj._expl = self._expl
3954 obj.optional = self.optional
3955 obj.offset = self.offset
3956 obj.llen = self.llen
3957 obj.vlen = self.vlen
3960 def __eq__(self, their):
3961 if isinstance(their, binary_type):
3962 return self._value == their
3963 if issubclass(their.__class__, Any):
3964 return self._value == their._value
3973 return self.__class__(
3975 expl=self._expl if expl is None else expl,
3976 optional=self.optional if optional is None else optional,
3979 def __bytes__(self):
3980 self._assert_ready()
3988 self._assert_ready()
3991 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3993 t, tlen, lv = tag_strip(tlv)
3994 except DecodeError as err:
3995 raise err.__class__(
3997 klass=self.__class__,
3998 decode_path=decode_path,
4002 l, llen, v = len_decode(lv)
4003 except LenIndefForm as err:
4004 if not ctx.get("bered", False):
4005 raise err.__class__(
4007 klass=self.__class__,
4008 decode_path=decode_path,
4011 llen, vlen, v = 1, 0, lv[1:]
4012 sub_offset = offset + tlen + llen
4015 if v[:EOC_LEN].tobytes() == EOC:
4016 tlvlen = tlen + llen + vlen + EOC_LEN
4017 obj = self.__class__(
4018 value=tlv[:tlvlen].tobytes(),
4020 optional=self.optional,
4021 _decoded=(offset, 0, tlvlen),
4025 return obj, v[EOC_LEN:]
4027 chunk, v = Any().decode(
4030 decode_path=decode_path + (str(chunk_i),),
4034 vlen += chunk.tlvlen
4035 sub_offset += chunk.tlvlen
4037 except DecodeError as err:
4038 raise err.__class__(
4040 klass=self.__class__,
4041 decode_path=decode_path,
4045 raise NotEnoughData(
4046 "encoded length is longer than data",
4047 klass=self.__class__,
4048 decode_path=decode_path,
4051 tlvlen = tlen + llen + l
4052 v, tail = tlv[:tlvlen], v[l:]
4053 obj = self.__class__(
4056 optional=self.optional,
4057 _decoded=(offset, 0, tlvlen),
4063 return pp_console_row(next(self.pps()))
4065 def pps(self, decode_path=()):
4067 asn1_type_name=self.asn1_type_name,
4068 obj_name=self.__class__.__name__,
4069 decode_path=decode_path,
4070 blob=self._value if self.ready else None,
4071 optional=self.optional,
4072 default=self == self.default,
4073 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4074 expl=None if self._expl is None else tag_decode(self._expl),
4079 expl_offset=self.expl_offset if self.expled else None,
4080 expl_tlen=self.expl_tlen if self.expled else None,
4081 expl_llen=self.expl_llen if self.expled else None,
4082 expl_vlen=self.expl_vlen if self.expled else None,
4083 expl_lenindef=self.expl_lenindef,
4084 lenindef=self.lenindef,
4086 defined_by, defined = self.defined or (None, None)
4087 if defined_by is not None:
4089 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4093 ########################################################################
4094 # ASN.1 constructed types
4095 ########################################################################
4097 def get_def_by_path(defines_by_path, sub_decode_path):
4098 """Get define by decode path
4100 for path, define in defines_by_path:
4101 if len(path) != len(sub_decode_path):
4103 for p1, p2 in zip(path, sub_decode_path):
4104 if (p1 != any) and (p1 != p2):
4110 def abs_decode_path(decode_path, rel_path):
4111 """Create an absolute decode path from current and relative ones
4113 :param decode_path: current decode path, starting point.
4115 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4116 If first tuple's element is "/", then treat it as
4117 an absolute path, ignoring ``decode_path`` as
4118 starting point. Also this tuple can contain ".."
4119 elements, stripping the leading element from
4122 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4123 ("foo", "bar", "baz", "whatever")
4124 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4126 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4129 if rel_path[0] == "/":
4131 if rel_path[0] == "..":
4132 return abs_decode_path(decode_path[:-1], rel_path[1:])
4133 return decode_path + rel_path
4136 class Sequence(Obj):
4137 """``SEQUENCE`` structure type
4139 You have to make specification of sequence::
4141 class Extension(Sequence):
4143 ("extnID", ObjectIdentifier()),
4144 ("critical", Boolean(default=False)),
4145 ("extnValue", OctetString()),
4148 Then, you can work with it as with dictionary.
4150 >>> ext = Extension()
4151 >>> Extension().specs
4153 ('extnID', OBJECT IDENTIFIER),
4154 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4155 ('extnValue', OCTET STRING),
4157 >>> ext["extnID"] = "1.2.3"
4158 Traceback (most recent call last):
4159 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4160 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4162 You can determine if sequence is ready to be encoded:
4167 Traceback (most recent call last):
4168 pyderasn.ObjNotReady: object is not ready: extnValue
4169 >>> ext["extnValue"] = OctetString(b"foobar")
4173 Value you want to assign, must have the same **type** as in
4174 corresponding specification, but it can have different tags,
4175 optional/default attributes -- they will be taken from specification
4178 class TBSCertificate(Sequence):
4180 ("version", Version(expl=tag_ctxc(0), default="v1")),
4183 >>> tbs = TBSCertificate()
4184 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4186 Assign ``None`` to remove value from sequence.
4188 You can set values in Sequence during its initialization:
4190 >>> AlgorithmIdentifier((
4191 ("algorithm", ObjectIdentifier("1.2.3")),
4192 ("parameters", Any(Null()))
4194 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4196 You can determine if value exists/set in the sequence and take its value:
4198 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4201 OBJECT IDENTIFIER 1.2.3
4203 But pay attention that if value has default, then it won't be (not
4204 in) in the sequence (because ``DEFAULT`` must not be encoded in
4205 DER), but you can read its value:
4207 >>> "critical" in ext, ext["critical"]
4208 (False, BOOLEAN False)
4209 >>> ext["critical"] = Boolean(True)
4210 >>> "critical" in ext, ext["critical"]
4211 (True, BOOLEAN True)
4213 All defaulted values are always optional.
4215 .. _strict_default_existence_ctx:
4219 When decoded DER contains defaulted value inside, then
4220 technically this is not valid DER encoding. But we allow and pass
4221 it **by default**. Of course reencoding of that kind of DER will
4222 result in different binary representation (validly without
4223 defaulted value inside). You can enable strict defaulted values
4224 existence validation by setting ``"strict_default_existence":
4225 True`` :ref:`context <ctx>` option -- decoding process will raise
4226 an exception if defaulted value is met.
4228 Two sequences are equal if they have equal specification (schema),
4229 implicit/explicit tagging and the same values.
4231 __slots__ = ("specs",)
4232 tag_default = tag_encode(form=TagFormConstructed, num=16)
4233 asn1_type_name = "SEQUENCE"
4245 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4247 schema = getattr(self, "schema", ())
4249 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4252 if value is not None:
4253 if issubclass(value.__class__, Sequence):
4254 self._value = value._value
4255 elif hasattr(value, "__iter__"):
4256 for seq_key, seq_value in value:
4257 self[seq_key] = seq_value
4259 raise InvalidValueType((Sequence,))
4260 if default is not None:
4261 if not issubclass(default.__class__, Sequence):
4262 raise InvalidValueType((Sequence,))
4263 default_value = default._value
4264 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4265 default_obj.specs = self.specs
4266 default_obj._value = default_value
4267 self.default = default_obj
4269 self._value = default_obj.copy()._value
4273 for name, spec in self.specs.items():
4274 value = self._value.get(name)
4285 obj = self.__class__(schema=self.specs)
4287 obj._expl = self._expl
4288 obj.default = self.default
4289 obj.optional = self.optional
4290 obj.offset = self.offset
4291 obj.llen = self.llen
4292 obj.vlen = self.vlen
4293 obj._value = {k: v.copy() for k, v in self._value.items()}
4296 def __eq__(self, their):
4297 if not isinstance(their, self.__class__):
4300 self.specs == their.specs and
4301 self.tag == their.tag and
4302 self._expl == their._expl and
4303 self._value == their._value
4314 return self.__class__(
4317 impl=self.tag if impl is None else impl,
4318 expl=self._expl if expl is None else expl,
4319 default=self.default if default is None else default,
4320 optional=self.optional if optional is None else optional,
4323 def __contains__(self, key):
4324 return key in self._value
4326 def __setitem__(self, key, value):
4327 spec = self.specs.get(key)
4329 raise ObjUnknown(key)
4331 self._value.pop(key, None)
4333 if not isinstance(value, spec.__class__):
4334 raise InvalidValueType((spec.__class__,))
4335 value = spec(value=value)
4336 if spec.default is not None and value == spec.default:
4337 self._value.pop(key, None)
4339 self._value[key] = value
4341 def __getitem__(self, key):
4342 value = self._value.get(key)
4343 if value is not None:
4345 spec = self.specs.get(key)
4347 raise ObjUnknown(key)
4348 if spec.default is not None:
4352 def _encoded_values(self):
4354 for name, spec in self.specs.items():
4355 value = self._value.get(name)
4359 raise ObjNotReady(name)
4360 raws.append(value.encode())
4364 v = b"".join(self._encoded_values())
4365 return b"".join((self.tag, len_encode(len(v)), v))
4367 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4369 t, tlen, lv = tag_strip(tlv)
4370 except DecodeError as err:
4371 raise err.__class__(
4373 klass=self.__class__,
4374 decode_path=decode_path,
4379 klass=self.__class__,
4380 decode_path=decode_path,
4387 l, llen, v = len_decode(lv)
4388 except LenIndefForm as err:
4389 if not ctx.get("bered", False):
4390 raise err.__class__(
4392 klass=self.__class__,
4393 decode_path=decode_path,
4396 l, llen, v = 0, 1, lv[1:]
4398 except DecodeError as err:
4399 raise err.__class__(
4401 klass=self.__class__,
4402 decode_path=decode_path,
4406 raise NotEnoughData(
4407 "encoded length is longer than data",
4408 klass=self.__class__,
4409 decode_path=decode_path,
4413 v, tail = v[:l], v[l:]
4415 sub_offset = offset + tlen + llen
4417 for name, spec in self.specs.items():
4418 if spec.optional and (
4419 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4423 sub_decode_path = decode_path + (name,)
4425 value, v_tail = spec.decode(
4429 decode_path=sub_decode_path,
4437 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4438 if defined is not None:
4439 defined_by, defined_spec = defined
4440 if issubclass(value.__class__, SequenceOf):
4441 for i, _value in enumerate(value):
4442 sub_sub_decode_path = sub_decode_path + (
4444 DecodePathDefBy(defined_by),
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_sub_decode_path,
4456 if len(defined_tail) > 0:
4459 klass=self.__class__,
4460 decode_path=sub_sub_decode_path,
4463 _value.defined = (defined_by, defined_value)
4465 defined_value, defined_tail = defined_spec.decode(
4466 memoryview(bytes(value)),
4468 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4469 if value.expled else (value.tlen + value.llen)
4472 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4475 if len(defined_tail) > 0:
4478 klass=self.__class__,
4479 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4482 value.defined = (defined_by, defined_value)
4484 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4486 sub_offset += value_len
4488 if spec.default is not None and value == spec.default:
4489 if ctx.get("strict_default_existence", False):
4491 "DEFAULT value met",
4492 klass=self.__class__,
4493 decode_path=sub_decode_path,
4498 values[name] = value
4500 spec_defines = getattr(spec, "defines", ())
4501 if len(spec_defines) == 0:
4502 defines_by_path = ctx.get("defines_by_path", ())
4503 if len(defines_by_path) > 0:
4504 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4505 if spec_defines is not None and len(spec_defines) > 0:
4506 for rel_path, schema in spec_defines:
4507 defined = schema.get(value, None)
4508 if defined is not None:
4509 ctx.setdefault("defines", []).append((
4510 abs_decode_path(sub_decode_path[:-1], rel_path),
4514 if v[:EOC_LEN].tobytes() != EOC:
4517 klass=self.__class__,
4518 decode_path=decode_path,
4526 klass=self.__class__,
4527 decode_path=decode_path,
4530 obj = self.__class__(
4534 default=self.default,
4535 optional=self.optional,
4536 _decoded=(offset, llen, vlen),
4539 obj.lenindef = lenindef
4543 value = pp_console_row(next(self.pps()))
4545 for name in self.specs:
4546 _value = self._value.get(name)
4549 cols.append(repr(_value))
4550 return "%s[%s]" % (value, ", ".join(cols))
4552 def pps(self, decode_path=()):
4554 asn1_type_name=self.asn1_type_name,
4555 obj_name=self.__class__.__name__,
4556 decode_path=decode_path,
4557 optional=self.optional,
4558 default=self == self.default,
4559 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4560 expl=None if self._expl is None else tag_decode(self._expl),
4565 expl_offset=self.expl_offset if self.expled else None,
4566 expl_tlen=self.expl_tlen if self.expled else None,
4567 expl_llen=self.expl_llen if self.expled else None,
4568 expl_vlen=self.expl_vlen if self.expled else None,
4569 expl_lenindef=self.expl_lenindef,
4570 lenindef=self.lenindef,
4572 for name in self.specs:
4573 value = self._value.get(name)
4576 yield value.pps(decode_path=decode_path + (name,))
4579 class Set(Sequence):
4580 """``SET`` structure type
4582 Its usage is identical to :py:class:`pyderasn.Sequence`.
4585 tag_default = tag_encode(form=TagFormConstructed, num=17)
4586 asn1_type_name = "SET"
4589 raws = self._encoded_values()
4592 return b"".join((self.tag, len_encode(len(v)), v))
4594 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4596 t, tlen, lv = tag_strip(tlv)
4597 except DecodeError as err:
4598 raise err.__class__(
4600 klass=self.__class__,
4601 decode_path=decode_path,
4606 klass=self.__class__,
4607 decode_path=decode_path,
4614 l, llen, v = len_decode(lv)
4615 except LenIndefForm as err:
4616 if not ctx.get("bered", False):
4617 raise err.__class__(
4619 klass=self.__class__,
4620 decode_path=decode_path,
4623 l, llen, v = 0, 1, lv[1:]
4625 except DecodeError as err:
4626 raise err.__class__(
4628 klass=self.__class__,
4629 decode_path=decode_path,
4633 raise NotEnoughData(
4634 "encoded length is longer than data",
4635 klass=self.__class__,
4639 v, tail = v[:l], v[l:]
4641 sub_offset = offset + tlen + llen
4643 specs_items = self.specs.items
4645 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4647 for name, spec in specs_items():
4648 sub_decode_path = decode_path + (name,)
4654 decode_path=sub_decode_path,
4663 klass=self.__class__,
4664 decode_path=decode_path,
4667 value, v_tail = spec.decode(
4671 decode_path=sub_decode_path,
4674 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4675 sub_offset += value_len
4678 if spec.default is None or value != spec.default: # pragma: no cover
4679 # SeqMixing.test_encoded_default_accepted covers that place
4680 values[name] = value
4681 obj = self.__class__(
4685 default=self.default,
4686 optional=self.optional,
4687 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4692 msg="not all values are ready",
4693 klass=self.__class__,
4694 decode_path=decode_path,
4697 obj.lenindef = lenindef
4698 return obj, (v[EOC_LEN:] if lenindef else tail)
4701 class SequenceOf(Obj):
4702 """``SEQUENCE OF`` sequence type
4704 For that kind of type you must specify the object it will carry on
4705 (bounds are for example here, not required)::
4707 class Ints(SequenceOf):
4712 >>> ints.append(Integer(123))
4713 >>> ints.append(Integer(234))
4715 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4716 >>> [int(i) for i in ints]
4718 >>> ints.append(Integer(345))
4719 Traceback (most recent call last):
4720 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4723 >>> ints[1] = Integer(345)
4725 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4727 Also you can initialize sequence with preinitialized values:
4729 >>> ints = Ints([Integer(123), Integer(234)])
4731 __slots__ = ("spec", "_bound_min", "_bound_max")
4732 tag_default = tag_encode(form=TagFormConstructed, num=16)
4733 asn1_type_name = "SEQUENCE OF"
4746 super(SequenceOf, self).__init__(
4754 schema = getattr(self, "schema", None)
4756 raise ValueError("schema must be specified")
4758 self._bound_min, self._bound_max = getattr(
4762 ) if bounds is None else bounds
4764 if value is not None:
4765 self._value = self._value_sanitize(value)
4766 if default is not None:
4767 default_value = self._value_sanitize(default)
4768 default_obj = self.__class__(
4773 default_obj._value = default_value
4774 self.default = default_obj
4776 self._value = default_obj.copy()._value
4778 def _value_sanitize(self, value):
4779 if issubclass(value.__class__, SequenceOf):
4780 value = value._value
4781 elif hasattr(value, "__iter__"):
4784 raise InvalidValueType((self.__class__, iter))
4785 if not self._bound_min <= len(value) <= self._bound_max:
4786 raise BoundsError(self._bound_min, len(value), self._bound_max)
4788 if not isinstance(v, self.spec.__class__):
4789 raise InvalidValueType((self.spec.__class__,))
4794 return all(v.ready for v in self._value)
4797 obj = self.__class__(schema=self.spec)
4798 obj._bound_min = self._bound_min
4799 obj._bound_max = self._bound_max
4801 obj._expl = self._expl
4802 obj.default = self.default
4803 obj.optional = self.optional
4804 obj.offset = self.offset
4805 obj.llen = self.llen
4806 obj.vlen = self.vlen
4807 obj._value = [v.copy() for v in self._value]
4810 def __eq__(self, their):
4811 if isinstance(their, self.__class__):
4813 self.spec == their.spec and
4814 self.tag == their.tag and
4815 self._expl == their._expl and
4816 self._value == their._value
4818 if hasattr(their, "__iter__"):
4819 return self._value == list(their)
4831 return self.__class__(
4835 (self._bound_min, self._bound_max)
4836 if bounds is None else bounds
4838 impl=self.tag if impl is None else impl,
4839 expl=self._expl if expl is None else expl,
4840 default=self.default if default is None else default,
4841 optional=self.optional if optional is None else optional,
4844 def __contains__(self, key):
4845 return key in self._value
4847 def append(self, value):
4848 if not isinstance(value, self.spec.__class__):
4849 raise InvalidValueType((self.spec.__class__,))
4850 if len(self._value) + 1 > self._bound_max:
4853 len(self._value) + 1,
4856 self._value.append(value)
4859 self._assert_ready()
4860 return iter(self._value)
4863 self._assert_ready()
4864 return len(self._value)
4866 def __setitem__(self, key, value):
4867 if not isinstance(value, self.spec.__class__):
4868 raise InvalidValueType((self.spec.__class__,))
4869 self._value[key] = self.spec(value=value)
4871 def __getitem__(self, key):
4872 return self._value[key]
4874 def _encoded_values(self):
4875 return [v.encode() for v in self._value]
4878 v = b"".join(self._encoded_values())
4879 return b"".join((self.tag, len_encode(len(v)), v))
4881 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4883 t, tlen, lv = tag_strip(tlv)
4884 except DecodeError as err:
4885 raise err.__class__(
4887 klass=self.__class__,
4888 decode_path=decode_path,
4893 klass=self.__class__,
4894 decode_path=decode_path,
4901 l, llen, v = len_decode(lv)
4902 except LenIndefForm as err:
4903 if not ctx.get("bered", False):
4904 raise err.__class__(
4906 klass=self.__class__,
4907 decode_path=decode_path,
4910 l, llen, v = 0, 1, lv[1:]
4912 except DecodeError as err:
4913 raise err.__class__(
4915 klass=self.__class__,
4916 decode_path=decode_path,
4920 raise NotEnoughData(
4921 "encoded length is longer than data",
4922 klass=self.__class__,
4923 decode_path=decode_path,
4927 v, tail = v[:l], v[l:]
4929 sub_offset = offset + tlen + llen
4933 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4935 value, v_tail = spec.decode(
4939 decode_path=decode_path + (str(len(_value)),),
4942 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4943 sub_offset += value_len
4946 _value.append(value)
4947 obj = self.__class__(
4950 bounds=(self._bound_min, self._bound_max),
4953 default=self.default,
4954 optional=self.optional,
4955 _decoded=(offset, llen, vlen),
4957 obj.lenindef = lenindef
4958 return obj, (v[EOC_LEN:] if lenindef else tail)
4962 pp_console_row(next(self.pps())),
4963 ", ".join(repr(v) for v in self._value),
4966 def pps(self, decode_path=()):
4968 asn1_type_name=self.asn1_type_name,
4969 obj_name=self.__class__.__name__,
4970 decode_path=decode_path,
4971 optional=self.optional,
4972 default=self == self.default,
4973 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4974 expl=None if self._expl is None else tag_decode(self._expl),
4979 expl_offset=self.expl_offset if self.expled else None,
4980 expl_tlen=self.expl_tlen if self.expled else None,
4981 expl_llen=self.expl_llen if self.expled else None,
4982 expl_vlen=self.expl_vlen if self.expled else None,
4983 expl_lenindef=self.expl_lenindef,
4984 lenindef=self.lenindef,
4986 for i, value in enumerate(self._value):
4987 yield value.pps(decode_path=decode_path + (str(i),))
4990 class SetOf(SequenceOf):
4991 """``SET OF`` sequence type
4993 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4996 tag_default = tag_encode(form=TagFormConstructed, num=17)
4997 asn1_type_name = "SET OF"
5000 raws = self._encoded_values()
5003 return b"".join((self.tag, len_encode(len(v)), v))
5006 def obj_by_path(pypath): # pragma: no cover
5007 """Import object specified as string Python path
5009 Modules must be separated from classes/functions with ``:``.
5011 >>> obj_by_path("foo.bar:Baz")
5012 <class 'foo.bar.Baz'>
5013 >>> obj_by_path("foo.bar:Baz.boo")
5014 <classmethod 'foo.bar.Baz.boo'>
5016 mod, objs = pypath.rsplit(":", 1)
5017 from importlib import import_module
5018 obj = import_module(mod)
5019 for obj_name in objs.split("."):
5020 obj = getattr(obj, obj_name)
5024 def generic_decoder(): # pragma: no cover
5025 # All of this below is a big hack with self references
5026 choice = PrimitiveTypes()
5027 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5028 choice.specs["SetOf"] = SetOf(schema=choice)
5030 choice.specs["SequenceOf%d" % i] = SequenceOf(
5034 choice.specs["Any"] = Any()
5036 # Class name equals to type name, to omit it from output
5037 class SEQUENCEOF(SequenceOf):
5041 def pprint_any(obj, oids=None, with_colours=False):
5042 def _pprint_pps(pps):
5044 if hasattr(pp, "_fields"):
5045 if pp.asn1_type_name == Choice.asn1_type_name:
5047 pp_kwargs = pp._asdict()
5048 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5049 pp = _pp(**pp_kwargs)
5050 yield pp_console_row(
5055 with_colours=with_colours,
5057 for row in pp_console_blob(pp):
5060 for row in _pprint_pps(pp):
5062 return "\n".join(_pprint_pps(obj.pps()))
5063 return SEQUENCEOF(), pprint_any
5066 def main(): # pragma: no cover
5068 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5069 parser.add_argument(
5073 help="Skip that number of bytes from the beginning",
5075 parser.add_argument(
5077 help="Python path to dictionary with OIDs",
5079 parser.add_argument(
5081 help="Python path to schema definition to use",
5083 parser.add_argument(
5084 "--defines-by-path",
5085 help="Python path to decoder's defines_by_path",
5087 parser.add_argument(
5089 action='store_true',
5090 help="Disallow BER encoding",
5092 parser.add_argument(
5094 type=argparse.FileType("rb"),
5095 help="Path to DER file you want to decode",
5097 args = parser.parse_args()
5098 args.DERFile.seek(args.skip)
5099 der = memoryview(args.DERFile.read())
5100 args.DERFile.close()
5101 oids = obj_by_path(args.oids) if args.oids else {}
5103 schema = obj_by_path(args.schema)
5104 from functools import partial
5105 pprinter = partial(pprint, big_blobs=True)
5107 schema, pprinter = generic_decoder()
5108 ctx = {"bered": not args.nobered}
5109 if args.defines_by_path is not None:
5110 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5111 obj, tail = schema().decode(der, ctx=ctx)
5115 with_colours=True if environ.get("NO_COLOR") is None else False,
5118 print("\nTrailing data: %s" % hexenc(tail))
5121 if __name__ == "__main__":