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):
581 "TagClassApplication",
585 "TagFormConstructed",
596 TagClassUniversal = 0
597 TagClassApplication = 1 << 6
598 TagClassContext = 1 << 7
599 TagClassPrivate = 1 << 6 | 1 << 7
601 TagFormConstructed = 1 << 5
604 TagClassApplication: "APPLICATION ",
605 TagClassPrivate: "PRIVATE ",
606 TagClassUniversal: "UNIV ",
612 ########################################################################
614 ########################################################################
616 class DecodeError(Exception):
617 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
619 :param str msg: reason of decode failing
620 :param klass: optional exact DecodeError inherited class (like
621 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
622 :py:exc:`InvalidLength`)
623 :param decode_path: tuple of strings. It contains human
624 readable names of the fields through which
625 decoding process has passed
626 :param int offset: binary offset where failure happened
628 super(DecodeError, self).__init__()
631 self.decode_path = decode_path
637 "" if self.klass is None else self.klass.__name__,
639 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
640 if len(self.decode_path) > 0 else ""
642 ("(at %d)" % self.offset) if self.offset > 0 else "",
648 return "%s(%s)" % (self.__class__.__name__, self)
651 class NotEnoughData(DecodeError):
655 class LenIndefForm(DecodeError):
659 class TagMismatch(DecodeError):
663 class InvalidLength(DecodeError):
667 class InvalidOID(DecodeError):
671 class ObjUnknown(ValueError):
672 def __init__(self, name):
673 super(ObjUnknown, self).__init__()
677 return "object is unknown: %s" % self.name
680 return "%s(%s)" % (self.__class__.__name__, self)
683 class ObjNotReady(ValueError):
684 def __init__(self, name):
685 super(ObjNotReady, self).__init__()
689 return "object is not ready: %s" % self.name
692 return "%s(%s)" % (self.__class__.__name__, self)
695 class InvalidValueType(ValueError):
696 def __init__(self, expected_types):
697 super(InvalidValueType, self).__init__()
698 self.expected_types = expected_types
701 return "invalid value type, expected: %s" % ", ".join(
702 [repr(t) for t in self.expected_types]
706 return "%s(%s)" % (self.__class__.__name__, self)
709 class BoundsError(ValueError):
710 def __init__(self, bound_min, value, bound_max):
711 super(BoundsError, self).__init__()
712 self.bound_min = bound_min
714 self.bound_max = bound_max
717 return "unsatisfied bounds: %s <= %s <= %s" % (
724 return "%s(%s)" % (self.__class__.__name__, self)
727 ########################################################################
729 ########################################################################
731 _hexdecoder = getdecoder("hex")
732 _hexencoder = getencoder("hex")
736 """Binary data to hexadecimal string convert
738 return _hexdecoder(data)[0]
742 """Hexadecimal string to binary data convert
744 return _hexencoder(data)[0].decode("ascii")
747 def int_bytes_len(num, byte_len=8):
750 return int(ceil(float(num.bit_length()) / byte_len))
753 def zero_ended_encode(num):
754 octets = bytearray(int_bytes_len(num, 7))
756 octets[i] = num & 0x7F
760 octets[i] = 0x80 | (num & 0x7F)
766 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
767 """Encode tag to binary form
769 :param int num: tag's number
770 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
771 :py:data:`pyderasn.TagClassContext`,
772 :py:data:`pyderasn.TagClassApplication`,
773 :py:data:`pyderasn.TagClassPrivate`)
774 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
775 :py:data:`pyderasn.TagFormConstructed`)
779 return int2byte(klass | form | num)
780 # [XX|X|11111][1.......][1.......] ... [0.......]
781 return int2byte(klass | form | 31) + zero_ended_encode(num)
785 """Decode tag from binary form
789 No validation is performed, assuming that it has already passed.
791 It returns tuple with three integers, as
792 :py:func:`pyderasn.tag_encode` accepts.
794 first_octet = byte2int(tag)
795 klass = first_octet & 0xC0
796 form = first_octet & 0x20
797 if first_octet & 0x1F < 0x1F:
798 return (klass, form, first_octet & 0x1F)
800 for octet in iterbytes(tag[1:]):
803 return (klass, form, num)
807 """Create CONTEXT PRIMITIVE tag
809 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
813 """Create CONTEXT CONSTRUCTED tag
815 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
819 """Take off tag from the data
821 :returns: (encoded tag, tag length, remaining data)
824 raise NotEnoughData("no data at all")
825 if byte2int(data) & 0x1F < 31:
826 return data[:1], 1, data[1:]
831 raise DecodeError("unfinished tag")
832 if indexbytes(data, i) & 0x80 == 0:
835 return data[:i], i, data[i:]
841 octets = bytearray(int_bytes_len(l) + 1)
842 octets[0] = 0x80 | (len(octets) - 1)
843 for i in six_xrange(len(octets) - 1, 0, -1):
849 def len_decode(data):
852 :returns: (decoded length, length's length, remaining data)
853 :raises LenIndefForm: if indefinite form encoding is met
856 raise NotEnoughData("no data at all")
857 first_octet = byte2int(data)
858 if first_octet & 0x80 == 0:
859 return first_octet, 1, data[1:]
860 octets_num = first_octet & 0x7F
861 if octets_num + 1 > len(data):
862 raise NotEnoughData("encoded length is longer than data")
865 if byte2int(data[1:]) == 0:
866 raise DecodeError("leading zeros")
868 for v in iterbytes(data[1:1 + octets_num]):
871 raise DecodeError("long form instead of short one")
872 return l, 1 + octets_num, data[1 + octets_num:]
875 ########################################################################
877 ########################################################################
879 class AutoAddSlots(type):
880 def __new__(mcs, name, bases, _dict):
881 _dict["__slots__"] = _dict.get("__slots__", ())
882 return type.__new__(mcs, name, bases, _dict)
885 @add_metaclass(AutoAddSlots)
887 """Common ASN.1 object class
889 All ASN.1 types are inherited from it. It has metaclass that
890 automatically adds ``__slots__`` to all inherited classes.
914 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
915 self._expl = getattr(self, "expl", None) if expl is None else expl
916 if self.tag != self.tag_default and self._expl is not None:
918 "implicit and explicit tags can not be set simultaneously"
920 if default is not None:
922 self.optional = optional
923 self.offset, self.llen, self.vlen = _decoded
925 self.expl_lenindef = False
926 self.lenindef = False
930 def ready(self): # pragma: no cover
931 """Is object ready to be encoded?
933 raise NotImplementedError()
935 def _assert_ready(self):
937 raise ObjNotReady(self.__class__.__name__)
941 """Is object decoded?
943 return (self.llen + self.vlen) > 0
945 def copy(self): # pragma: no cover
946 """Make a copy of object, safe to be mutated
948 raise NotImplementedError()
956 return self.tlen + self.llen + self.vlen
958 def __str__(self): # pragma: no cover
959 return self.__bytes__() if PY2 else self.__unicode__()
961 def __ne__(self, their):
962 return not(self == their)
964 def __gt__(self, their): # pragma: no cover
965 return not(self < their)
967 def __le__(self, their): # pragma: no cover
968 return (self == their) or (self < their)
970 def __ge__(self, their): # pragma: no cover
971 return (self == their) or (self > their)
973 def _encode(self): # pragma: no cover
974 raise NotImplementedError()
976 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
977 raise NotImplementedError()
981 if self._expl is None:
983 return b"".join((self._expl, len_encode(len(raw)), raw))
996 :param data: either binary or memoryview
997 :param int offset: initial data's offset
998 :param bool leavemm: do we need to leave memoryview of remaining
999 data as is, or convert it to bytes otherwise
1000 :param ctx: optional :ref:`context <ctx>` governing decoding process.
1001 :param tag_only: decode only the tag, without length and contents
1002 (used only in Choice and Set structures, trying to
1003 determine if tag satisfies the scheme)
1004 :returns: (Obj, remaining data)
1008 tlv = memoryview(data)
1009 if self._expl is None:
1010 result = self._decode(
1013 decode_path=decode_path,
1022 t, tlen, lv = tag_strip(tlv)
1023 except DecodeError as err:
1024 raise err.__class__(
1026 klass=self.__class__,
1027 decode_path=decode_path,
1032 klass=self.__class__,
1033 decode_path=decode_path,
1037 l, llen, v = len_decode(lv)
1038 except LenIndefForm as err:
1039 if not ctx.get("bered", False):
1040 raise err.__class__(
1042 klass=self.__class__,
1043 decode_path=decode_path,
1047 offset += tlen + llen
1048 result = self._decode(
1051 decode_path=decode_path,
1058 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1059 if eoc_expected.tobytes() != EOC:
1062 decode_path=decode_path,
1066 obj.expl_lenindef = True
1067 except DecodeError as err:
1068 raise err.__class__(
1070 klass=self.__class__,
1071 decode_path=decode_path,
1076 raise NotEnoughData(
1077 "encoded length is longer than data",
1078 klass=self.__class__,
1079 decode_path=decode_path,
1082 result = self._decode(
1084 offset=offset + tlen + llen,
1085 decode_path=decode_path,
1092 return obj, (tail if leavemm else tail.tobytes())
1096 return self._expl is not None
1103 def expl_tlen(self):
1104 return len(self._expl)
1107 def expl_llen(self):
1108 if self.expl_lenindef:
1110 return len(len_encode(self.tlvlen))
1113 def expl_offset(self):
1114 return self.offset - self.expl_tlen - self.expl_llen
1117 def expl_vlen(self):
1121 def expl_tlvlen(self):
1122 return self.expl_tlen + self.expl_llen + self.expl_vlen
1125 class DecodePathDefBy(object):
1126 """DEFINED BY representation inside decode path
1128 __slots__ = ("defined_by",)
1130 def __init__(self, defined_by):
1131 self.defined_by = defined_by
1133 def __ne__(self, their):
1134 return not(self == their)
1136 def __eq__(self, their):
1137 if not isinstance(their, self.__class__):
1139 return self.defined_by == their.defined_by
1142 return "DEFINED BY " + str(self.defined_by)
1145 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1148 ########################################################################
1150 ########################################################################
1152 PP = namedtuple("PP", (
1177 asn1_type_name="unknown",
1194 expl_lenindef=False,
1222 def _colorize(what, colour, with_colours, attrs=("bold",)):
1223 return colored(what, colour, attrs=attrs) if with_colours else what
1238 " " if pp.expl_offset is None else
1239 ("-%d" % (pp.offset - pp.expl_offset))
1242 cols.append(_colorize(col, "red", with_colours, ()))
1243 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1244 col = _colorize(col, "green", with_colours, ())
1246 if pp.expl_lenindef:
1251 " " if ber_deoffset == 0 else
1252 _colorize(("-%d" % ber_deoffset), "red", with_colours)
1255 if len(pp.decode_path) > 0:
1256 cols.append(" ." * (len(pp.decode_path)))
1257 ent = pp.decode_path[-1]
1258 if isinstance(ent, DecodePathDefBy):
1259 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1260 value = str(ent.defined_by)
1262 oids is not None and
1263 ent.defined_by.asn1_type_name ==
1264 ObjectIdentifier.asn1_type_name and
1267 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1269 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1271 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1272 if pp.expl is not None:
1273 klass, _, num = pp.expl
1274 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1275 cols.append(_colorize(col, "blue", with_colours))
1276 if pp.impl is not None:
1277 klass, _, num = pp.impl
1278 col = "[%s%d]" % (TagClassReprs[klass], num)
1279 cols.append(_colorize(col, "blue", with_colours))
1280 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1281 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1283 cols.append(_colorize("BER", "red", with_colours))
1284 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1285 if pp.value is not None:
1287 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1289 oids is not None and
1290 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1293 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1295 if isinstance(pp.blob, binary_type):
1296 cols.append(hexenc(pp.blob))
1297 elif isinstance(pp.blob, tuple):
1298 cols.append(", ".join(pp.blob))
1300 cols.append(_colorize("OPTIONAL", "red", with_colours))
1302 cols.append(_colorize("DEFAULT", "red", with_colours))
1303 return " ".join(cols)
1306 def pp_console_blob(pp):
1307 cols = [" " * len("XXXXXYY [X,X,XXXX]YY")]
1308 if len(pp.decode_path) > 0:
1309 cols.append(" ." * (len(pp.decode_path) + 1))
1310 if isinstance(pp.blob, binary_type):
1311 blob = hexenc(pp.blob).upper()
1312 for i in range(0, len(blob), 32):
1313 chunk = blob[i:i + 32]
1314 yield " ".join(cols + [":".join(
1315 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1317 elif isinstance(pp.blob, tuple):
1318 yield " ".join(cols + [", ".join(pp.blob)])
1321 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1322 """Pretty print object
1324 :param Obj obj: object you want to pretty print
1325 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1326 from it is met, then its humand readable form is printed
1327 :param big_blobs: if large binary objects are met (like OctetString
1328 values), do we need to print them too, on separate
1330 :param with_colours: colourize output, if ``termcolor`` library
1333 def _pprint_pps(pps):
1335 if hasattr(pp, "_fields"):
1337 yield pp_console_row(
1342 with_colours=with_colours,
1344 for row in pp_console_blob(pp):
1347 yield pp_console_row(
1352 with_colours=with_colours,
1355 for row in _pprint_pps(pp):
1357 return "\n".join(_pprint_pps(obj.pps()))
1360 ########################################################################
1361 # ASN.1 primitive types
1362 ########################################################################
1365 """``BOOLEAN`` boolean type
1367 >>> b = Boolean(True)
1369 >>> b == Boolean(True)
1375 tag_default = tag_encode(1)
1376 asn1_type_name = "BOOLEAN"
1388 :param value: set the value. Either boolean type, or
1389 :py:class:`pyderasn.Boolean` object
1390 :param bytes impl: override default tag with ``IMPLICIT`` one
1391 :param bytes expl: override default tag with ``EXPLICIT`` one
1392 :param default: set default value. Type same as in ``value``
1393 :param bool optional: is object ``OPTIONAL`` in sequence
1395 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1396 self._value = None if value is None else self._value_sanitize(value)
1397 if default is not None:
1398 default = self._value_sanitize(default)
1399 self.default = self.__class__(
1405 self._value = default
1407 def _value_sanitize(self, value):
1408 if issubclass(value.__class__, Boolean):
1410 if isinstance(value, bool):
1412 raise InvalidValueType((self.__class__, bool))
1416 return self._value is not None
1419 obj = self.__class__()
1420 obj._value = self._value
1422 obj._expl = self._expl
1423 obj.default = self.default
1424 obj.optional = self.optional
1425 obj.offset = self.offset
1426 obj.llen = self.llen
1427 obj.vlen = self.vlen
1430 def __nonzero__(self):
1431 self._assert_ready()
1435 self._assert_ready()
1438 def __eq__(self, their):
1439 if isinstance(their, bool):
1440 return self._value == their
1441 if not issubclass(their.__class__, Boolean):
1444 self._value == their._value and
1445 self.tag == their.tag and
1446 self._expl == their._expl
1457 return self.__class__(
1459 impl=self.tag if impl is None else impl,
1460 expl=self._expl if expl is None else expl,
1461 default=self.default if default is None else default,
1462 optional=self.optional if optional is None else optional,
1466 self._assert_ready()
1470 (b"\xFF" if self._value else b"\x00"),
1473 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1475 t, _, lv = tag_strip(tlv)
1476 except DecodeError as err:
1477 raise err.__class__(
1479 klass=self.__class__,
1480 decode_path=decode_path,
1485 klass=self.__class__,
1486 decode_path=decode_path,
1492 l, _, v = len_decode(lv)
1493 except DecodeError as err:
1494 raise err.__class__(
1496 klass=self.__class__,
1497 decode_path=decode_path,
1501 raise InvalidLength(
1502 "Boolean's length must be equal to 1",
1503 klass=self.__class__,
1504 decode_path=decode_path,
1508 raise NotEnoughData(
1509 "encoded length is longer than data",
1510 klass=self.__class__,
1511 decode_path=decode_path,
1514 first_octet = byte2int(v)
1516 if first_octet == 0:
1518 elif first_octet == 0xFF:
1520 elif ctx.get("bered", False):
1525 "unacceptable Boolean value",
1526 klass=self.__class__,
1527 decode_path=decode_path,
1530 obj = self.__class__(
1534 default=self.default,
1535 optional=self.optional,
1536 _decoded=(offset, 1, 1),
1542 return pp_console_row(next(self.pps()))
1544 def pps(self, decode_path=()):
1546 asn1_type_name=self.asn1_type_name,
1547 obj_name=self.__class__.__name__,
1548 decode_path=decode_path,
1549 value=str(self._value) if self.ready else None,
1550 optional=self.optional,
1551 default=self == self.default,
1552 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1553 expl=None if self._expl is None else tag_decode(self._expl),
1558 expl_offset=self.expl_offset if self.expled else None,
1559 expl_tlen=self.expl_tlen if self.expled else None,
1560 expl_llen=self.expl_llen if self.expled else None,
1561 expl_vlen=self.expl_vlen if self.expled else None,
1562 expl_lenindef=self.expl_lenindef,
1568 """``INTEGER`` integer type
1570 >>> b = Integer(-123)
1572 >>> b == Integer(-123)
1577 >>> Integer(2, bounds=(1, 3))
1579 >>> Integer(5, bounds=(1, 3))
1580 Traceback (most recent call last):
1581 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1585 class Version(Integer):
1592 >>> v = Version("v1")
1599 {'v3': 2, 'v1': 0, 'v2': 1}
1601 __slots__ = ("specs", "_bound_min", "_bound_max")
1602 tag_default = tag_encode(2)
1603 asn1_type_name = "INTEGER"
1617 :param value: set the value. Either integer type, named value
1618 (if ``schema`` is specified in the class), or
1619 :py:class:`pyderasn.Integer` object
1620 :param bounds: set ``(MIN, MAX)`` value constraint.
1621 (-inf, +inf) by default
1622 :param bytes impl: override default tag with ``IMPLICIT`` one
1623 :param bytes expl: override default tag with ``EXPLICIT`` one
1624 :param default: set default value. Type same as in ``value``
1625 :param bool optional: is object ``OPTIONAL`` in sequence
1627 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1629 specs = getattr(self, "schema", {}) if _specs is None else _specs
1630 self.specs = specs if isinstance(specs, dict) else dict(specs)
1631 self._bound_min, self._bound_max = getattr(
1634 (float("-inf"), float("+inf")),
1635 ) if bounds is None else bounds
1636 if value is not None:
1637 self._value = self._value_sanitize(value)
1638 if default is not None:
1639 default = self._value_sanitize(default)
1640 self.default = self.__class__(
1646 if self._value is None:
1647 self._value = default
1649 def _value_sanitize(self, value):
1650 if issubclass(value.__class__, Integer):
1651 value = value._value
1652 elif isinstance(value, integer_types):
1654 elif isinstance(value, str):
1655 value = self.specs.get(value)
1657 raise ObjUnknown("integer value: %s" % value)
1659 raise InvalidValueType((self.__class__, int, str))
1660 if not self._bound_min <= value <= self._bound_max:
1661 raise BoundsError(self._bound_min, value, self._bound_max)
1666 return self._value is not None
1669 obj = self.__class__(_specs=self.specs)
1670 obj._value = self._value
1671 obj._bound_min = self._bound_min
1672 obj._bound_max = self._bound_max
1674 obj._expl = self._expl
1675 obj.default = self.default
1676 obj.optional = self.optional
1677 obj.offset = self.offset
1678 obj.llen = self.llen
1679 obj.vlen = self.vlen
1683 self._assert_ready()
1684 return int(self._value)
1687 self._assert_ready()
1690 bytes(self._expl or b"") +
1691 str(self._value).encode("ascii"),
1694 def __eq__(self, their):
1695 if isinstance(their, integer_types):
1696 return self._value == their
1697 if not issubclass(their.__class__, Integer):
1700 self._value == their._value and
1701 self.tag == their.tag and
1702 self._expl == their._expl
1705 def __lt__(self, their):
1706 return self._value < their._value
1710 for name, value in self.specs.items():
1711 if value == self._value:
1723 return self.__class__(
1726 (self._bound_min, self._bound_max)
1727 if bounds is None else bounds
1729 impl=self.tag if impl is None else impl,
1730 expl=self._expl if expl is None else expl,
1731 default=self.default if default is None else default,
1732 optional=self.optional if optional is None else optional,
1737 self._assert_ready()
1741 octets = bytearray([0])
1745 octets = bytearray()
1747 octets.append((value & 0xFF) ^ 0xFF)
1749 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1752 octets = bytearray()
1754 octets.append(value & 0xFF)
1756 if octets[-1] & 0x80 > 0:
1759 octets = bytes(octets)
1761 bytes_len = ceil(value.bit_length() / 8) or 1
1764 octets = value.to_bytes(
1769 except OverflowError:
1773 return b"".join((self.tag, len_encode(len(octets)), octets))
1775 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1777 t, _, lv = tag_strip(tlv)
1778 except DecodeError as err:
1779 raise err.__class__(
1781 klass=self.__class__,
1782 decode_path=decode_path,
1787 klass=self.__class__,
1788 decode_path=decode_path,
1794 l, llen, v = len_decode(lv)
1795 except DecodeError as err:
1796 raise err.__class__(
1798 klass=self.__class__,
1799 decode_path=decode_path,
1803 raise NotEnoughData(
1804 "encoded length is longer than data",
1805 klass=self.__class__,
1806 decode_path=decode_path,
1810 raise NotEnoughData(
1812 klass=self.__class__,
1813 decode_path=decode_path,
1816 v, tail = v[:l], v[l:]
1817 first_octet = byte2int(v)
1819 second_octet = byte2int(v[1:])
1821 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1822 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1825 "non normalized integer",
1826 klass=self.__class__,
1827 decode_path=decode_path,
1832 if first_octet & 0x80 > 0:
1833 octets = bytearray()
1834 for octet in bytearray(v):
1835 octets.append(octet ^ 0xFF)
1836 for octet in octets:
1837 value = (value << 8) | octet
1841 for octet in bytearray(v):
1842 value = (value << 8) | octet
1844 value = int.from_bytes(v, byteorder="big", signed=True)
1846 obj = self.__class__(
1848 bounds=(self._bound_min, self._bound_max),
1851 default=self.default,
1852 optional=self.optional,
1854 _decoded=(offset, llen, l),
1856 except BoundsError as err:
1859 klass=self.__class__,
1860 decode_path=decode_path,
1866 return pp_console_row(next(self.pps()))
1868 def pps(self, decode_path=()):
1870 asn1_type_name=self.asn1_type_name,
1871 obj_name=self.__class__.__name__,
1872 decode_path=decode_path,
1873 value=(self.named or str(self._value)) if self.ready else None,
1874 optional=self.optional,
1875 default=self == self.default,
1876 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1877 expl=None if self._expl is None else tag_decode(self._expl),
1882 expl_offset=self.expl_offset if self.expled else None,
1883 expl_tlen=self.expl_tlen if self.expled else None,
1884 expl_llen=self.expl_llen if self.expled else None,
1885 expl_vlen=self.expl_vlen if self.expled else None,
1886 expl_lenindef=self.expl_lenindef,
1890 class BitString(Obj):
1891 """``BIT STRING`` bit string type
1893 >>> BitString(b"hello world")
1894 BIT STRING 88 bits 68656c6c6f20776f726c64
1897 >>> b == b"hello world"
1902 >>> BitString("'0A3B5F291CD'H")
1903 BIT STRING 44 bits 0a3b5f291cd0
1904 >>> b = BitString("'010110000000'B")
1905 BIT STRING 12 bits 5800
1908 >>> b[0], b[1], b[2], b[3]
1909 (False, True, False, True)
1913 [False, True, False, True, True, False, False, False, False, False, False, False]
1917 class KeyUsage(BitString):
1919 ("digitalSignature", 0),
1920 ("nonRepudiation", 1),
1921 ("keyEncipherment", 2),
1924 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1925 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1927 ['nonRepudiation', 'keyEncipherment']
1929 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1931 __slots__ = ("tag_constructed", "specs", "defined")
1932 tag_default = tag_encode(3)
1933 asn1_type_name = "BIT STRING"
1946 :param value: set the value. Either binary type, tuple of named
1947 values (if ``schema`` is specified in the class),
1948 string in ``'XXX...'B`` form, or
1949 :py:class:`pyderasn.BitString` object
1950 :param bytes impl: override default tag with ``IMPLICIT`` one
1951 :param bytes expl: override default tag with ``EXPLICIT`` one
1952 :param default: set default value. Type same as in ``value``
1953 :param bool optional: is object ``OPTIONAL`` in sequence
1955 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1956 specs = getattr(self, "schema", {}) if _specs is None else _specs
1957 self.specs = specs if isinstance(specs, dict) else dict(specs)
1958 self._value = None if value is None else self._value_sanitize(value)
1959 if default is not None:
1960 default = self._value_sanitize(default)
1961 self.default = self.__class__(
1967 self._value = default
1969 tag_klass, _, tag_num = tag_decode(self.tag)
1970 self.tag_constructed = tag_encode(
1972 form=TagFormConstructed,
1976 def _bits2octets(self, bits):
1977 if len(self.specs) > 0:
1978 bits = bits.rstrip("0")
1980 bits += "0" * ((8 - (bit_len % 8)) % 8)
1981 octets = bytearray(len(bits) // 8)
1982 for i in six_xrange(len(octets)):
1983 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1984 return bit_len, bytes(octets)
1986 def _value_sanitize(self, value):
1987 if issubclass(value.__class__, BitString):
1989 if isinstance(value, (string_types, binary_type)):
1991 isinstance(value, string_types) and
1992 value.startswith("'")
1994 if value.endswith("'B"):
1996 if not set(value) <= set(("0", "1")):
1997 raise ValueError("B's coding contains unacceptable chars")
1998 return self._bits2octets(value)
1999 elif value.endswith("'H"):
2003 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2006 raise InvalidValueType((self.__class__, string_types, binary_type))
2007 elif isinstance(value, binary_type):
2008 return (len(value) * 8, value)
2010 raise InvalidValueType((self.__class__, string_types, binary_type))
2011 if isinstance(value, tuple):
2014 isinstance(value[0], integer_types) and
2015 isinstance(value[1], binary_type)
2020 bit = self.specs.get(name)
2022 raise ObjUnknown("BitString value: %s" % name)
2025 return self._bits2octets("")
2027 return self._bits2octets("".join(
2028 ("1" if bit in bits else "0")
2029 for bit in six_xrange(max(bits) + 1)
2031 raise InvalidValueType((self.__class__, binary_type, string_types))
2035 return self._value is not None
2038 obj = self.__class__(_specs=self.specs)
2040 if value is not None:
2041 value = (value[0], value[1])
2044 obj._expl = self._expl
2045 obj.default = self.default
2046 obj.optional = self.optional
2047 obj.offset = self.offset
2048 obj.llen = self.llen
2049 obj.vlen = self.vlen
2053 self._assert_ready()
2054 for i in six_xrange(self._value[0]):
2059 self._assert_ready()
2060 return self._value[0]
2062 def __bytes__(self):
2063 self._assert_ready()
2064 return self._value[1]
2066 def __eq__(self, their):
2067 if isinstance(their, bytes):
2068 return self._value[1] == their
2069 if not issubclass(their.__class__, BitString):
2072 self._value == their._value and
2073 self.tag == their.tag and
2074 self._expl == their._expl
2079 return [name for name, bit in self.specs.items() if self[bit]]
2089 return self.__class__(
2091 impl=self.tag if impl is None else impl,
2092 expl=self._expl if expl is None else expl,
2093 default=self.default if default is None else default,
2094 optional=self.optional if optional is None else optional,
2098 def __getitem__(self, key):
2099 if isinstance(key, int):
2100 bit_len, octets = self._value
2104 byte2int(memoryview(octets)[key // 8:]) >>
2107 if isinstance(key, string_types):
2108 value = self.specs.get(key)
2110 raise ObjUnknown("BitString value: %s" % key)
2112 raise InvalidValueType((int, str))
2115 self._assert_ready()
2116 bit_len, octets = self._value
2119 len_encode(len(octets) + 1),
2120 int2byte((8 - bit_len % 8) % 8),
2124 def _decode_chunk(self, lv, offset, decode_path, ctx):
2126 l, llen, v = len_decode(lv)
2127 except DecodeError as err:
2128 raise err.__class__(
2130 klass=self.__class__,
2131 decode_path=decode_path,
2135 raise NotEnoughData(
2136 "encoded length is longer than data",
2137 klass=self.__class__,
2138 decode_path=decode_path,
2142 raise NotEnoughData(
2144 klass=self.__class__,
2145 decode_path=decode_path,
2148 pad_size = byte2int(v)
2149 if l == 1 and pad_size != 0:
2151 "invalid empty value",
2152 klass=self.__class__,
2153 decode_path=decode_path,
2159 klass=self.__class__,
2160 decode_path=decode_path,
2163 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2166 klass=self.__class__,
2167 decode_path=decode_path,
2170 v, tail = v[:l], v[l:]
2171 obj = self.__class__(
2172 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2175 default=self.default,
2176 optional=self.optional,
2178 _decoded=(offset, llen, l),
2182 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2184 t, tlen, lv = tag_strip(tlv)
2185 except DecodeError as err:
2186 raise err.__class__(
2188 klass=self.__class__,
2189 decode_path=decode_path,
2195 return self._decode_chunk(lv, offset, decode_path, ctx)
2196 if t == self.tag_constructed:
2197 if not ctx.get("bered", False):
2199 msg="unallowed BER constructed encoding",
2200 decode_path=decode_path,
2207 l, llen, v = len_decode(lv)
2208 except LenIndefForm:
2209 llen, l, v = 1, 0, lv[1:]
2211 except DecodeError as err:
2212 raise err.__class__(
2214 klass=self.__class__,
2215 decode_path=decode_path,
2218 if l > 0 and l > len(v):
2219 raise NotEnoughData(
2220 "encoded length is longer than data",
2221 klass=self.__class__,
2222 decode_path=decode_path,
2225 if not lenindef and l == 0:
2226 raise NotEnoughData(
2228 klass=self.__class__,
2229 decode_path=decode_path,
2233 sub_offset = offset + tlen + llen
2237 if v[:EOC_LEN].tobytes() == EOC:
2244 msg="chunk out of bounds",
2245 decode_path=len(chunks) - 1,
2246 offset=chunks[-1].offset,
2248 sub_decode_path = decode_path + (str(len(chunks)),)
2250 chunk, v_tail = BitString().decode(
2253 decode_path=sub_decode_path,
2259 msg="expected BitString encoded chunk",
2260 decode_path=sub_decode_path,
2263 chunks.append(chunk)
2264 sub_offset += chunk.tlvlen
2265 vlen += chunk.tlvlen
2267 if len(chunks) == 0:
2270 decode_path=decode_path,
2275 for chunk_i, chunk in enumerate(chunks[:-1]):
2276 if chunk.bit_len % 8 != 0:
2278 msg="BitString chunk is not multiple of 8 bit",
2279 decode_path=decode_path + (str(chunk_i),),
2280 offset=chunk.offset,
2282 values.append(bytes(chunk))
2283 bit_len += chunk.bit_len
2284 chunk_last = chunks[-1]
2285 values.append(bytes(chunk_last))
2286 bit_len += chunk_last.bit_len
2287 obj = self.__class__(
2288 value=(bit_len, b"".join(values)),
2291 default=self.default,
2292 optional=self.optional,
2294 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2296 obj.lenindef = lenindef
2298 return obj, (v[EOC_LEN:] if lenindef else v)
2300 klass=self.__class__,
2301 decode_path=decode_path,
2306 return pp_console_row(next(self.pps()))
2308 def pps(self, decode_path=()):
2312 bit_len, blob = self._value
2313 value = "%d bits" % bit_len
2314 if len(self.specs) > 0:
2315 blob = tuple(self.named)
2317 asn1_type_name=self.asn1_type_name,
2318 obj_name=self.__class__.__name__,
2319 decode_path=decode_path,
2322 optional=self.optional,
2323 default=self == self.default,
2324 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2325 expl=None if self._expl is None else tag_decode(self._expl),
2330 expl_offset=self.expl_offset if self.expled else None,
2331 expl_tlen=self.expl_tlen if self.expled else None,
2332 expl_llen=self.expl_llen if self.expled else None,
2333 expl_vlen=self.expl_vlen if self.expled else None,
2334 expl_lenindef=self.expl_lenindef,
2335 lenindef=self.lenindef,
2338 defined_by, defined = self.defined or (None, None)
2339 if defined_by is not None:
2341 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2345 class OctetString(Obj):
2346 """``OCTET STRING`` binary string type
2348 >>> s = OctetString(b"hello world")
2349 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2350 >>> s == OctetString(b"hello world")
2355 >>> OctetString(b"hello", bounds=(4, 4))
2356 Traceback (most recent call last):
2357 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2358 >>> OctetString(b"hell", bounds=(4, 4))
2359 OCTET STRING 4 bytes 68656c6c
2361 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2362 tag_default = tag_encode(4)
2363 asn1_type_name = "OCTET STRING"
2376 :param value: set the value. Either binary type, or
2377 :py:class:`pyderasn.OctetString` object
2378 :param bounds: set ``(MIN, MAX)`` value size constraint.
2379 (-inf, +inf) by default
2380 :param bytes impl: override default tag with ``IMPLICIT`` one
2381 :param bytes expl: override default tag with ``EXPLICIT`` one
2382 :param default: set default value. Type same as in ``value``
2383 :param bool optional: is object ``OPTIONAL`` in sequence
2385 super(OctetString, self).__init__(
2393 self._bound_min, self._bound_max = getattr(
2397 ) if bounds is None else bounds
2398 if value is not None:
2399 self._value = self._value_sanitize(value)
2400 if default is not None:
2401 default = self._value_sanitize(default)
2402 self.default = self.__class__(
2407 if self._value is None:
2408 self._value = default
2410 tag_klass, _, tag_num = tag_decode(self.tag)
2411 self.tag_constructed = tag_encode(
2413 form=TagFormConstructed,
2417 def _value_sanitize(self, value):
2418 if issubclass(value.__class__, OctetString):
2419 value = value._value
2420 elif isinstance(value, binary_type):
2423 raise InvalidValueType((self.__class__, bytes))
2424 if not self._bound_min <= len(value) <= self._bound_max:
2425 raise BoundsError(self._bound_min, len(value), self._bound_max)
2430 return self._value is not None
2433 obj = self.__class__()
2434 obj._value = self._value
2435 obj._bound_min = self._bound_min
2436 obj._bound_max = self._bound_max
2438 obj._expl = self._expl
2439 obj.default = self.default
2440 obj.optional = self.optional
2441 obj.offset = self.offset
2442 obj.llen = self.llen
2443 obj.vlen = self.vlen
2446 def __bytes__(self):
2447 self._assert_ready()
2450 def __eq__(self, their):
2451 if isinstance(their, binary_type):
2452 return self._value == their
2453 if not issubclass(their.__class__, OctetString):
2456 self._value == their._value and
2457 self.tag == their.tag and
2458 self._expl == their._expl
2461 def __lt__(self, their):
2462 return self._value < their._value
2473 return self.__class__(
2476 (self._bound_min, self._bound_max)
2477 if bounds is None else bounds
2479 impl=self.tag if impl is None else impl,
2480 expl=self._expl if expl is None else expl,
2481 default=self.default if default is None else default,
2482 optional=self.optional if optional is None else optional,
2486 self._assert_ready()
2489 len_encode(len(self._value)),
2493 def _decode_chunk(self, lv, offset, decode_path, ctx):
2495 l, llen, v = len_decode(lv)
2496 except DecodeError as err:
2497 raise err.__class__(
2499 klass=self.__class__,
2500 decode_path=decode_path,
2504 raise NotEnoughData(
2505 "encoded length is longer than data",
2506 klass=self.__class__,
2507 decode_path=decode_path,
2510 v, tail = v[:l], v[l:]
2512 obj = self.__class__(
2514 bounds=(self._bound_min, self._bound_max),
2517 default=self.default,
2518 optional=self.optional,
2519 _decoded=(offset, llen, l),
2521 except DecodeError as err:
2524 klass=self.__class__,
2525 decode_path=decode_path,
2528 except BoundsError as err:
2531 klass=self.__class__,
2532 decode_path=decode_path,
2537 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2539 t, tlen, lv = tag_strip(tlv)
2540 except DecodeError as err:
2541 raise err.__class__(
2543 klass=self.__class__,
2544 decode_path=decode_path,
2550 return self._decode_chunk(lv, offset, decode_path, ctx)
2551 if t == self.tag_constructed:
2552 if not ctx.get("bered", False):
2554 msg="unallowed BER constructed encoding",
2555 decode_path=decode_path,
2562 l, llen, v = len_decode(lv)
2563 except LenIndefForm:
2564 llen, l, v = 1, 0, lv[1:]
2566 except DecodeError as err:
2567 raise err.__class__(
2569 klass=self.__class__,
2570 decode_path=decode_path,
2573 if l > 0 and l > len(v):
2574 raise NotEnoughData(
2575 "encoded length is longer than data",
2576 klass=self.__class__,
2577 decode_path=decode_path,
2580 if not lenindef and l == 0:
2581 raise NotEnoughData(
2583 klass=self.__class__,
2584 decode_path=decode_path,
2588 sub_offset = offset + tlen + llen
2592 if v[:EOC_LEN].tobytes() == EOC:
2599 msg="chunk out of bounds",
2600 decode_path=len(chunks) - 1,
2601 offset=chunks[-1].offset,
2603 sub_decode_path = decode_path + (str(len(chunks)),)
2605 chunk, v_tail = OctetString().decode(
2608 decode_path=sub_decode_path,
2614 msg="expected OctetString encoded chunk",
2615 decode_path=sub_decode_path,
2618 chunks.append(chunk)
2619 sub_offset += chunk.tlvlen
2620 vlen += chunk.tlvlen
2622 if len(chunks) == 0:
2625 decode_path=decode_path,
2629 obj = self.__class__(
2630 value=b"".join(bytes(chunk) for chunk in chunks),
2631 bounds=(self._bound_min, self._bound_max),
2634 default=self.default,
2635 optional=self.optional,
2636 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2638 except DecodeError as err:
2641 klass=self.__class__,
2642 decode_path=decode_path,
2645 except BoundsError as err:
2648 klass=self.__class__,
2649 decode_path=decode_path,
2652 obj.lenindef = lenindef
2654 return obj, (v[EOC_LEN:] if lenindef else v)
2656 klass=self.__class__,
2657 decode_path=decode_path,
2662 return pp_console_row(next(self.pps()))
2664 def pps(self, decode_path=()):
2666 asn1_type_name=self.asn1_type_name,
2667 obj_name=self.__class__.__name__,
2668 decode_path=decode_path,
2669 value=("%d bytes" % len(self._value)) if self.ready else None,
2670 blob=self._value if self.ready else None,
2671 optional=self.optional,
2672 default=self == self.default,
2673 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2674 expl=None if self._expl is None else tag_decode(self._expl),
2679 expl_offset=self.expl_offset if self.expled else None,
2680 expl_tlen=self.expl_tlen if self.expled else None,
2681 expl_llen=self.expl_llen if self.expled else None,
2682 expl_vlen=self.expl_vlen if self.expled else None,
2683 expl_lenindef=self.expl_lenindef,
2684 lenindef=self.lenindef,
2687 defined_by, defined = self.defined or (None, None)
2688 if defined_by is not None:
2690 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2695 """``NULL`` null object
2703 tag_default = tag_encode(5)
2704 asn1_type_name = "NULL"
2708 value=None, # unused, but Sequence passes it
2715 :param bytes impl: override default tag with ``IMPLICIT`` one
2716 :param bytes expl: override default tag with ``EXPLICIT`` one
2717 :param bool optional: is object ``OPTIONAL`` in sequence
2719 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2727 obj = self.__class__()
2729 obj._expl = self._expl
2730 obj.default = self.default
2731 obj.optional = self.optional
2732 obj.offset = self.offset
2733 obj.llen = self.llen
2734 obj.vlen = self.vlen
2737 def __eq__(self, their):
2738 if not issubclass(their.__class__, Null):
2741 self.tag == their.tag and
2742 self._expl == their._expl
2752 return self.__class__(
2753 impl=self.tag if impl is None else impl,
2754 expl=self._expl if expl is None else expl,
2755 optional=self.optional if optional is None else optional,
2759 return self.tag + len_encode(0)
2761 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2763 t, _, lv = tag_strip(tlv)
2764 except DecodeError as err:
2765 raise err.__class__(
2767 klass=self.__class__,
2768 decode_path=decode_path,
2773 klass=self.__class__,
2774 decode_path=decode_path,
2780 l, _, v = len_decode(lv)
2781 except DecodeError as err:
2782 raise err.__class__(
2784 klass=self.__class__,
2785 decode_path=decode_path,
2789 raise InvalidLength(
2790 "Null must have zero length",
2791 klass=self.__class__,
2792 decode_path=decode_path,
2795 obj = self.__class__(
2798 optional=self.optional,
2799 _decoded=(offset, 1, 0),
2804 return pp_console_row(next(self.pps()))
2806 def pps(self, decode_path=()):
2808 asn1_type_name=self.asn1_type_name,
2809 obj_name=self.__class__.__name__,
2810 decode_path=decode_path,
2811 optional=self.optional,
2812 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2813 expl=None if self._expl is None else tag_decode(self._expl),
2818 expl_offset=self.expl_offset if self.expled else None,
2819 expl_tlen=self.expl_tlen if self.expled else None,
2820 expl_llen=self.expl_llen if self.expled else None,
2821 expl_vlen=self.expl_vlen if self.expled else None,
2822 expl_lenindef=self.expl_lenindef,
2826 class ObjectIdentifier(Obj):
2827 """``OBJECT IDENTIFIER`` OID type
2829 >>> oid = ObjectIdentifier((1, 2, 3))
2830 OBJECT IDENTIFIER 1.2.3
2831 >>> oid == ObjectIdentifier("1.2.3")
2837 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2838 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2840 >>> str(ObjectIdentifier((3, 1)))
2841 Traceback (most recent call last):
2842 pyderasn.InvalidOID: unacceptable first arc value
2844 __slots__ = ("defines",)
2845 tag_default = tag_encode(6)
2846 asn1_type_name = "OBJECT IDENTIFIER"
2859 :param value: set the value. Either tuples of integers,
2860 string of "."-concatenated integers, or
2861 :py:class:`pyderasn.ObjectIdentifier` object
2862 :param defines: sequence of tuples. Each tuple has two elements.
2863 First one is relative to current one decode
2864 path, aiming to the field defined by that OID.
2865 Read about relative path in
2866 :py:func:`pyderasn.abs_decode_path`. Second
2867 tuple element is ``{OID: pyderasn.Obj()}``
2868 dictionary, mapping between current OID value
2869 and structure applied to defined field.
2870 :ref:`Read about DEFINED BY <definedby>`
2871 :param bytes impl: override default tag with ``IMPLICIT`` one
2872 :param bytes expl: override default tag with ``EXPLICIT`` one
2873 :param default: set default value. Type same as in ``value``
2874 :param bool optional: is object ``OPTIONAL`` in sequence
2876 super(ObjectIdentifier, self).__init__(
2884 if value is not None:
2885 self._value = self._value_sanitize(value)
2886 if default is not None:
2887 default = self._value_sanitize(default)
2888 self.default = self.__class__(
2893 if self._value is None:
2894 self._value = default
2895 self.defines = defines
2897 def __add__(self, their):
2898 if isinstance(their, self.__class__):
2899 return self.__class__(self._value + their._value)
2900 if isinstance(their, tuple):
2901 return self.__class__(self._value + their)
2902 raise InvalidValueType((self.__class__, tuple))
2904 def _value_sanitize(self, value):
2905 if issubclass(value.__class__, ObjectIdentifier):
2907 if isinstance(value, string_types):
2909 value = tuple(int(arc) for arc in value.split("."))
2911 raise InvalidOID("unacceptable arcs values")
2912 if isinstance(value, tuple):
2914 raise InvalidOID("less than 2 arcs")
2915 first_arc = value[0]
2916 if first_arc in (0, 1):
2917 if not (0 <= value[1] <= 39):
2918 raise InvalidOID("second arc is too wide")
2919 elif first_arc == 2:
2922 raise InvalidOID("unacceptable first arc value")
2924 raise InvalidValueType((self.__class__, str, tuple))
2928 return self._value is not None
2931 obj = self.__class__()
2932 obj._value = self._value
2933 obj.defines = self.defines
2935 obj._expl = self._expl
2936 obj.default = self.default
2937 obj.optional = self.optional
2938 obj.offset = self.offset
2939 obj.llen = self.llen
2940 obj.vlen = self.vlen
2944 self._assert_ready()
2945 return iter(self._value)
2948 return ".".join(str(arc) for arc in self._value or ())
2951 self._assert_ready()
2954 bytes(self._expl or b"") +
2955 str(self._value).encode("ascii"),
2958 def __eq__(self, their):
2959 if isinstance(their, tuple):
2960 return self._value == their
2961 if not issubclass(their.__class__, ObjectIdentifier):
2964 self.tag == their.tag and
2965 self._expl == their._expl and
2966 self._value == their._value
2969 def __lt__(self, their):
2970 return self._value < their._value
2981 return self.__class__(
2983 defines=self.defines if defines is None else defines,
2984 impl=self.tag if impl is None else impl,
2985 expl=self._expl if expl is None else expl,
2986 default=self.default if default is None else default,
2987 optional=self.optional if optional is None else optional,
2991 self._assert_ready()
2993 first_value = value[1]
2994 first_arc = value[0]
2997 elif first_arc == 1:
2999 elif first_arc == 2:
3001 else: # pragma: no cover
3002 raise RuntimeError("invalid arc is stored")
3003 octets = [zero_ended_encode(first_value)]
3004 for arc in value[2:]:
3005 octets.append(zero_ended_encode(arc))
3006 v = b"".join(octets)
3007 return b"".join((self.tag, len_encode(len(v)), v))
3009 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3011 t, _, lv = tag_strip(tlv)
3012 except DecodeError as err:
3013 raise err.__class__(
3015 klass=self.__class__,
3016 decode_path=decode_path,
3021 klass=self.__class__,
3022 decode_path=decode_path,
3028 l, llen, v = len_decode(lv)
3029 except DecodeError as err:
3030 raise err.__class__(
3032 klass=self.__class__,
3033 decode_path=decode_path,
3037 raise NotEnoughData(
3038 "encoded length is longer than data",
3039 klass=self.__class__,
3040 decode_path=decode_path,
3044 raise NotEnoughData(
3046 klass=self.__class__,
3047 decode_path=decode_path,
3050 v, tail = v[:l], v[l:]
3056 octet = indexbytes(v, i)
3057 arc = (arc << 7) | (octet & 0x7F)
3058 if octet & 0x80 == 0:
3066 klass=self.__class__,
3067 decode_path=decode_path,
3071 second_arc = arcs[0]
3072 if 0 <= second_arc <= 39:
3074 elif 40 <= second_arc <= 79:
3080 obj = self.__class__(
3081 value=tuple([first_arc, second_arc] + arcs[1:]),
3084 default=self.default,
3085 optional=self.optional,
3086 _decoded=(offset, llen, l),
3091 return pp_console_row(next(self.pps()))
3093 def pps(self, decode_path=()):
3095 asn1_type_name=self.asn1_type_name,
3096 obj_name=self.__class__.__name__,
3097 decode_path=decode_path,
3098 value=str(self) if self.ready else None,
3099 optional=self.optional,
3100 default=self == self.default,
3101 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3102 expl=None if self._expl is None else tag_decode(self._expl),
3107 expl_offset=self.expl_offset if self.expled else None,
3108 expl_tlen=self.expl_tlen if self.expled else None,
3109 expl_llen=self.expl_llen if self.expled else None,
3110 expl_vlen=self.expl_vlen if self.expled else None,
3111 expl_lenindef=self.expl_lenindef,
3115 class Enumerated(Integer):
3116 """``ENUMERATED`` integer type
3118 This type is identical to :py:class:`pyderasn.Integer`, but requires
3119 schema to be specified and does not accept values missing from it.
3122 tag_default = tag_encode(10)
3123 asn1_type_name = "ENUMERATED"
3134 bounds=None, # dummy argument, workability for Integer.decode
3136 super(Enumerated, self).__init__(
3145 if len(self.specs) == 0:
3146 raise ValueError("schema must be specified")
3148 def _value_sanitize(self, value):
3149 if isinstance(value, self.__class__):
3150 value = value._value
3151 elif isinstance(value, integer_types):
3152 if value not in list(self.specs.values()):
3154 "unknown integer value: %s" % value,
3155 klass=self.__class__,
3157 elif isinstance(value, string_types):
3158 value = self.specs.get(value)
3160 raise ObjUnknown("integer value: %s" % value)
3162 raise InvalidValueType((self.__class__, int, str))
3166 obj = self.__class__(_specs=self.specs)
3167 obj._value = self._value
3168 obj._bound_min = self._bound_min
3169 obj._bound_max = self._bound_max
3171 obj._expl = self._expl
3172 obj.default = self.default
3173 obj.optional = self.optional
3174 obj.offset = self.offset
3175 obj.llen = self.llen
3176 obj.vlen = self.vlen
3188 return self.__class__(
3190 impl=self.tag if impl is None else impl,
3191 expl=self._expl if expl is None else expl,
3192 default=self.default if default is None else default,
3193 optional=self.optional if optional is None else optional,
3198 class CommonString(OctetString):
3199 """Common class for all strings
3201 Everything resembles :py:class:`pyderasn.OctetString`, except
3202 ability to deal with unicode text strings.
3204 >>> hexenc("привет мир".encode("utf-8"))
3205 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3206 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3208 >>> s = UTF8String("привет мир")
3209 UTF8String UTF8String привет мир
3211 'привет мир'
3212 >>> hexenc(bytes(s))
3213 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3215 >>> PrintableString("привет мир")
3216 Traceback (most recent call last):
3217 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3219 >>> BMPString("ада", bounds=(2, 2))
3220 Traceback (most recent call last):
3221 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3222 >>> s = BMPString("ад", bounds=(2, 2))
3225 >>> hexenc(bytes(s))
3233 * - :py:class:`pyderasn.UTF8String`
3235 * - :py:class:`pyderasn.NumericString`
3237 * - :py:class:`pyderasn.PrintableString`
3239 * - :py:class:`pyderasn.TeletexString`
3241 * - :py:class:`pyderasn.T61String`
3243 * - :py:class:`pyderasn.VideotexString`
3245 * - :py:class:`pyderasn.IA5String`
3247 * - :py:class:`pyderasn.GraphicString`
3249 * - :py:class:`pyderasn.VisibleString`
3251 * - :py:class:`pyderasn.ISO646String`
3253 * - :py:class:`pyderasn.GeneralString`
3255 * - :py:class:`pyderasn.UniversalString`
3257 * - :py:class:`pyderasn.BMPString`
3260 __slots__ = ("encoding",)
3262 def _value_sanitize(self, value):
3264 value_decoded = None
3265 if isinstance(value, self.__class__):
3266 value_raw = value._value
3267 elif isinstance(value, text_type):
3268 value_decoded = value
3269 elif isinstance(value, binary_type):
3272 raise InvalidValueType((self.__class__, text_type, binary_type))
3275 value_decoded.encode(self.encoding)
3276 if value_raw is None else value_raw
3279 value_raw.decode(self.encoding)
3280 if value_decoded is None else value_decoded
3282 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3283 raise DecodeError(str(err))
3284 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3292 def __eq__(self, their):
3293 if isinstance(their, binary_type):
3294 return self._value == their
3295 if isinstance(their, text_type):
3296 return self._value == their.encode(self.encoding)
3297 if not isinstance(their, self.__class__):
3300 self._value == their._value and
3301 self.tag == their.tag and
3302 self._expl == their._expl
3305 def __unicode__(self):
3307 return self._value.decode(self.encoding)
3308 return text_type(self._value)
3311 return pp_console_row(next(self.pps(no_unicode=PY2)))
3313 def pps(self, decode_path=(), no_unicode=False):
3316 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3318 asn1_type_name=self.asn1_type_name,
3319 obj_name=self.__class__.__name__,
3320 decode_path=decode_path,
3322 optional=self.optional,
3323 default=self == self.default,
3324 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3325 expl=None if self._expl is None else tag_decode(self._expl),
3330 expl_offset=self.expl_offset if self.expled else None,
3331 expl_tlen=self.expl_tlen if self.expled else None,
3332 expl_llen=self.expl_llen if self.expled else None,
3333 expl_vlen=self.expl_vlen if self.expled else None,
3334 expl_lenindef=self.expl_lenindef,
3338 class UTF8String(CommonString):
3340 tag_default = tag_encode(12)
3342 asn1_type_name = "UTF8String"
3345 class NumericString(CommonString):
3348 Its value is properly sanitized: only ASCII digits can be stored.
3351 tag_default = tag_encode(18)
3353 asn1_type_name = "NumericString"
3354 allowable_chars = set(digits.encode("ascii"))
3356 def _value_sanitize(self, value):
3357 value = super(NumericString, self)._value_sanitize(value)
3358 if not set(value) <= self.allowable_chars:
3359 raise DecodeError("non-numeric value")
3363 class PrintableString(CommonString):
3365 tag_default = tag_encode(19)
3367 asn1_type_name = "PrintableString"
3370 class TeletexString(CommonString):
3372 tag_default = tag_encode(20)
3374 asn1_type_name = "TeletexString"
3377 class T61String(TeletexString):
3379 asn1_type_name = "T61String"
3382 class VideotexString(CommonString):
3384 tag_default = tag_encode(21)
3385 encoding = "iso-8859-1"
3386 asn1_type_name = "VideotexString"
3389 class IA5String(CommonString):
3391 tag_default = tag_encode(22)
3393 asn1_type_name = "IA5"
3396 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3397 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3398 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3401 class UTCTime(CommonString):
3402 """``UTCTime`` datetime type
3404 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3405 UTCTime UTCTime 2017-09-30T22:07:50
3411 datetime.datetime(2017, 9, 30, 22, 7, 50)
3412 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3413 datetime.datetime(1957, 9, 30, 22, 7, 50)
3416 tag_default = tag_encode(23)
3418 asn1_type_name = "UTCTime"
3420 fmt = "%y%m%d%H%M%SZ"
3430 bounds=None, # dummy argument, workability for OctetString.decode
3433 :param value: set the value. Either datetime type, or
3434 :py:class:`pyderasn.UTCTime` object
3435 :param bytes impl: override default tag with ``IMPLICIT`` one
3436 :param bytes expl: override default tag with ``EXPLICIT`` one
3437 :param default: set default value. Type same as in ``value``
3438 :param bool optional: is object ``OPTIONAL`` in sequence
3440 super(UTCTime, self).__init__(
3448 if value is not None:
3449 self._value = self._value_sanitize(value)
3450 if default is not None:
3451 default = self._value_sanitize(default)
3452 self.default = self.__class__(
3457 if self._value is None:
3458 self._value = default
3460 def _value_sanitize(self, value):
3461 if isinstance(value, self.__class__):
3463 if isinstance(value, datetime):
3464 return value.strftime(self.fmt).encode("ascii")
3465 if isinstance(value, binary_type):
3466 value_decoded = value.decode("ascii")
3467 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3469 datetime.strptime(value_decoded, self.fmt)
3471 raise DecodeError("invalid UTCTime format")
3474 raise DecodeError("invalid UTCTime length")
3475 raise InvalidValueType((self.__class__, datetime))
3477 def __eq__(self, their):
3478 if isinstance(their, binary_type):
3479 return self._value == their
3480 if isinstance(their, datetime):
3481 return self.todatetime() == their
3482 if not isinstance(their, self.__class__):
3485 self._value == their._value and
3486 self.tag == their.tag and
3487 self._expl == their._expl
3490 def todatetime(self):
3491 """Convert to datetime
3495 Pay attention that UTCTime can not hold full year, so all years
3496 having < 50 years are treated as 20xx, 19xx otherwise, according
3497 to X.509 recomendation.
3499 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3500 year = value.year % 100
3502 year=(2000 + year) if year < 50 else (1900 + year),
3506 minute=value.minute,
3507 second=value.second,
3511 return pp_console_row(next(self.pps()))
3513 def pps(self, decode_path=()):
3515 asn1_type_name=self.asn1_type_name,
3516 obj_name=self.__class__.__name__,
3517 decode_path=decode_path,
3518 value=self.todatetime().isoformat() if self.ready else None,
3519 optional=self.optional,
3520 default=self == self.default,
3521 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3522 expl=None if self._expl is None else tag_decode(self._expl),
3527 expl_offset=self.expl_offset if self.expled else None,
3528 expl_tlen=self.expl_tlen if self.expled else None,
3529 expl_llen=self.expl_llen if self.expled else None,
3530 expl_vlen=self.expl_vlen if self.expled else None,
3531 expl_lenindef=self.expl_lenindef,
3535 class GeneralizedTime(UTCTime):
3536 """``GeneralizedTime`` datetime type
3538 This type is similar to :py:class:`pyderasn.UTCTime`.
3540 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3541 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3543 '20170930220750.000123Z'
3544 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3545 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3548 tag_default = tag_encode(24)
3549 asn1_type_name = "GeneralizedTime"
3551 fmt = "%Y%m%d%H%M%SZ"
3552 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3554 def _value_sanitize(self, value):
3555 if isinstance(value, self.__class__):
3557 if isinstance(value, datetime):
3558 return value.strftime(
3559 self.fmt_ms if value.microsecond > 0 else self.fmt
3561 if isinstance(value, binary_type):
3562 value_decoded = value.decode("ascii")
3563 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3565 datetime.strptime(value_decoded, self.fmt)
3568 "invalid GeneralizedTime (without ms) format",
3571 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3573 datetime.strptime(value_decoded, self.fmt_ms)
3576 "invalid GeneralizedTime (with ms) format",
3581 "invalid GeneralizedTime length",
3582 klass=self.__class__,
3584 raise InvalidValueType((self.__class__, datetime))
3586 def todatetime(self):
3587 value = self._value.decode("ascii")
3588 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3589 return datetime.strptime(value, self.fmt)
3590 return datetime.strptime(value, self.fmt_ms)
3593 class GraphicString(CommonString):
3595 tag_default = tag_encode(25)
3596 encoding = "iso-8859-1"
3597 asn1_type_name = "GraphicString"
3600 class VisibleString(CommonString):
3602 tag_default = tag_encode(26)
3604 asn1_type_name = "VisibleString"
3607 class ISO646String(VisibleString):
3609 asn1_type_name = "ISO646String"
3612 class GeneralString(CommonString):
3614 tag_default = tag_encode(27)
3615 encoding = "iso-8859-1"
3616 asn1_type_name = "GeneralString"
3619 class UniversalString(CommonString):
3621 tag_default = tag_encode(28)
3622 encoding = "utf-32-be"
3623 asn1_type_name = "UniversalString"
3626 class BMPString(CommonString):
3628 tag_default = tag_encode(30)
3629 encoding = "utf-16-be"
3630 asn1_type_name = "BMPString"
3634 """``CHOICE`` special type
3638 class GeneralName(Choice):
3640 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3641 ("dNSName", IA5String(impl=tag_ctxp(2))),
3644 >>> gn = GeneralName()
3646 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3647 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3648 >>> gn["dNSName"] = IA5String("bar.baz")
3649 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3650 >>> gn["rfc822Name"]
3653 [2] IA5String IA5 bar.baz
3656 >>> gn.value == gn["dNSName"]
3659 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3661 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3662 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3664 __slots__ = ("specs",)
3666 asn1_type_name = "CHOICE"
3679 :param value: set the value. Either ``(choice, value)`` tuple, or
3680 :py:class:`pyderasn.Choice` object
3681 :param bytes impl: can not be set, do **not** use it
3682 :param bytes expl: override default tag with ``EXPLICIT`` one
3683 :param default: set default value. Type same as in ``value``
3684 :param bool optional: is object ``OPTIONAL`` in sequence
3686 if impl is not None:
3687 raise ValueError("no implicit tag allowed for CHOICE")
3688 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3690 schema = getattr(self, "schema", ())
3691 if len(schema) == 0:
3692 raise ValueError("schema must be specified")
3694 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3697 if value is not None:
3698 self._value = self._value_sanitize(value)
3699 if default is not None:
3700 default_value = self._value_sanitize(default)
3701 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3702 default_obj.specs = self.specs
3703 default_obj._value = default_value
3704 self.default = default_obj
3706 self._value = default_obj.copy()._value
3708 def _value_sanitize(self, value):
3709 if isinstance(value, self.__class__):
3711 if isinstance(value, tuple) and len(value) == 2:
3713 spec = self.specs.get(choice)
3715 raise ObjUnknown(choice)
3716 if not isinstance(obj, spec.__class__):
3717 raise InvalidValueType((spec,))
3718 return (choice, spec(obj))
3719 raise InvalidValueType((self.__class__, tuple))
3723 return self._value is not None and self._value[1].ready
3726 obj = self.__class__(schema=self.specs)
3727 obj._expl = self._expl
3728 obj.default = self.default
3729 obj.optional = self.optional
3730 obj.offset = self.offset
3731 obj.llen = self.llen
3732 obj.vlen = self.vlen
3734 if value is not None:
3735 obj._value = (value[0], value[1].copy())
3738 def __eq__(self, their):
3739 if isinstance(their, tuple) and len(their) == 2:
3740 return self._value == their
3741 if not isinstance(their, self.__class__):
3744 self.specs == their.specs and
3745 self._value == their._value
3755 return self.__class__(
3758 expl=self._expl if expl is None else expl,
3759 default=self.default if default is None else default,
3760 optional=self.optional if optional is None else optional,
3765 self._assert_ready()
3766 return self._value[0]
3770 self._assert_ready()
3771 return self._value[1]
3773 def __getitem__(self, key):
3774 if key not in self.specs:
3775 raise ObjUnknown(key)
3776 if self._value is None:
3778 choice, value = self._value
3783 def __setitem__(self, key, value):
3784 spec = self.specs.get(key)
3786 raise ObjUnknown(key)
3787 if not isinstance(value, spec.__class__):
3788 raise InvalidValueType((spec.__class__,))
3789 self._value = (key, spec(value))
3797 return self._value[1].decoded if self.ready else False
3800 self._assert_ready()
3801 return self._value[1].encode()
3803 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3804 for choice, spec in self.specs.items():
3805 sub_decode_path = decode_path + (choice,)
3811 decode_path=sub_decode_path,
3820 klass=self.__class__,
3821 decode_path=decode_path,
3826 value, tail = spec.decode(
3830 decode_path=sub_decode_path,
3833 obj = self.__class__(
3836 default=self.default,
3837 optional=self.optional,
3838 _decoded=(offset, 0, value.tlvlen),
3840 obj._value = (choice, value)
3844 value = pp_console_row(next(self.pps()))
3846 value = "%s[%r]" % (value, self.value)
3849 def pps(self, decode_path=()):
3851 asn1_type_name=self.asn1_type_name,
3852 obj_name=self.__class__.__name__,
3853 decode_path=decode_path,
3854 value=self.choice if self.ready else None,
3855 optional=self.optional,
3856 default=self == self.default,
3857 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3858 expl=None if self._expl is None else tag_decode(self._expl),
3863 expl_lenindef=self.expl_lenindef,
3866 yield self.value.pps(decode_path=decode_path + (self.choice,))
3869 class PrimitiveTypes(Choice):
3870 """Predefined ``CHOICE`` for all generic primitive types
3872 It could be useful for general decoding of some unspecified values:
3874 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3875 OCTET STRING 3 bytes 666f6f
3876 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3880 schema = tuple((klass.__name__, klass()) for klass in (
3905 """``ANY`` special type
3907 >>> Any(Integer(-123))
3909 >>> a = Any(OctetString(b"hello world").encode())
3910 ANY 040b68656c6c6f20776f726c64
3911 >>> hexenc(bytes(a))
3912 b'0x040x0bhello world'
3914 __slots__ = ("defined",)
3915 tag_default = tag_encode(0)
3916 asn1_type_name = "ANY"
3926 :param value: set the value. Either any kind of pyderasn's
3927 **ready** object, or bytes. Pay attention that
3928 **no** validation is performed is raw binary value
3930 :param bytes expl: override default tag with ``EXPLICIT`` one
3931 :param bool optional: is object ``OPTIONAL`` in sequence
3933 super(Any, self).__init__(None, expl, None, optional, _decoded)
3934 self._value = None if value is None else self._value_sanitize(value)
3937 def _value_sanitize(self, value):
3938 if isinstance(value, self.__class__):
3940 if isinstance(value, Obj):
3941 return value.encode()
3942 if isinstance(value, binary_type):
3944 raise InvalidValueType((self.__class__, Obj, binary_type))
3948 return self._value is not None
3951 obj = self.__class__()
3952 obj._value = self._value
3954 obj._expl = self._expl
3955 obj.optional = self.optional
3956 obj.offset = self.offset
3957 obj.llen = self.llen
3958 obj.vlen = self.vlen
3961 def __eq__(self, their):
3962 if isinstance(their, binary_type):
3963 return self._value == their
3964 if issubclass(their.__class__, Any):
3965 return self._value == their._value
3974 return self.__class__(
3976 expl=self._expl if expl is None else expl,
3977 optional=self.optional if optional is None else optional,
3980 def __bytes__(self):
3981 self._assert_ready()
3989 self._assert_ready()
3992 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3994 t, tlen, lv = tag_strip(tlv)
3995 except DecodeError as err:
3996 raise err.__class__(
3998 klass=self.__class__,
3999 decode_path=decode_path,
4003 l, llen, v = len_decode(lv)
4004 except LenIndefForm as err:
4005 if not ctx.get("bered", False):
4006 raise err.__class__(
4008 klass=self.__class__,
4009 decode_path=decode_path,
4012 llen, vlen, v = 1, 0, lv[1:]
4013 sub_offset = offset + tlen + llen
4016 if v[:EOC_LEN].tobytes() == EOC:
4017 tlvlen = tlen + llen + vlen + EOC_LEN
4018 obj = self.__class__(
4019 value=tlv[:tlvlen].tobytes(),
4021 optional=self.optional,
4022 _decoded=(offset, 0, tlvlen),
4026 return obj, v[EOC_LEN:]
4028 chunk, v = Any().decode(
4031 decode_path=decode_path + (str(chunk_i),),
4035 vlen += chunk.tlvlen
4036 sub_offset += chunk.tlvlen
4038 except DecodeError as err:
4039 raise err.__class__(
4041 klass=self.__class__,
4042 decode_path=decode_path,
4046 raise NotEnoughData(
4047 "encoded length is longer than data",
4048 klass=self.__class__,
4049 decode_path=decode_path,
4052 tlvlen = tlen + llen + l
4053 v, tail = tlv[:tlvlen], v[l:]
4054 obj = self.__class__(
4057 optional=self.optional,
4058 _decoded=(offset, 0, tlvlen),
4064 return pp_console_row(next(self.pps()))
4066 def pps(self, decode_path=()):
4068 asn1_type_name=self.asn1_type_name,
4069 obj_name=self.__class__.__name__,
4070 decode_path=decode_path,
4071 blob=self._value if self.ready else None,
4072 optional=self.optional,
4073 default=self == self.default,
4074 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4075 expl=None if self._expl is None else tag_decode(self._expl),
4080 expl_offset=self.expl_offset if self.expled else None,
4081 expl_tlen=self.expl_tlen if self.expled else None,
4082 expl_llen=self.expl_llen if self.expled else None,
4083 expl_vlen=self.expl_vlen if self.expled else None,
4084 expl_lenindef=self.expl_lenindef,
4085 lenindef=self.lenindef,
4087 defined_by, defined = self.defined or (None, None)
4088 if defined_by is not None:
4090 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4094 ########################################################################
4095 # ASN.1 constructed types
4096 ########################################################################
4098 def get_def_by_path(defines_by_path, sub_decode_path):
4099 """Get define by decode path
4101 for path, define in defines_by_path:
4102 if len(path) != len(sub_decode_path):
4104 for p1, p2 in zip(path, sub_decode_path):
4105 if (p1 != any) and (p1 != p2):
4111 def abs_decode_path(decode_path, rel_path):
4112 """Create an absolute decode path from current and relative ones
4114 :param decode_path: current decode path, starting point.
4116 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4117 If first tuple's element is "/", then treat it as
4118 an absolute path, ignoring ``decode_path`` as
4119 starting point. Also this tuple can contain ".."
4120 elements, stripping the leading element from
4123 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4124 ("foo", "bar", "baz", "whatever")
4125 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4127 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4130 if rel_path[0] == "/":
4132 if rel_path[0] == "..":
4133 return abs_decode_path(decode_path[:-1], rel_path[1:])
4134 return decode_path + rel_path
4137 class Sequence(Obj):
4138 """``SEQUENCE`` structure type
4140 You have to make specification of sequence::
4142 class Extension(Sequence):
4144 ("extnID", ObjectIdentifier()),
4145 ("critical", Boolean(default=False)),
4146 ("extnValue", OctetString()),
4149 Then, you can work with it as with dictionary.
4151 >>> ext = Extension()
4152 >>> Extension().specs
4154 ('extnID', OBJECT IDENTIFIER),
4155 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4156 ('extnValue', OCTET STRING),
4158 >>> ext["extnID"] = "1.2.3"
4159 Traceback (most recent call last):
4160 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4161 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4163 You can determine if sequence is ready to be encoded:
4168 Traceback (most recent call last):
4169 pyderasn.ObjNotReady: object is not ready: extnValue
4170 >>> ext["extnValue"] = OctetString(b"foobar")
4174 Value you want to assign, must have the same **type** as in
4175 corresponding specification, but it can have different tags,
4176 optional/default attributes -- they will be taken from specification
4179 class TBSCertificate(Sequence):
4181 ("version", Version(expl=tag_ctxc(0), default="v1")),
4184 >>> tbs = TBSCertificate()
4185 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4187 Assign ``None`` to remove value from sequence.
4189 You can set values in Sequence during its initialization:
4191 >>> AlgorithmIdentifier((
4192 ("algorithm", ObjectIdentifier("1.2.3")),
4193 ("parameters", Any(Null()))
4195 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4197 You can determine if value exists/set in the sequence and take its value:
4199 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4202 OBJECT IDENTIFIER 1.2.3
4204 But pay attention that if value has default, then it won't be (not
4205 in) in the sequence (because ``DEFAULT`` must not be encoded in
4206 DER), but you can read its value:
4208 >>> "critical" in ext, ext["critical"]
4209 (False, BOOLEAN False)
4210 >>> ext["critical"] = Boolean(True)
4211 >>> "critical" in ext, ext["critical"]
4212 (True, BOOLEAN True)
4214 All defaulted values are always optional.
4216 .. _strict_default_existence_ctx:
4220 When decoded DER contains defaulted value inside, then
4221 technically this is not valid DER encoding. But we allow and pass
4222 it **by default**. Of course reencoding of that kind of DER will
4223 result in different binary representation (validly without
4224 defaulted value inside). You can enable strict defaulted values
4225 existence validation by setting ``"strict_default_existence":
4226 True`` :ref:`context <ctx>` option -- decoding process will raise
4227 an exception if defaulted value is met.
4229 Two sequences are equal if they have equal specification (schema),
4230 implicit/explicit tagging and the same values.
4232 __slots__ = ("specs",)
4233 tag_default = tag_encode(form=TagFormConstructed, num=16)
4234 asn1_type_name = "SEQUENCE"
4246 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4248 schema = getattr(self, "schema", ())
4250 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4253 if value is not None:
4254 if issubclass(value.__class__, Sequence):
4255 self._value = value._value
4256 elif hasattr(value, "__iter__"):
4257 for seq_key, seq_value in value:
4258 self[seq_key] = seq_value
4260 raise InvalidValueType((Sequence,))
4261 if default is not None:
4262 if not issubclass(default.__class__, Sequence):
4263 raise InvalidValueType((Sequence,))
4264 default_value = default._value
4265 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4266 default_obj.specs = self.specs
4267 default_obj._value = default_value
4268 self.default = default_obj
4270 self._value = default_obj.copy()._value
4274 for name, spec in self.specs.items():
4275 value = self._value.get(name)
4286 obj = self.__class__(schema=self.specs)
4288 obj._expl = self._expl
4289 obj.default = self.default
4290 obj.optional = self.optional
4291 obj.offset = self.offset
4292 obj.llen = self.llen
4293 obj.vlen = self.vlen
4294 obj._value = {k: v.copy() for k, v in self._value.items()}
4297 def __eq__(self, their):
4298 if not isinstance(their, self.__class__):
4301 self.specs == their.specs and
4302 self.tag == their.tag and
4303 self._expl == their._expl and
4304 self._value == their._value
4315 return self.__class__(
4318 impl=self.tag if impl is None else impl,
4319 expl=self._expl if expl is None else expl,
4320 default=self.default if default is None else default,
4321 optional=self.optional if optional is None else optional,
4324 def __contains__(self, key):
4325 return key in self._value
4327 def __setitem__(self, key, value):
4328 spec = self.specs.get(key)
4330 raise ObjUnknown(key)
4332 self._value.pop(key, None)
4334 if not isinstance(value, spec.__class__):
4335 raise InvalidValueType((spec.__class__,))
4336 value = spec(value=value)
4337 if spec.default is not None and value == spec.default:
4338 self._value.pop(key, None)
4340 self._value[key] = value
4342 def __getitem__(self, key):
4343 value = self._value.get(key)
4344 if value is not None:
4346 spec = self.specs.get(key)
4348 raise ObjUnknown(key)
4349 if spec.default is not None:
4353 def _encoded_values(self):
4355 for name, spec in self.specs.items():
4356 value = self._value.get(name)
4360 raise ObjNotReady(name)
4361 raws.append(value.encode())
4365 v = b"".join(self._encoded_values())
4366 return b"".join((self.tag, len_encode(len(v)), v))
4368 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4370 t, tlen, lv = tag_strip(tlv)
4371 except DecodeError as err:
4372 raise err.__class__(
4374 klass=self.__class__,
4375 decode_path=decode_path,
4380 klass=self.__class__,
4381 decode_path=decode_path,
4388 l, llen, v = len_decode(lv)
4389 except LenIndefForm as err:
4390 if not ctx.get("bered", False):
4391 raise err.__class__(
4393 klass=self.__class__,
4394 decode_path=decode_path,
4397 l, llen, v = 0, 1, lv[1:]
4399 except DecodeError as err:
4400 raise err.__class__(
4402 klass=self.__class__,
4403 decode_path=decode_path,
4407 raise NotEnoughData(
4408 "encoded length is longer than data",
4409 klass=self.__class__,
4410 decode_path=decode_path,
4414 v, tail = v[:l], v[l:]
4416 sub_offset = offset + tlen + llen
4418 for name, spec in self.specs.items():
4419 if spec.optional and (
4420 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4424 sub_decode_path = decode_path + (name,)
4426 value, v_tail = spec.decode(
4430 decode_path=sub_decode_path,
4438 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4439 if defined is not None:
4440 defined_by, defined_spec = defined
4441 if issubclass(value.__class__, SequenceOf):
4442 for i, _value in enumerate(value):
4443 sub_sub_decode_path = sub_decode_path + (
4445 DecodePathDefBy(defined_by),
4447 defined_value, defined_tail = defined_spec.decode(
4448 memoryview(bytes(_value)),
4450 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4451 if value.expled else (value.tlen + value.llen)
4454 decode_path=sub_sub_decode_path,
4457 if len(defined_tail) > 0:
4460 klass=self.__class__,
4461 decode_path=sub_sub_decode_path,
4464 _value.defined = (defined_by, defined_value)
4466 defined_value, defined_tail = defined_spec.decode(
4467 memoryview(bytes(value)),
4469 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4470 if value.expled else (value.tlen + value.llen)
4473 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4476 if len(defined_tail) > 0:
4479 klass=self.__class__,
4480 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4483 value.defined = (defined_by, defined_value)
4485 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4487 sub_offset += value_len
4489 if spec.default is not None and value == spec.default:
4490 if ctx.get("strict_default_existence", False):
4492 "DEFAULT value met",
4493 klass=self.__class__,
4494 decode_path=sub_decode_path,
4499 values[name] = value
4501 spec_defines = getattr(spec, "defines", ())
4502 if len(spec_defines) == 0:
4503 defines_by_path = ctx.get("defines_by_path", ())
4504 if len(defines_by_path) > 0:
4505 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4506 if spec_defines is not None and len(spec_defines) > 0:
4507 for rel_path, schema in spec_defines:
4508 defined = schema.get(value, None)
4509 if defined is not None:
4510 ctx.setdefault("defines", []).append((
4511 abs_decode_path(sub_decode_path[:-1], rel_path),
4515 if v[:EOC_LEN].tobytes() != EOC:
4518 klass=self.__class__,
4519 decode_path=decode_path,
4527 klass=self.__class__,
4528 decode_path=decode_path,
4531 obj = self.__class__(
4535 default=self.default,
4536 optional=self.optional,
4537 _decoded=(offset, llen, vlen),
4540 obj.lenindef = lenindef
4544 value = pp_console_row(next(self.pps()))
4546 for name in self.specs:
4547 _value = self._value.get(name)
4550 cols.append(repr(_value))
4551 return "%s[%s]" % (value, ", ".join(cols))
4553 def pps(self, decode_path=()):
4555 asn1_type_name=self.asn1_type_name,
4556 obj_name=self.__class__.__name__,
4557 decode_path=decode_path,
4558 optional=self.optional,
4559 default=self == self.default,
4560 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4561 expl=None if self._expl is None else tag_decode(self._expl),
4566 expl_offset=self.expl_offset if self.expled else None,
4567 expl_tlen=self.expl_tlen if self.expled else None,
4568 expl_llen=self.expl_llen if self.expled else None,
4569 expl_vlen=self.expl_vlen if self.expled else None,
4570 expl_lenindef=self.expl_lenindef,
4571 lenindef=self.lenindef,
4573 for name in self.specs:
4574 value = self._value.get(name)
4577 yield value.pps(decode_path=decode_path + (name,))
4580 class Set(Sequence):
4581 """``SET`` structure type
4583 Its usage is identical to :py:class:`pyderasn.Sequence`.
4586 tag_default = tag_encode(form=TagFormConstructed, num=17)
4587 asn1_type_name = "SET"
4590 raws = self._encoded_values()
4593 return b"".join((self.tag, len_encode(len(v)), v))
4595 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4597 t, tlen, lv = tag_strip(tlv)
4598 except DecodeError as err:
4599 raise err.__class__(
4601 klass=self.__class__,
4602 decode_path=decode_path,
4607 klass=self.__class__,
4608 decode_path=decode_path,
4615 l, llen, v = len_decode(lv)
4616 except LenIndefForm as err:
4617 if not ctx.get("bered", False):
4618 raise err.__class__(
4620 klass=self.__class__,
4621 decode_path=decode_path,
4624 l, llen, v = 0, 1, lv[1:]
4626 except DecodeError as err:
4627 raise err.__class__(
4629 klass=self.__class__,
4630 decode_path=decode_path,
4634 raise NotEnoughData(
4635 "encoded length is longer than data",
4636 klass=self.__class__,
4640 v, tail = v[:l], v[l:]
4642 sub_offset = offset + tlen + llen
4644 specs_items = self.specs.items
4646 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4648 for name, spec in specs_items():
4649 sub_decode_path = decode_path + (name,)
4655 decode_path=sub_decode_path,
4664 klass=self.__class__,
4665 decode_path=decode_path,
4668 value, v_tail = spec.decode(
4672 decode_path=sub_decode_path,
4675 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4676 sub_offset += value_len
4679 if spec.default is None or value != spec.default: # pragma: no cover
4680 # SeqMixing.test_encoded_default_accepted covers that place
4681 values[name] = value
4682 obj = self.__class__(
4686 default=self.default,
4687 optional=self.optional,
4688 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4693 msg="not all values are ready",
4694 klass=self.__class__,
4695 decode_path=decode_path,
4698 obj.lenindef = lenindef
4699 return obj, (v[EOC_LEN:] if lenindef else tail)
4702 class SequenceOf(Obj):
4703 """``SEQUENCE OF`` sequence type
4705 For that kind of type you must specify the object it will carry on
4706 (bounds are for example here, not required)::
4708 class Ints(SequenceOf):
4713 >>> ints.append(Integer(123))
4714 >>> ints.append(Integer(234))
4716 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4717 >>> [int(i) for i in ints]
4719 >>> ints.append(Integer(345))
4720 Traceback (most recent call last):
4721 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4724 >>> ints[1] = Integer(345)
4726 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4728 Also you can initialize sequence with preinitialized values:
4730 >>> ints = Ints([Integer(123), Integer(234)])
4732 __slots__ = ("spec", "_bound_min", "_bound_max")
4733 tag_default = tag_encode(form=TagFormConstructed, num=16)
4734 asn1_type_name = "SEQUENCE OF"
4747 super(SequenceOf, self).__init__(
4755 schema = getattr(self, "schema", None)
4757 raise ValueError("schema must be specified")
4759 self._bound_min, self._bound_max = getattr(
4763 ) if bounds is None else bounds
4765 if value is not None:
4766 self._value = self._value_sanitize(value)
4767 if default is not None:
4768 default_value = self._value_sanitize(default)
4769 default_obj = self.__class__(
4774 default_obj._value = default_value
4775 self.default = default_obj
4777 self._value = default_obj.copy()._value
4779 def _value_sanitize(self, value):
4780 if issubclass(value.__class__, SequenceOf):
4781 value = value._value
4782 elif hasattr(value, "__iter__"):
4785 raise InvalidValueType((self.__class__, iter))
4786 if not self._bound_min <= len(value) <= self._bound_max:
4787 raise BoundsError(self._bound_min, len(value), self._bound_max)
4789 if not isinstance(v, self.spec.__class__):
4790 raise InvalidValueType((self.spec.__class__,))
4795 return all(v.ready for v in self._value)
4798 obj = self.__class__(schema=self.spec)
4799 obj._bound_min = self._bound_min
4800 obj._bound_max = self._bound_max
4802 obj._expl = self._expl
4803 obj.default = self.default
4804 obj.optional = self.optional
4805 obj.offset = self.offset
4806 obj.llen = self.llen
4807 obj.vlen = self.vlen
4808 obj._value = [v.copy() for v in self._value]
4811 def __eq__(self, their):
4812 if isinstance(their, self.__class__):
4814 self.spec == their.spec and
4815 self.tag == their.tag and
4816 self._expl == their._expl and
4817 self._value == their._value
4819 if hasattr(their, "__iter__"):
4820 return self._value == list(their)
4832 return self.__class__(
4836 (self._bound_min, self._bound_max)
4837 if bounds is None else bounds
4839 impl=self.tag if impl is None else impl,
4840 expl=self._expl if expl is None else expl,
4841 default=self.default if default is None else default,
4842 optional=self.optional if optional is None else optional,
4845 def __contains__(self, key):
4846 return key in self._value
4848 def append(self, value):
4849 if not isinstance(value, self.spec.__class__):
4850 raise InvalidValueType((self.spec.__class__,))
4851 if len(self._value) + 1 > self._bound_max:
4854 len(self._value) + 1,
4857 self._value.append(value)
4860 self._assert_ready()
4861 return iter(self._value)
4864 self._assert_ready()
4865 return len(self._value)
4867 def __setitem__(self, key, value):
4868 if not isinstance(value, self.spec.__class__):
4869 raise InvalidValueType((self.spec.__class__,))
4870 self._value[key] = self.spec(value=value)
4872 def __getitem__(self, key):
4873 return self._value[key]
4875 def _encoded_values(self):
4876 return [v.encode() for v in self._value]
4879 v = b"".join(self._encoded_values())
4880 return b"".join((self.tag, len_encode(len(v)), v))
4882 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4884 t, tlen, lv = tag_strip(tlv)
4885 except DecodeError as err:
4886 raise err.__class__(
4888 klass=self.__class__,
4889 decode_path=decode_path,
4894 klass=self.__class__,
4895 decode_path=decode_path,
4902 l, llen, v = len_decode(lv)
4903 except LenIndefForm as err:
4904 if not ctx.get("bered", False):
4905 raise err.__class__(
4907 klass=self.__class__,
4908 decode_path=decode_path,
4911 l, llen, v = 0, 1, lv[1:]
4913 except DecodeError as err:
4914 raise err.__class__(
4916 klass=self.__class__,
4917 decode_path=decode_path,
4921 raise NotEnoughData(
4922 "encoded length is longer than data",
4923 klass=self.__class__,
4924 decode_path=decode_path,
4928 v, tail = v[:l], v[l:]
4930 sub_offset = offset + tlen + llen
4934 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4936 value, v_tail = spec.decode(
4940 decode_path=decode_path + (str(len(_value)),),
4943 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4944 sub_offset += value_len
4947 _value.append(value)
4948 obj = self.__class__(
4951 bounds=(self._bound_min, self._bound_max),
4954 default=self.default,
4955 optional=self.optional,
4956 _decoded=(offset, llen, vlen),
4958 obj.lenindef = lenindef
4959 return obj, (v[EOC_LEN:] if lenindef else tail)
4963 pp_console_row(next(self.pps())),
4964 ", ".join(repr(v) for v in self._value),
4967 def pps(self, decode_path=()):
4969 asn1_type_name=self.asn1_type_name,
4970 obj_name=self.__class__.__name__,
4971 decode_path=decode_path,
4972 optional=self.optional,
4973 default=self == self.default,
4974 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4975 expl=None if self._expl is None else tag_decode(self._expl),
4980 expl_offset=self.expl_offset if self.expled else None,
4981 expl_tlen=self.expl_tlen if self.expled else None,
4982 expl_llen=self.expl_llen if self.expled else None,
4983 expl_vlen=self.expl_vlen if self.expled else None,
4984 expl_lenindef=self.expl_lenindef,
4985 lenindef=self.lenindef,
4987 for i, value in enumerate(self._value):
4988 yield value.pps(decode_path=decode_path + (str(i),))
4991 class SetOf(SequenceOf):
4992 """``SET OF`` sequence type
4994 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4997 tag_default = tag_encode(form=TagFormConstructed, num=17)
4998 asn1_type_name = "SET OF"
5001 raws = self._encoded_values()
5004 return b"".join((self.tag, len_encode(len(v)), v))
5007 def obj_by_path(pypath): # pragma: no cover
5008 """Import object specified as string Python path
5010 Modules must be separated from classes/functions with ``:``.
5012 >>> obj_by_path("foo.bar:Baz")
5013 <class 'foo.bar.Baz'>
5014 >>> obj_by_path("foo.bar:Baz.boo")
5015 <classmethod 'foo.bar.Baz.boo'>
5017 mod, objs = pypath.rsplit(":", 1)
5018 from importlib import import_module
5019 obj = import_module(mod)
5020 for obj_name in objs.split("."):
5021 obj = getattr(obj, obj_name)
5025 def generic_decoder(): # pragma: no cover
5026 # All of this below is a big hack with self references
5027 choice = PrimitiveTypes()
5028 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5029 choice.specs["SetOf"] = SetOf(schema=choice)
5031 choice.specs["SequenceOf%d" % i] = SequenceOf(
5035 choice.specs["Any"] = Any()
5037 # Class name equals to type name, to omit it from output
5038 class SEQUENCEOF(SequenceOf):
5042 def pprint_any(obj, oids=None, with_colours=False):
5043 def _pprint_pps(pps):
5045 if hasattr(pp, "_fields"):
5046 if pp.asn1_type_name == Choice.asn1_type_name:
5048 pp_kwargs = pp._asdict()
5049 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5050 pp = _pp(**pp_kwargs)
5051 yield pp_console_row(
5056 with_colours=with_colours,
5058 for row in pp_console_blob(pp):
5061 for row in _pprint_pps(pp):
5063 return "\n".join(_pprint_pps(obj.pps()))
5064 return SEQUENCEOF(), pprint_any
5067 def main(): # pragma: no cover
5069 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5070 parser.add_argument(
5074 help="Skip that number of bytes from the beginning",
5076 parser.add_argument(
5078 help="Python path to dictionary with OIDs",
5080 parser.add_argument(
5082 help="Python path to schema definition to use",
5084 parser.add_argument(
5085 "--defines-by-path",
5086 help="Python path to decoder's defines_by_path",
5088 parser.add_argument(
5090 action='store_true',
5091 help="Disallow BER encoding",
5093 parser.add_argument(
5095 type=argparse.FileType("rb"),
5096 help="Path to DER file you want to decode",
5098 args = parser.parse_args()
5099 args.DERFile.seek(args.skip)
5100 der = memoryview(args.DERFile.read())
5101 args.DERFile.close()
5102 oids = obj_by_path(args.oids) if args.oids else {}
5104 schema = obj_by_path(args.schema)
5105 from functools import partial
5106 pprinter = partial(pprint, big_blobs=True)
5108 schema, pprinter = generic_decoder()
5109 ctx = {"bered": not args.nobered}
5110 if args.defines_by_path is not None:
5111 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5112 obj, tail = schema().decode(der, ctx=ctx)
5116 with_colours=True if environ.get("NO_COLOR") is None else False,
5119 print("\nTrailing data: %s" % hexenc(tail))
5122 if __name__ == "__main__":