3 # PyDERASN -- Python ASN.1 DER 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 codec with abstract structures
21 This library allows you to marshal and unmarshal various structures in
22 ASN.1 DER format, like this:
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, then :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:`defines_by_path <defines_by_path_ctx>`
210 * :ref:`strict_default_existence <strict_default_existence_ctx>`
217 All objects have ``pps()`` method, that is a generator of
218 :py:class:`pyderasn.PP` namedtuple, holding various raw information
219 about the object. If ``pps`` is called on sequences, then all underlying
220 ``PP`` will be yielded.
222 You can use :py:func:`pyderasn.pp_console_row` function, converting
223 those ``PP`` to human readable string. Actually exactly it is used for
224 all object ``repr``. But it is easy to write custom formatters.
226 >>> from pyderasn import pprint
227 >>> encoded = Integer(-12345).encode()
228 >>> obj, tail = Integer().decode(encoded)
229 >>> print(pprint(obj))
230 0 [1,1, 2] INTEGER -12345
237 ASN.1 structures often have ANY and OCTET STRING fields, that are
238 DEFINED BY some previously met ObjectIdentifier. This library provides
239 ability to specify mapping between some OID and field that must be
240 decoded with specific specification.
245 :py:class:`pyderasn.ObjectIdentifier` field inside
246 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
247 necessary for decoding structures. For example, CMS (:rfc:`5652`)
250 class ContentInfo(Sequence):
252 ("contentType", ContentType(defines=((("content",), {
253 id_digestedData: DigestedData(),
254 id_signedData: SignedData(),
256 ("content", Any(expl=tag_ctxc(0))),
259 ``contentType`` field tells that it defines that ``content`` must be
260 decoded with ``SignedData`` specification, if ``contentType`` equals to
261 ``id-signedData``. The same applies to ``DigestedData``. If
262 ``contentType`` contains unknown OID, then no automatic decoding is
265 You can specify multiple fields, that will be autodecoded -- that is why
266 ``defines`` kwarg is a sequence. You can specify defined field
267 relatively or absolutely to current decode path. For example ``defines``
268 for AlgorithmIdentifier of X.509's
269 ``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
273 id_ecPublicKey: ECParameters(),
274 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
276 (("..", "subjectPublicKey"), {
277 id_rsaEncryption: RSAPublicKey(),
278 id_GostR3410_2001: OctetString(),
282 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
283 autodecode its parameters inside SPKI's algorithm and its public key
286 Following types can be automatically decoded (DEFINED BY):
288 * :py:class:`pyderasn.Any`
289 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
290 * :py:class:`pyderasn.OctetString`
291 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
292 ``Any``/``BitString``/``OctetString``-s
294 When any of those fields is automatically decoded, then ``.defined``
295 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
296 was defined, ``value`` contains corresponding decoded value. For example
297 above, ``content_info["content"].defined == (id_signedData,
300 .. _defines_by_path_ctx:
302 defines_by_path context option
303 ______________________________
305 Sometimes you either can not or do not want to explicitly set *defines*
306 in the scheme. You can dynamically apply those definitions when calling
307 ``.decode()`` method.
309 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
310 value must be sequence of following tuples::
312 (decode_path, defines)
314 where ``decode_path`` is a tuple holding so-called decode path to the
315 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
316 ``defines``, holding exactly the same value as accepted in its keyword
319 For example, again for CMS, you want to automatically decode
320 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
321 structures it may hold. Also, automatically decode ``controlSequence``
324 content_info, tail = ContentInfo().decode(data, defines_by_path=(
327 ((("content",), {id_signedData: SignedData()}),),
332 DecodePathDefBy(id_signedData),
337 id_cct_PKIData: PKIData(),
338 id_cct_PKIResponse: PKIResponse(),
344 DecodePathDefBy(id_signedData),
347 DecodePathDefBy(id_cct_PKIResponse),
353 id_cmc_recipientNonce: RecipientNonce(),
354 id_cmc_senderNonce: SenderNonce(),
355 id_cmc_statusInfoV2: CMCStatusInfoV2(),
356 id_cmc_transactionId: TransactionId(),
361 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
362 First function is useful for path construction when some automatic
363 decoding is already done. ``any`` means literally any value it meet --
364 useful for SEQUENCE/SET OF-s.
371 .. autoclass:: pyderasn.Boolean
376 .. autoclass:: pyderasn.Integer
381 .. autoclass:: pyderasn.BitString
386 .. autoclass:: pyderasn.OctetString
391 .. autoclass:: pyderasn.Null
396 .. autoclass:: pyderasn.ObjectIdentifier
401 .. autoclass:: pyderasn.Enumerated
405 .. autoclass:: pyderasn.CommonString
409 .. autoclass:: pyderasn.UTCTime
410 :members: __init__, todatetime
414 .. autoclass:: pyderasn.GeneralizedTime
421 .. autoclass:: pyderasn.Choice
426 .. autoclass:: PrimitiveTypes
430 .. autoclass:: pyderasn.Any
438 .. autoclass:: pyderasn.Sequence
443 .. autoclass:: pyderasn.Set
448 .. autoclass:: pyderasn.SequenceOf
453 .. autoclass:: pyderasn.SetOf
459 .. autofunction:: pyderasn.abs_decode_path
460 .. autofunction:: pyderasn.hexenc
461 .. autofunction:: pyderasn.hexdec
462 .. autofunction:: pyderasn.tag_encode
463 .. autofunction:: pyderasn.tag_decode
464 .. autofunction:: pyderasn.tag_ctxp
465 .. autofunction:: pyderasn.tag_ctxc
466 .. autoclass:: pyderasn.Obj
469 from codecs import getdecoder
470 from codecs import getencoder
471 from collections import namedtuple
472 from collections import OrderedDict
473 from datetime import datetime
474 from math import ceil
475 from os import environ
476 from string import digits
478 from six import add_metaclass
479 from six import binary_type
480 from six import byte2int
481 from six import indexbytes
482 from six import int2byte
483 from six import integer_types
484 from six import iterbytes
486 from six import string_types
487 from six import text_type
488 from six.moves import xrange as six_xrange
492 from termcolor import colored
494 def colored(what, *args):
537 "TagClassApplication",
541 "TagFormConstructed",
552 TagClassUniversal = 0
553 TagClassApplication = 1 << 6
554 TagClassContext = 1 << 7
555 TagClassPrivate = 1 << 6 | 1 << 7
557 TagFormConstructed = 1 << 5
560 TagClassApplication: "APPLICATION ",
561 TagClassPrivate: "PRIVATE ",
562 TagClassUniversal: "UNIV ",
566 ########################################################################
568 ########################################################################
570 class DecodeError(Exception):
571 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
573 :param str msg: reason of decode failing
574 :param klass: optional exact DecodeError inherited class (like
575 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
576 :py:exc:`InvalidLength`)
577 :param decode_path: tuple of strings. It contains human
578 readable names of the fields through which
579 decoding process has passed
580 :param int offset: binary offset where failure happened
582 super(DecodeError, self).__init__()
585 self.decode_path = decode_path
591 "" if self.klass is None else self.klass.__name__,
593 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
594 if len(self.decode_path) > 0 else ""
596 ("(at %d)" % self.offset) if self.offset > 0 else "",
602 return "%s(%s)" % (self.__class__.__name__, self)
605 class NotEnoughData(DecodeError):
609 class TagMismatch(DecodeError):
613 class InvalidLength(DecodeError):
617 class InvalidOID(DecodeError):
621 class ObjUnknown(ValueError):
622 def __init__(self, name):
623 super(ObjUnknown, self).__init__()
627 return "object is unknown: %s" % self.name
630 return "%s(%s)" % (self.__class__.__name__, self)
633 class ObjNotReady(ValueError):
634 def __init__(self, name):
635 super(ObjNotReady, self).__init__()
639 return "object is not ready: %s" % self.name
642 return "%s(%s)" % (self.__class__.__name__, self)
645 class InvalidValueType(ValueError):
646 def __init__(self, expected_types):
647 super(InvalidValueType, self).__init__()
648 self.expected_types = expected_types
651 return "invalid value type, expected: %s" % ", ".join(
652 [repr(t) for t in self.expected_types]
656 return "%s(%s)" % (self.__class__.__name__, self)
659 class BoundsError(ValueError):
660 def __init__(self, bound_min, value, bound_max):
661 super(BoundsError, self).__init__()
662 self.bound_min = bound_min
664 self.bound_max = bound_max
667 return "unsatisfied bounds: %s <= %s <= %s" % (
674 return "%s(%s)" % (self.__class__.__name__, self)
677 ########################################################################
679 ########################################################################
681 _hexdecoder = getdecoder("hex")
682 _hexencoder = getencoder("hex")
686 """Binary data to hexadecimal string convert
688 return _hexdecoder(data)[0]
692 """Hexadecimal string to binary data convert
694 return _hexencoder(data)[0].decode("ascii")
697 def int_bytes_len(num, byte_len=8):
700 return int(ceil(float(num.bit_length()) / byte_len))
703 def zero_ended_encode(num):
704 octets = bytearray(int_bytes_len(num, 7))
706 octets[i] = num & 0x7F
710 octets[i] = 0x80 | (num & 0x7F)
716 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
717 """Encode tag to binary form
719 :param int num: tag's number
720 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
721 :py:data:`pyderasn.TagClassContext`,
722 :py:data:`pyderasn.TagClassApplication`,
723 :py:data:`pyderasn.TagClassPrivate`)
724 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
725 :py:data:`pyderasn.TagFormConstructed`)
729 return int2byte(klass | form | num)
730 # [XX|X|11111][1.......][1.......] ... [0.......]
731 return int2byte(klass | form | 31) + zero_ended_encode(num)
735 """Decode tag from binary form
739 No validation is performed, assuming that it has already passed.
741 It returns tuple with three integers, as
742 :py:func:`pyderasn.tag_encode` accepts.
744 first_octet = byte2int(tag)
745 klass = first_octet & 0xC0
746 form = first_octet & 0x20
747 if first_octet & 0x1F < 0x1F:
748 return (klass, form, first_octet & 0x1F)
750 for octet in iterbytes(tag[1:]):
753 return (klass, form, num)
757 """Create CONTEXT PRIMITIVE tag
759 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
763 """Create CONTEXT CONSTRUCTED tag
765 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
769 """Take off tag from the data
771 :returns: (encoded tag, tag length, remaining data)
774 raise NotEnoughData("no data at all")
775 if byte2int(data) & 0x1F < 31:
776 return data[:1], 1, data[1:]
781 raise DecodeError("unfinished tag")
782 if indexbytes(data, i) & 0x80 == 0:
785 return data[:i], i, data[i:]
791 octets = bytearray(int_bytes_len(l) + 1)
792 octets[0] = 0x80 | (len(octets) - 1)
793 for i in six_xrange(len(octets) - 1, 0, -1):
799 def len_decode(data):
801 raise NotEnoughData("no data at all")
802 first_octet = byte2int(data)
803 if first_octet & 0x80 == 0:
804 return first_octet, 1, data[1:]
805 octets_num = first_octet & 0x7F
806 if octets_num + 1 > len(data):
807 raise NotEnoughData("encoded length is longer than data")
809 raise DecodeError("long form instead of short one")
810 if byte2int(data[1:]) == 0:
811 raise DecodeError("leading zeros")
813 for v in iterbytes(data[1:1 + octets_num]):
816 raise DecodeError("long form instead of short one")
817 return l, 1 + octets_num, data[1 + octets_num:]
820 ########################################################################
822 ########################################################################
824 class AutoAddSlots(type):
825 def __new__(mcs, name, bases, _dict):
826 _dict["__slots__"] = _dict.get("__slots__", ())
827 return type.__new__(mcs, name, bases, _dict)
830 @add_metaclass(AutoAddSlots)
832 """Common ASN.1 object class
834 All ASN.1 types are inherited from it. It has metaclass that
835 automatically adds ``__slots__`` to all inherited classes.
856 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
857 self._expl = getattr(self, "expl", None) if expl is None else expl
858 if self.tag != self.tag_default and self._expl is not None:
860 "implicit and explicit tags can not be set simultaneously"
862 if default is not None:
864 self.optional = optional
865 self.offset, self.llen, self.vlen = _decoded
869 def ready(self): # pragma: no cover
870 """Is object ready to be encoded?
872 raise NotImplementedError()
874 def _assert_ready(self):
876 raise ObjNotReady(self.__class__.__name__)
880 """Is object decoded?
882 return (self.llen + self.vlen) > 0
884 def copy(self): # pragma: no cover
885 """Make a copy of object, safe to be mutated
887 raise NotImplementedError()
895 return self.tlen + self.llen + self.vlen
897 def __str__(self): # pragma: no cover
898 return self.__bytes__() if PY2 else self.__unicode__()
900 def __ne__(self, their):
901 return not(self == their)
903 def __gt__(self, their): # pragma: no cover
904 return not(self < their)
906 def __le__(self, their): # pragma: no cover
907 return (self == their) or (self < their)
909 def __ge__(self, their): # pragma: no cover
910 return (self == their) or (self > their)
912 def _encode(self): # pragma: no cover
913 raise NotImplementedError()
915 def _decode(self, tlv, offset, decode_path, ctx): # pragma: no cover
916 raise NotImplementedError()
920 if self._expl is None:
922 return b"".join((self._expl, len_encode(len(raw)), raw))
924 def decode(self, data, offset=0, leavemm=False, decode_path=(), ctx=None):
927 :param data: either binary or memoryview
928 :param int offset: initial data's offset
929 :param bool leavemm: do we need to leave memoryview of remaining
930 data as is, or convert it to bytes otherwise
931 :param ctx: optional :ref:`context <ctx>` governing decoding process.
932 :returns: (Obj, remaining data)
936 tlv = memoryview(data)
937 if self._expl is None:
938 obj, tail = self._decode(
941 decode_path=decode_path,
946 t, tlen, lv = tag_strip(tlv)
947 except DecodeError as err:
950 klass=self.__class__,
951 decode_path=decode_path,
956 klass=self.__class__,
957 decode_path=decode_path,
961 l, llen, v = len_decode(lv)
962 except DecodeError as err:
965 klass=self.__class__,
966 decode_path=decode_path,
971 "encoded length is longer than data",
972 klass=self.__class__,
973 decode_path=decode_path,
976 obj, tail = self._decode(
978 offset=offset + tlen + llen,
979 decode_path=decode_path,
982 return obj, (tail if leavemm else tail.tobytes())
986 return self._expl is not None
994 return len(self._expl)
998 return len(len_encode(self.tlvlen))
1001 def expl_offset(self):
1002 return self.offset - self.expl_tlen - self.expl_llen
1005 def expl_vlen(self):
1009 def expl_tlvlen(self):
1010 return self.expl_tlen + self.expl_llen + self.expl_vlen
1013 class DecodePathDefBy(object):
1014 """DEFINED BY representation inside decode path
1016 __slots__ = ("defined_by",)
1018 def __init__(self, defined_by):
1019 self.defined_by = defined_by
1021 def __ne__(self, their):
1022 return not(self == their)
1024 def __eq__(self, their):
1025 if not isinstance(their, self.__class__):
1027 return self.defined_by == their.defined_by
1030 return "DEFINED BY " + str(self.defined_by)
1033 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1036 ########################################################################
1038 ########################################################################
1040 PP = namedtuple("PP", (
1062 asn1_type_name="unknown",
1101 def _colorize(what, colour, with_colours, attrs=("bold",)):
1102 return colored(what, colour, attrs=attrs) if with_colours else what
1117 " " if pp.expl_offset is None else
1118 ("-%d" % (pp.offset - pp.expl_offset))
1121 cols.append(_colorize(col, "red", with_colours, ()))
1122 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1123 cols.append(_colorize(col, "green", with_colours, ()))
1124 if len(pp.decode_path) > 0:
1125 cols.append(" ." * (len(pp.decode_path)))
1126 ent = pp.decode_path[-1]
1127 if isinstance(ent, DecodePathDefBy):
1128 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1129 value = str(ent.defined_by)
1131 oids is not None and
1132 ent.defined_by.asn1_type_name ==
1133 ObjectIdentifier.asn1_type_name and
1136 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1138 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1140 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1141 if pp.expl is not None:
1142 klass, _, num = pp.expl
1143 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1144 cols.append(_colorize(col, "blue", with_colours))
1145 if pp.impl is not None:
1146 klass, _, num = pp.impl
1147 col = "[%s%d]" % (TagClassReprs[klass], num)
1148 cols.append(_colorize(col, "blue", with_colours))
1149 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1150 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1151 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1152 if pp.value is not None:
1154 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1156 oids is not None and
1157 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1160 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1162 if isinstance(pp.blob, binary_type):
1163 cols.append(hexenc(pp.blob))
1164 elif isinstance(pp.blob, tuple):
1165 cols.append(", ".join(pp.blob))
1167 cols.append(_colorize("OPTIONAL", "red", with_colours))
1169 cols.append(_colorize("DEFAULT", "red", with_colours))
1170 return " ".join(cols)
1173 def pp_console_blob(pp):
1174 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
1175 if len(pp.decode_path) > 0:
1176 cols.append(" ." * (len(pp.decode_path) + 1))
1177 if isinstance(pp.blob, binary_type):
1178 blob = hexenc(pp.blob).upper()
1179 for i in range(0, len(blob), 32):
1180 chunk = blob[i:i + 32]
1181 yield " ".join(cols + [":".join(
1182 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1184 elif isinstance(pp.blob, tuple):
1185 yield " ".join(cols + [", ".join(pp.blob)])
1188 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1189 """Pretty print object
1191 :param Obj obj: object you want to pretty print
1192 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1193 from it is met, then its humand readable form is printed
1194 :param big_blobs: if large binary objects are met (like OctetString
1195 values), do we need to print them too, on separate
1197 :param with_colours: colourize output, if ``termcolor`` library
1200 def _pprint_pps(pps):
1202 if hasattr(pp, "_fields"):
1204 yield pp_console_row(
1209 with_colours=with_colours,
1211 for row in pp_console_blob(pp):
1214 yield pp_console_row(
1219 with_colours=with_colours,
1222 for row in _pprint_pps(pp):
1224 return "\n".join(_pprint_pps(obj.pps()))
1227 ########################################################################
1228 # ASN.1 primitive types
1229 ########################################################################
1232 """``BOOLEAN`` boolean type
1234 >>> b = Boolean(True)
1236 >>> b == Boolean(True)
1242 tag_default = tag_encode(1)
1243 asn1_type_name = "BOOLEAN"
1255 :param value: set the value. Either boolean type, or
1256 :py:class:`pyderasn.Boolean` object
1257 :param bytes impl: override default tag with ``IMPLICIT`` one
1258 :param bytes expl: override default tag with ``EXPLICIT`` one
1259 :param default: set default value. Type same as in ``value``
1260 :param bool optional: is object ``OPTIONAL`` in sequence
1262 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1263 self._value = None if value is None else self._value_sanitize(value)
1264 if default is not None:
1265 default = self._value_sanitize(default)
1266 self.default = self.__class__(
1272 self._value = default
1274 def _value_sanitize(self, value):
1275 if issubclass(value.__class__, Boolean):
1277 if isinstance(value, bool):
1279 raise InvalidValueType((self.__class__, bool))
1283 return self._value is not None
1286 obj = self.__class__()
1287 obj._value = self._value
1289 obj._expl = self._expl
1290 obj.default = self.default
1291 obj.optional = self.optional
1292 obj.offset = self.offset
1293 obj.llen = self.llen
1294 obj.vlen = self.vlen
1297 def __nonzero__(self):
1298 self._assert_ready()
1302 self._assert_ready()
1305 def __eq__(self, their):
1306 if isinstance(their, bool):
1307 return self._value == their
1308 if not issubclass(their.__class__, Boolean):
1311 self._value == their._value and
1312 self.tag == their.tag and
1313 self._expl == their._expl
1324 return self.__class__(
1326 impl=self.tag if impl is None else impl,
1327 expl=self._expl if expl is None else expl,
1328 default=self.default if default is None else default,
1329 optional=self.optional if optional is None else optional,
1333 self._assert_ready()
1337 (b"\xFF" if self._value else b"\x00"),
1340 def _decode(self, tlv, offset, decode_path, ctx):
1342 t, _, lv = tag_strip(tlv)
1343 except DecodeError as err:
1344 raise err.__class__(
1346 klass=self.__class__,
1347 decode_path=decode_path,
1352 klass=self.__class__,
1353 decode_path=decode_path,
1357 l, _, v = len_decode(lv)
1358 except DecodeError as err:
1359 raise err.__class__(
1361 klass=self.__class__,
1362 decode_path=decode_path,
1366 raise InvalidLength(
1367 "Boolean's length must be equal to 1",
1368 klass=self.__class__,
1369 decode_path=decode_path,
1373 raise NotEnoughData(
1374 "encoded length is longer than data",
1375 klass=self.__class__,
1376 decode_path=decode_path,
1379 first_octet = byte2int(v)
1380 if first_octet == 0:
1382 elif first_octet == 0xFF:
1386 "unacceptable Boolean value",
1387 klass=self.__class__,
1388 decode_path=decode_path,
1391 obj = self.__class__(
1395 default=self.default,
1396 optional=self.optional,
1397 _decoded=(offset, 1, 1),
1402 return pp_console_row(next(self.pps()))
1404 def pps(self, decode_path=()):
1406 asn1_type_name=self.asn1_type_name,
1407 obj_name=self.__class__.__name__,
1408 decode_path=decode_path,
1409 value=str(self._value) if self.ready else None,
1410 optional=self.optional,
1411 default=self == self.default,
1412 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1413 expl=None if self._expl is None else tag_decode(self._expl),
1418 expl_offset=self.expl_offset if self.expled else None,
1419 expl_tlen=self.expl_tlen if self.expled else None,
1420 expl_llen=self.expl_llen if self.expled else None,
1421 expl_vlen=self.expl_vlen if self.expled else None,
1426 """``INTEGER`` integer type
1428 >>> b = Integer(-123)
1430 >>> b == Integer(-123)
1435 >>> Integer(2, bounds=(1, 3))
1437 >>> Integer(5, bounds=(1, 3))
1438 Traceback (most recent call last):
1439 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1443 class Version(Integer):
1450 >>> v = Version("v1")
1457 {'v3': 2, 'v1': 0, 'v2': 1}
1459 __slots__ = ("specs", "_bound_min", "_bound_max")
1460 tag_default = tag_encode(2)
1461 asn1_type_name = "INTEGER"
1475 :param value: set the value. Either integer type, named value
1476 (if ``schema`` is specified in the class), or
1477 :py:class:`pyderasn.Integer` object
1478 :param bounds: set ``(MIN, MAX)`` value constraint.
1479 (-inf, +inf) by default
1480 :param bytes impl: override default tag with ``IMPLICIT`` one
1481 :param bytes expl: override default tag with ``EXPLICIT`` one
1482 :param default: set default value. Type same as in ``value``
1483 :param bool optional: is object ``OPTIONAL`` in sequence
1485 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1487 specs = getattr(self, "schema", {}) if _specs is None else _specs
1488 self.specs = specs if isinstance(specs, dict) else dict(specs)
1489 self._bound_min, self._bound_max = getattr(
1492 (float("-inf"), float("+inf")),
1493 ) if bounds is None else bounds
1494 if value is not None:
1495 self._value = self._value_sanitize(value)
1496 if default is not None:
1497 default = self._value_sanitize(default)
1498 self.default = self.__class__(
1504 if self._value is None:
1505 self._value = default
1507 def _value_sanitize(self, value):
1508 if issubclass(value.__class__, Integer):
1509 value = value._value
1510 elif isinstance(value, integer_types):
1512 elif isinstance(value, str):
1513 value = self.specs.get(value)
1515 raise ObjUnknown("integer value: %s" % value)
1517 raise InvalidValueType((self.__class__, int, str))
1518 if not self._bound_min <= value <= self._bound_max:
1519 raise BoundsError(self._bound_min, value, self._bound_max)
1524 return self._value is not None
1527 obj = self.__class__(_specs=self.specs)
1528 obj._value = self._value
1529 obj._bound_min = self._bound_min
1530 obj._bound_max = self._bound_max
1532 obj._expl = self._expl
1533 obj.default = self.default
1534 obj.optional = self.optional
1535 obj.offset = self.offset
1536 obj.llen = self.llen
1537 obj.vlen = self.vlen
1541 self._assert_ready()
1542 return int(self._value)
1545 self._assert_ready()
1548 bytes(self._expl or b"") +
1549 str(self._value).encode("ascii"),
1552 def __eq__(self, their):
1553 if isinstance(their, integer_types):
1554 return self._value == their
1555 if not issubclass(their.__class__, Integer):
1558 self._value == their._value and
1559 self.tag == their.tag and
1560 self._expl == their._expl
1563 def __lt__(self, their):
1564 return self._value < their._value
1568 for name, value in self.specs.items():
1569 if value == self._value:
1581 return self.__class__(
1584 (self._bound_min, self._bound_max)
1585 if bounds is None else bounds
1587 impl=self.tag if impl is None else impl,
1588 expl=self._expl if expl is None else expl,
1589 default=self.default if default is None else default,
1590 optional=self.optional if optional is None else optional,
1595 self._assert_ready()
1599 octets = bytearray([0])
1603 octets = bytearray()
1605 octets.append((value & 0xFF) ^ 0xFF)
1607 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1610 octets = bytearray()
1612 octets.append(value & 0xFF)
1614 if octets[-1] & 0x80 > 0:
1617 octets = bytes(octets)
1619 bytes_len = ceil(value.bit_length() / 8) or 1
1622 octets = value.to_bytes(
1627 except OverflowError:
1631 return b"".join((self.tag, len_encode(len(octets)), octets))
1633 def _decode(self, tlv, offset, decode_path, ctx):
1635 t, _, lv = tag_strip(tlv)
1636 except DecodeError as err:
1637 raise err.__class__(
1639 klass=self.__class__,
1640 decode_path=decode_path,
1645 klass=self.__class__,
1646 decode_path=decode_path,
1650 l, llen, v = len_decode(lv)
1651 except DecodeError as err:
1652 raise err.__class__(
1654 klass=self.__class__,
1655 decode_path=decode_path,
1659 raise NotEnoughData(
1660 "encoded length is longer than data",
1661 klass=self.__class__,
1662 decode_path=decode_path,
1666 raise NotEnoughData(
1668 klass=self.__class__,
1669 decode_path=decode_path,
1672 v, tail = v[:l], v[l:]
1673 first_octet = byte2int(v)
1675 second_octet = byte2int(v[1:])
1677 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1678 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1681 "non normalized integer",
1682 klass=self.__class__,
1683 decode_path=decode_path,
1688 if first_octet & 0x80 > 0:
1689 octets = bytearray()
1690 for octet in bytearray(v):
1691 octets.append(octet ^ 0xFF)
1692 for octet in octets:
1693 value = (value << 8) | octet
1697 for octet in bytearray(v):
1698 value = (value << 8) | octet
1700 value = int.from_bytes(v, byteorder="big", signed=True)
1702 obj = self.__class__(
1704 bounds=(self._bound_min, self._bound_max),
1707 default=self.default,
1708 optional=self.optional,
1710 _decoded=(offset, llen, l),
1712 except BoundsError as err:
1715 klass=self.__class__,
1716 decode_path=decode_path,
1722 return pp_console_row(next(self.pps()))
1724 def pps(self, decode_path=()):
1726 asn1_type_name=self.asn1_type_name,
1727 obj_name=self.__class__.__name__,
1728 decode_path=decode_path,
1729 value=(self.named or str(self._value)) if self.ready else None,
1730 optional=self.optional,
1731 default=self == self.default,
1732 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1733 expl=None if self._expl is None else tag_decode(self._expl),
1738 expl_offset=self.expl_offset if self.expled else None,
1739 expl_tlen=self.expl_tlen if self.expled else None,
1740 expl_llen=self.expl_llen if self.expled else None,
1741 expl_vlen=self.expl_vlen if self.expled else None,
1745 class BitString(Obj):
1746 """``BIT STRING`` bit string type
1748 >>> BitString(b"hello world")
1749 BIT STRING 88 bits 68656c6c6f20776f726c64
1752 >>> b == b"hello world"
1757 >>> b = BitString("'010110000000'B")
1758 BIT STRING 12 bits 5800
1761 >>> b[0], b[1], b[2], b[3]
1762 (False, True, False, True)
1766 [False, True, False, True, True, False, False, False, False, False, False, False]
1770 class KeyUsage(BitString):
1772 ("digitalSignature", 0),
1773 ("nonRepudiation", 1),
1774 ("keyEncipherment", 2),
1777 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1778 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1780 ['nonRepudiation', 'keyEncipherment']
1782 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1784 __slots__ = ("specs", "defined")
1785 tag_default = tag_encode(3)
1786 asn1_type_name = "BIT STRING"
1799 :param value: set the value. Either binary type, tuple of named
1800 values (if ``schema`` is specified in the class),
1801 string in ``'XXX...'B`` form, or
1802 :py:class:`pyderasn.BitString` object
1803 :param bytes impl: override default tag with ``IMPLICIT`` one
1804 :param bytes expl: override default tag with ``EXPLICIT`` one
1805 :param default: set default value. Type same as in ``value``
1806 :param bool optional: is object ``OPTIONAL`` in sequence
1808 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1809 specs = getattr(self, "schema", {}) if _specs is None else _specs
1810 self.specs = specs if isinstance(specs, dict) else dict(specs)
1811 self._value = None if value is None else self._value_sanitize(value)
1812 if default is not None:
1813 default = self._value_sanitize(default)
1814 self.default = self.__class__(
1820 self._value = default
1823 def _bits2octets(self, bits):
1824 if len(self.specs) > 0:
1825 bits = bits.rstrip("0")
1827 bits += "0" * ((8 - (bit_len % 8)) % 8)
1828 octets = bytearray(len(bits) // 8)
1829 for i in six_xrange(len(octets)):
1830 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1831 return bit_len, bytes(octets)
1833 def _value_sanitize(self, value):
1834 if issubclass(value.__class__, BitString):
1836 if isinstance(value, (string_types, binary_type)):
1838 isinstance(value, string_types) and
1839 value.startswith("'") and
1840 value.endswith("'B")
1843 if not set(value) <= set(("0", "1")):
1844 raise ValueError("B's coding contains unacceptable chars")
1845 return self._bits2octets(value)
1846 elif isinstance(value, binary_type):
1847 return (len(value) * 8, value)
1849 raise InvalidValueType((
1854 if isinstance(value, tuple):
1857 isinstance(value[0], integer_types) and
1858 isinstance(value[1], binary_type)
1863 bit = self.specs.get(name)
1865 raise ObjUnknown("BitString value: %s" % name)
1868 return self._bits2octets("")
1870 return self._bits2octets("".join(
1871 ("1" if bit in bits else "0")
1872 for bit in six_xrange(max(bits) + 1)
1874 raise InvalidValueType((self.__class__, binary_type, string_types))
1878 return self._value is not None
1881 obj = self.__class__(_specs=self.specs)
1883 if value is not None:
1884 value = (value[0], value[1])
1887 obj._expl = self._expl
1888 obj.default = self.default
1889 obj.optional = self.optional
1890 obj.offset = self.offset
1891 obj.llen = self.llen
1892 obj.vlen = self.vlen
1896 self._assert_ready()
1897 for i in six_xrange(self._value[0]):
1902 self._assert_ready()
1903 return self._value[0]
1905 def __bytes__(self):
1906 self._assert_ready()
1907 return self._value[1]
1909 def __eq__(self, their):
1910 if isinstance(their, bytes):
1911 return self._value[1] == their
1912 if not issubclass(their.__class__, BitString):
1915 self._value == their._value and
1916 self.tag == their.tag and
1917 self._expl == their._expl
1922 return [name for name, bit in self.specs.items() if self[bit]]
1932 return self.__class__(
1934 impl=self.tag if impl is None else impl,
1935 expl=self._expl if expl is None else expl,
1936 default=self.default if default is None else default,
1937 optional=self.optional if optional is None else optional,
1941 def __getitem__(self, key):
1942 if isinstance(key, int):
1943 bit_len, octets = self._value
1947 byte2int(memoryview(octets)[key // 8:]) >>
1950 if isinstance(key, string_types):
1951 value = self.specs.get(key)
1953 raise ObjUnknown("BitString value: %s" % key)
1955 raise InvalidValueType((int, str))
1958 self._assert_ready()
1959 bit_len, octets = self._value
1962 len_encode(len(octets) + 1),
1963 int2byte((8 - bit_len % 8) % 8),
1967 def _decode(self, tlv, offset, decode_path, ctx):
1969 t, _, lv = tag_strip(tlv)
1970 except DecodeError as err:
1971 raise err.__class__(
1973 klass=self.__class__,
1974 decode_path=decode_path,
1979 klass=self.__class__,
1980 decode_path=decode_path,
1984 l, llen, v = len_decode(lv)
1985 except DecodeError as err:
1986 raise err.__class__(
1988 klass=self.__class__,
1989 decode_path=decode_path,
1993 raise NotEnoughData(
1994 "encoded length is longer than data",
1995 klass=self.__class__,
1996 decode_path=decode_path,
2000 raise NotEnoughData(
2002 klass=self.__class__,
2003 decode_path=decode_path,
2006 pad_size = byte2int(v)
2007 if l == 1 and pad_size != 0:
2009 "invalid empty value",
2010 klass=self.__class__,
2011 decode_path=decode_path,
2017 klass=self.__class__,
2018 decode_path=decode_path,
2021 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2024 klass=self.__class__,
2025 decode_path=decode_path,
2028 v, tail = v[:l], v[l:]
2029 obj = self.__class__(
2030 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2033 default=self.default,
2034 optional=self.optional,
2036 _decoded=(offset, llen, l),
2041 return pp_console_row(next(self.pps()))
2043 def pps(self, decode_path=()):
2047 bit_len, blob = self._value
2048 value = "%d bits" % bit_len
2049 if len(self.specs) > 0:
2050 blob = tuple(self.named)
2052 asn1_type_name=self.asn1_type_name,
2053 obj_name=self.__class__.__name__,
2054 decode_path=decode_path,
2057 optional=self.optional,
2058 default=self == self.default,
2059 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2060 expl=None if self._expl is None else tag_decode(self._expl),
2065 expl_offset=self.expl_offset if self.expled else None,
2066 expl_tlen=self.expl_tlen if self.expled else None,
2067 expl_llen=self.expl_llen if self.expled else None,
2068 expl_vlen=self.expl_vlen if self.expled else None,
2070 defined_by, defined = self.defined or (None, None)
2071 if defined_by is not None:
2073 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2077 class OctetString(Obj):
2078 """``OCTET STRING`` binary string type
2080 >>> s = OctetString(b"hello world")
2081 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2082 >>> s == OctetString(b"hello world")
2087 >>> OctetString(b"hello", bounds=(4, 4))
2088 Traceback (most recent call last):
2089 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2090 >>> OctetString(b"hell", bounds=(4, 4))
2091 OCTET STRING 4 bytes 68656c6c
2093 __slots__ = ("_bound_min", "_bound_max", "defined")
2094 tag_default = tag_encode(4)
2095 asn1_type_name = "OCTET STRING"
2108 :param value: set the value. Either binary type, or
2109 :py:class:`pyderasn.OctetString` object
2110 :param bounds: set ``(MIN, MAX)`` value size constraint.
2111 (-inf, +inf) by default
2112 :param bytes impl: override default tag with ``IMPLICIT`` one
2113 :param bytes expl: override default tag with ``EXPLICIT`` one
2114 :param default: set default value. Type same as in ``value``
2115 :param bool optional: is object ``OPTIONAL`` in sequence
2117 super(OctetString, self).__init__(
2125 self._bound_min, self._bound_max = getattr(
2129 ) if bounds is None else bounds
2130 if value is not None:
2131 self._value = self._value_sanitize(value)
2132 if default is not None:
2133 default = self._value_sanitize(default)
2134 self.default = self.__class__(
2139 if self._value is None:
2140 self._value = default
2143 def _value_sanitize(self, value):
2144 if issubclass(value.__class__, OctetString):
2145 value = value._value
2146 elif isinstance(value, binary_type):
2149 raise InvalidValueType((self.__class__, bytes))
2150 if not self._bound_min <= len(value) <= self._bound_max:
2151 raise BoundsError(self._bound_min, len(value), self._bound_max)
2156 return self._value is not None
2159 obj = self.__class__()
2160 obj._value = self._value
2161 obj._bound_min = self._bound_min
2162 obj._bound_max = self._bound_max
2164 obj._expl = self._expl
2165 obj.default = self.default
2166 obj.optional = self.optional
2167 obj.offset = self.offset
2168 obj.llen = self.llen
2169 obj.vlen = self.vlen
2172 def __bytes__(self):
2173 self._assert_ready()
2176 def __eq__(self, their):
2177 if isinstance(their, binary_type):
2178 return self._value == their
2179 if not issubclass(their.__class__, OctetString):
2182 self._value == their._value and
2183 self.tag == their.tag and
2184 self._expl == their._expl
2187 def __lt__(self, their):
2188 return self._value < their._value
2199 return self.__class__(
2202 (self._bound_min, self._bound_max)
2203 if bounds is None else bounds
2205 impl=self.tag if impl is None else impl,
2206 expl=self._expl if expl is None else expl,
2207 default=self.default if default is None else default,
2208 optional=self.optional if optional is None else optional,
2212 self._assert_ready()
2215 len_encode(len(self._value)),
2219 def _decode(self, tlv, offset, decode_path, ctx):
2221 t, _, lv = tag_strip(tlv)
2222 except DecodeError as err:
2223 raise err.__class__(
2225 klass=self.__class__,
2226 decode_path=decode_path,
2231 klass=self.__class__,
2232 decode_path=decode_path,
2236 l, llen, v = len_decode(lv)
2237 except DecodeError as err:
2238 raise err.__class__(
2240 klass=self.__class__,
2241 decode_path=decode_path,
2245 raise NotEnoughData(
2246 "encoded length is longer than data",
2247 klass=self.__class__,
2248 decode_path=decode_path,
2251 v, tail = v[:l], v[l:]
2253 obj = self.__class__(
2255 bounds=(self._bound_min, self._bound_max),
2258 default=self.default,
2259 optional=self.optional,
2260 _decoded=(offset, llen, l),
2262 except DecodeError as err:
2265 klass=self.__class__,
2266 decode_path=decode_path,
2269 except BoundsError as err:
2272 klass=self.__class__,
2273 decode_path=decode_path,
2279 return pp_console_row(next(self.pps()))
2281 def pps(self, decode_path=()):
2283 asn1_type_name=self.asn1_type_name,
2284 obj_name=self.__class__.__name__,
2285 decode_path=decode_path,
2286 value=("%d bytes" % len(self._value)) if self.ready else None,
2287 blob=self._value if self.ready else None,
2288 optional=self.optional,
2289 default=self == self.default,
2290 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2291 expl=None if self._expl is None else tag_decode(self._expl),
2296 expl_offset=self.expl_offset if self.expled else None,
2297 expl_tlen=self.expl_tlen if self.expled else None,
2298 expl_llen=self.expl_llen if self.expled else None,
2299 expl_vlen=self.expl_vlen if self.expled else None,
2301 defined_by, defined = self.defined or (None, None)
2302 if defined_by is not None:
2304 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2309 """``NULL`` null object
2317 tag_default = tag_encode(5)
2318 asn1_type_name = "NULL"
2322 value=None, # unused, but Sequence passes it
2329 :param bytes impl: override default tag with ``IMPLICIT`` one
2330 :param bytes expl: override default tag with ``EXPLICIT`` one
2331 :param bool optional: is object ``OPTIONAL`` in sequence
2333 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2341 obj = self.__class__()
2343 obj._expl = self._expl
2344 obj.default = self.default
2345 obj.optional = self.optional
2346 obj.offset = self.offset
2347 obj.llen = self.llen
2348 obj.vlen = self.vlen
2351 def __eq__(self, their):
2352 if not issubclass(their.__class__, Null):
2355 self.tag == their.tag and
2356 self._expl == their._expl
2366 return self.__class__(
2367 impl=self.tag if impl is None else impl,
2368 expl=self._expl if expl is None else expl,
2369 optional=self.optional if optional is None else optional,
2373 return self.tag + len_encode(0)
2375 def _decode(self, tlv, offset, decode_path, ctx):
2377 t, _, lv = tag_strip(tlv)
2378 except DecodeError as err:
2379 raise err.__class__(
2381 klass=self.__class__,
2382 decode_path=decode_path,
2387 klass=self.__class__,
2388 decode_path=decode_path,
2392 l, _, v = len_decode(lv)
2393 except DecodeError as err:
2394 raise err.__class__(
2396 klass=self.__class__,
2397 decode_path=decode_path,
2401 raise InvalidLength(
2402 "Null must have zero length",
2403 klass=self.__class__,
2404 decode_path=decode_path,
2407 obj = self.__class__(
2410 optional=self.optional,
2411 _decoded=(offset, 1, 0),
2416 return pp_console_row(next(self.pps()))
2418 def pps(self, decode_path=()):
2420 asn1_type_name=self.asn1_type_name,
2421 obj_name=self.__class__.__name__,
2422 decode_path=decode_path,
2423 optional=self.optional,
2424 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2425 expl=None if self._expl is None else tag_decode(self._expl),
2430 expl_offset=self.expl_offset if self.expled else None,
2431 expl_tlen=self.expl_tlen if self.expled else None,
2432 expl_llen=self.expl_llen if self.expled else None,
2433 expl_vlen=self.expl_vlen if self.expled else None,
2437 class ObjectIdentifier(Obj):
2438 """``OBJECT IDENTIFIER`` OID type
2440 >>> oid = ObjectIdentifier((1, 2, 3))
2441 OBJECT IDENTIFIER 1.2.3
2442 >>> oid == ObjectIdentifier("1.2.3")
2448 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2449 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2451 >>> str(ObjectIdentifier((3, 1)))
2452 Traceback (most recent call last):
2453 pyderasn.InvalidOID: unacceptable first arc value
2455 __slots__ = ("defines",)
2456 tag_default = tag_encode(6)
2457 asn1_type_name = "OBJECT IDENTIFIER"
2470 :param value: set the value. Either tuples of integers,
2471 string of "."-concatenated integers, or
2472 :py:class:`pyderasn.ObjectIdentifier` object
2473 :param defines: sequence of tuples. Each tuple has two elements.
2474 First one is relative to current one decode
2475 path, aiming to the field defined by that OID.
2476 Read about relative path in
2477 :py:func:`pyderasn.abs_decode_path`. Second
2478 tuple element is ``{OID: pyderasn.Obj()}``
2479 dictionary, mapping between current OID value
2480 and structure applied to defined field.
2481 :ref:`Read about DEFINED BY <definedby>`
2482 :param bytes impl: override default tag with ``IMPLICIT`` one
2483 :param bytes expl: override default tag with ``EXPLICIT`` one
2484 :param default: set default value. Type same as in ``value``
2485 :param bool optional: is object ``OPTIONAL`` in sequence
2487 super(ObjectIdentifier, self).__init__(
2495 if value is not None:
2496 self._value = self._value_sanitize(value)
2497 if default is not None:
2498 default = self._value_sanitize(default)
2499 self.default = self.__class__(
2504 if self._value is None:
2505 self._value = default
2506 self.defines = defines
2508 def __add__(self, their):
2509 if isinstance(their, self.__class__):
2510 return self.__class__(self._value + their._value)
2511 if isinstance(their, tuple):
2512 return self.__class__(self._value + their)
2513 raise InvalidValueType((self.__class__, tuple))
2515 def _value_sanitize(self, value):
2516 if issubclass(value.__class__, ObjectIdentifier):
2518 if isinstance(value, string_types):
2520 value = tuple(int(arc) for arc in value.split("."))
2522 raise InvalidOID("unacceptable arcs values")
2523 if isinstance(value, tuple):
2525 raise InvalidOID("less than 2 arcs")
2526 first_arc = value[0]
2527 if first_arc in (0, 1):
2528 if not (0 <= value[1] <= 39):
2529 raise InvalidOID("second arc is too wide")
2530 elif first_arc == 2:
2533 raise InvalidOID("unacceptable first arc value")
2535 raise InvalidValueType((self.__class__, str, tuple))
2539 return self._value is not None
2542 obj = self.__class__()
2543 obj._value = self._value
2544 obj.defines = self.defines
2546 obj._expl = self._expl
2547 obj.default = self.default
2548 obj.optional = self.optional
2549 obj.offset = self.offset
2550 obj.llen = self.llen
2551 obj.vlen = self.vlen
2555 self._assert_ready()
2556 return iter(self._value)
2559 return ".".join(str(arc) for arc in self._value or ())
2562 self._assert_ready()
2565 bytes(self._expl or b"") +
2566 str(self._value).encode("ascii"),
2569 def __eq__(self, their):
2570 if isinstance(their, tuple):
2571 return self._value == their
2572 if not issubclass(their.__class__, ObjectIdentifier):
2575 self.tag == their.tag and
2576 self._expl == their._expl and
2577 self._value == their._value
2580 def __lt__(self, their):
2581 return self._value < their._value
2592 return self.__class__(
2594 defines=self.defines if defines is None else defines,
2595 impl=self.tag if impl is None else impl,
2596 expl=self._expl if expl is None else expl,
2597 default=self.default if default is None else default,
2598 optional=self.optional if optional is None else optional,
2602 self._assert_ready()
2604 first_value = value[1]
2605 first_arc = value[0]
2608 elif first_arc == 1:
2610 elif first_arc == 2:
2612 else: # pragma: no cover
2613 raise RuntimeError("invalid arc is stored")
2614 octets = [zero_ended_encode(first_value)]
2615 for arc in value[2:]:
2616 octets.append(zero_ended_encode(arc))
2617 v = b"".join(octets)
2618 return b"".join((self.tag, len_encode(len(v)), v))
2620 def _decode(self, tlv, offset, decode_path, ctx):
2622 t, _, lv = tag_strip(tlv)
2623 except DecodeError as err:
2624 raise err.__class__(
2626 klass=self.__class__,
2627 decode_path=decode_path,
2632 klass=self.__class__,
2633 decode_path=decode_path,
2637 l, llen, v = len_decode(lv)
2638 except DecodeError as err:
2639 raise err.__class__(
2641 klass=self.__class__,
2642 decode_path=decode_path,
2646 raise NotEnoughData(
2647 "encoded length is longer than data",
2648 klass=self.__class__,
2649 decode_path=decode_path,
2653 raise NotEnoughData(
2655 klass=self.__class__,
2656 decode_path=decode_path,
2659 v, tail = v[:l], v[l:]
2665 octet = indexbytes(v, i)
2666 arc = (arc << 7) | (octet & 0x7F)
2667 if octet & 0x80 == 0:
2675 klass=self.__class__,
2676 decode_path=decode_path,
2680 second_arc = arcs[0]
2681 if 0 <= second_arc <= 39:
2683 elif 40 <= second_arc <= 79:
2689 obj = self.__class__(
2690 value=tuple([first_arc, second_arc] + arcs[1:]),
2693 default=self.default,
2694 optional=self.optional,
2695 _decoded=(offset, llen, l),
2700 return pp_console_row(next(self.pps()))
2702 def pps(self, decode_path=()):
2704 asn1_type_name=self.asn1_type_name,
2705 obj_name=self.__class__.__name__,
2706 decode_path=decode_path,
2707 value=str(self) if self.ready else None,
2708 optional=self.optional,
2709 default=self == self.default,
2710 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2711 expl=None if self._expl is None else tag_decode(self._expl),
2716 expl_offset=self.expl_offset if self.expled else None,
2717 expl_tlen=self.expl_tlen if self.expled else None,
2718 expl_llen=self.expl_llen if self.expled else None,
2719 expl_vlen=self.expl_vlen if self.expled else None,
2723 class Enumerated(Integer):
2724 """``ENUMERATED`` integer type
2726 This type is identical to :py:class:`pyderasn.Integer`, but requires
2727 schema to be specified and does not accept values missing from it.
2730 tag_default = tag_encode(10)
2731 asn1_type_name = "ENUMERATED"
2742 bounds=None, # dummy argument, workability for Integer.decode
2744 super(Enumerated, self).__init__(
2753 if len(self.specs) == 0:
2754 raise ValueError("schema must be specified")
2756 def _value_sanitize(self, value):
2757 if isinstance(value, self.__class__):
2758 value = value._value
2759 elif isinstance(value, integer_types):
2760 if value not in list(self.specs.values()):
2762 "unknown integer value: %s" % value,
2763 klass=self.__class__,
2765 elif isinstance(value, string_types):
2766 value = self.specs.get(value)
2768 raise ObjUnknown("integer value: %s" % value)
2770 raise InvalidValueType((self.__class__, int, str))
2774 obj = self.__class__(_specs=self.specs)
2775 obj._value = self._value
2776 obj._bound_min = self._bound_min
2777 obj._bound_max = self._bound_max
2779 obj._expl = self._expl
2780 obj.default = self.default
2781 obj.optional = self.optional
2782 obj.offset = self.offset
2783 obj.llen = self.llen
2784 obj.vlen = self.vlen
2796 return self.__class__(
2798 impl=self.tag if impl is None else impl,
2799 expl=self._expl if expl is None else expl,
2800 default=self.default if default is None else default,
2801 optional=self.optional if optional is None else optional,
2806 class CommonString(OctetString):
2807 """Common class for all strings
2809 Everything resembles :py:class:`pyderasn.OctetString`, except
2810 ability to deal with unicode text strings.
2812 >>> hexenc("привет мир".encode("utf-8"))
2813 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2814 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2816 >>> s = UTF8String("привет мир")
2817 UTF8String UTF8String привет мир
2819 'привет мир'
2820 >>> hexenc(bytes(s))
2821 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2823 >>> PrintableString("привет мир")
2824 Traceback (most recent call last):
2825 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2827 >>> BMPString("ада", bounds=(2, 2))
2828 Traceback (most recent call last):
2829 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2830 >>> s = BMPString("ад", bounds=(2, 2))
2833 >>> hexenc(bytes(s))
2841 * - :py:class:`pyderasn.UTF8String`
2843 * - :py:class:`pyderasn.NumericString`
2845 * - :py:class:`pyderasn.PrintableString`
2847 * - :py:class:`pyderasn.TeletexString`
2849 * - :py:class:`pyderasn.T61String`
2851 * - :py:class:`pyderasn.VideotexString`
2853 * - :py:class:`pyderasn.IA5String`
2855 * - :py:class:`pyderasn.GraphicString`
2857 * - :py:class:`pyderasn.VisibleString`
2859 * - :py:class:`pyderasn.ISO646String`
2861 * - :py:class:`pyderasn.GeneralString`
2863 * - :py:class:`pyderasn.UniversalString`
2865 * - :py:class:`pyderasn.BMPString`
2868 __slots__ = ("encoding",)
2870 def _value_sanitize(self, value):
2872 value_decoded = None
2873 if isinstance(value, self.__class__):
2874 value_raw = value._value
2875 elif isinstance(value, text_type):
2876 value_decoded = value
2877 elif isinstance(value, binary_type):
2880 raise InvalidValueType((self.__class__, text_type, binary_type))
2883 value_decoded.encode(self.encoding)
2884 if value_raw is None else value_raw
2887 value_raw.decode(self.encoding)
2888 if value_decoded is None else value_decoded
2890 except (UnicodeEncodeError, UnicodeDecodeError) as err:
2891 raise DecodeError(str(err))
2892 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2900 def __eq__(self, their):
2901 if isinstance(their, binary_type):
2902 return self._value == their
2903 if isinstance(their, text_type):
2904 return self._value == their.encode(self.encoding)
2905 if not isinstance(their, self.__class__):
2908 self._value == their._value and
2909 self.tag == their.tag and
2910 self._expl == their._expl
2913 def __unicode__(self):
2915 return self._value.decode(self.encoding)
2916 return text_type(self._value)
2919 return pp_console_row(next(self.pps(no_unicode=PY2)))
2921 def pps(self, decode_path=(), no_unicode=False):
2924 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2926 asn1_type_name=self.asn1_type_name,
2927 obj_name=self.__class__.__name__,
2928 decode_path=decode_path,
2930 optional=self.optional,
2931 default=self == self.default,
2932 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2933 expl=None if self._expl is None else tag_decode(self._expl),
2941 class UTF8String(CommonString):
2943 tag_default = tag_encode(12)
2945 asn1_type_name = "UTF8String"
2948 class NumericString(CommonString):
2950 tag_default = tag_encode(18)
2952 asn1_type_name = "NumericString"
2953 allowable_chars = set(digits.encode("ascii"))
2955 def _value_sanitize(self, value):
2956 value = super(NumericString, self)._value_sanitize(value)
2957 if not set(value) <= self.allowable_chars:
2958 raise DecodeError("non-numeric value")
2962 class PrintableString(CommonString):
2964 tag_default = tag_encode(19)
2966 asn1_type_name = "PrintableString"
2969 class TeletexString(CommonString):
2971 tag_default = tag_encode(20)
2973 asn1_type_name = "TeletexString"
2976 class T61String(TeletexString):
2978 asn1_type_name = "T61String"
2981 class VideotexString(CommonString):
2983 tag_default = tag_encode(21)
2984 encoding = "iso-8859-1"
2985 asn1_type_name = "VideotexString"
2988 class IA5String(CommonString):
2990 tag_default = tag_encode(22)
2992 asn1_type_name = "IA5"
2995 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
2996 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
2997 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3000 class UTCTime(CommonString):
3001 """``UTCTime`` datetime type
3003 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3004 UTCTime UTCTime 2017-09-30T22:07:50
3010 datetime.datetime(2017, 9, 30, 22, 7, 50)
3011 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3012 datetime.datetime(1957, 9, 30, 22, 7, 50)
3015 tag_default = tag_encode(23)
3017 asn1_type_name = "UTCTime"
3019 fmt = "%y%m%d%H%M%SZ"
3029 bounds=None, # dummy argument, workability for OctetString.decode
3032 :param value: set the value. Either datetime type, or
3033 :py:class:`pyderasn.UTCTime` object
3034 :param bytes impl: override default tag with ``IMPLICIT`` one
3035 :param bytes expl: override default tag with ``EXPLICIT`` one
3036 :param default: set default value. Type same as in ``value``
3037 :param bool optional: is object ``OPTIONAL`` in sequence
3039 super(UTCTime, self).__init__(
3047 if value is not None:
3048 self._value = self._value_sanitize(value)
3049 if default is not None:
3050 default = self._value_sanitize(default)
3051 self.default = self.__class__(
3056 if self._value is None:
3057 self._value = default
3059 def _value_sanitize(self, value):
3060 if isinstance(value, self.__class__):
3062 if isinstance(value, datetime):
3063 return value.strftime(self.fmt).encode("ascii")
3064 if isinstance(value, binary_type):
3065 value_decoded = value.decode("ascii")
3066 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3068 datetime.strptime(value_decoded, self.fmt)
3070 raise DecodeError("invalid UTCTime format")
3073 raise DecodeError("invalid UTCTime length")
3074 raise InvalidValueType((self.__class__, datetime))
3076 def __eq__(self, their):
3077 if isinstance(their, binary_type):
3078 return self._value == their
3079 if isinstance(their, datetime):
3080 return self.todatetime() == their
3081 if not isinstance(their, self.__class__):
3084 self._value == their._value and
3085 self.tag == their.tag and
3086 self._expl == their._expl
3089 def todatetime(self):
3090 """Convert to datetime
3094 Pay attention that UTCTime can not hold full year, so all years
3095 having < 50 years are treated as 20xx, 19xx otherwise, according
3096 to X.509 recomendation.
3098 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3099 year = value.year % 100
3101 year=(2000 + year) if year < 50 else (1900 + year),
3105 minute=value.minute,
3106 second=value.second,
3110 return pp_console_row(next(self.pps()))
3112 def pps(self, decode_path=()):
3114 asn1_type_name=self.asn1_type_name,
3115 obj_name=self.__class__.__name__,
3116 decode_path=decode_path,
3117 value=self.todatetime().isoformat() if self.ready else None,
3118 optional=self.optional,
3119 default=self == self.default,
3120 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3121 expl=None if self._expl is None else tag_decode(self._expl),
3129 class GeneralizedTime(UTCTime):
3130 """``GeneralizedTime`` datetime type
3132 This type is similar to :py:class:`pyderasn.UTCTime`.
3134 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3135 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3137 '20170930220750.000123Z'
3138 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3139 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3142 tag_default = tag_encode(24)
3143 asn1_type_name = "GeneralizedTime"
3145 fmt = "%Y%m%d%H%M%SZ"
3146 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3148 def _value_sanitize(self, value):
3149 if isinstance(value, self.__class__):
3151 if isinstance(value, datetime):
3152 return value.strftime(
3153 self.fmt_ms if value.microsecond > 0 else self.fmt
3155 if isinstance(value, binary_type):
3156 value_decoded = value.decode("ascii")
3157 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3159 datetime.strptime(value_decoded, self.fmt)
3162 "invalid GeneralizedTime (without ms) format",
3165 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3167 datetime.strptime(value_decoded, self.fmt_ms)
3170 "invalid GeneralizedTime (with ms) format",
3175 "invalid GeneralizedTime length",
3176 klass=self.__class__,
3178 raise InvalidValueType((self.__class__, datetime))
3180 def todatetime(self):
3181 value = self._value.decode("ascii")
3182 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3183 return datetime.strptime(value, self.fmt)
3184 return datetime.strptime(value, self.fmt_ms)
3187 class GraphicString(CommonString):
3189 tag_default = tag_encode(25)
3190 encoding = "iso-8859-1"
3191 asn1_type_name = "GraphicString"
3194 class VisibleString(CommonString):
3196 tag_default = tag_encode(26)
3198 asn1_type_name = "VisibleString"
3201 class ISO646String(VisibleString):
3203 asn1_type_name = "ISO646String"
3206 class GeneralString(CommonString):
3208 tag_default = tag_encode(27)
3209 encoding = "iso-8859-1"
3210 asn1_type_name = "GeneralString"
3213 class UniversalString(CommonString):
3215 tag_default = tag_encode(28)
3216 encoding = "utf-32-be"
3217 asn1_type_name = "UniversalString"
3220 class BMPString(CommonString):
3222 tag_default = tag_encode(30)
3223 encoding = "utf-16-be"
3224 asn1_type_name = "BMPString"
3228 """``CHOICE`` special type
3232 class GeneralName(Choice):
3234 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3235 ("dNSName", IA5String(impl=tag_ctxp(2))),
3238 >>> gn = GeneralName()
3240 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3241 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3242 >>> gn["dNSName"] = IA5String("bar.baz")
3243 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3244 >>> gn["rfc822Name"]
3247 [2] IA5String IA5 bar.baz
3250 >>> gn.value == gn["dNSName"]
3253 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3255 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3256 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3258 __slots__ = ("specs",)
3260 asn1_type_name = "CHOICE"
3273 :param value: set the value. Either ``(choice, value)`` tuple, or
3274 :py:class:`pyderasn.Choice` object
3275 :param bytes impl: can not be set, do **not** use it
3276 :param bytes expl: override default tag with ``EXPLICIT`` one
3277 :param default: set default value. Type same as in ``value``
3278 :param bool optional: is object ``OPTIONAL`` in sequence
3280 if impl is not None:
3281 raise ValueError("no implicit tag allowed for CHOICE")
3282 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3284 schema = getattr(self, "schema", ())
3285 if len(schema) == 0:
3286 raise ValueError("schema must be specified")
3288 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3291 if value is not None:
3292 self._value = self._value_sanitize(value)
3293 if default is not None:
3294 default_value = self._value_sanitize(default)
3295 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3296 default_obj.specs = self.specs
3297 default_obj._value = default_value
3298 self.default = default_obj
3300 self._value = default_obj.copy()._value
3302 def _value_sanitize(self, value):
3303 if isinstance(value, self.__class__):
3305 if isinstance(value, tuple) and len(value) == 2:
3307 spec = self.specs.get(choice)
3309 raise ObjUnknown(choice)
3310 if not isinstance(obj, spec.__class__):
3311 raise InvalidValueType((spec,))
3312 return (choice, spec(obj))
3313 raise InvalidValueType((self.__class__, tuple))
3317 return self._value is not None and self._value[1].ready
3320 obj = self.__class__(schema=self.specs)
3321 obj._expl = self._expl
3322 obj.default = self.default
3323 obj.optional = self.optional
3324 obj.offset = self.offset
3325 obj.llen = self.llen
3326 obj.vlen = self.vlen
3328 if value is not None:
3329 obj._value = (value[0], value[1].copy())
3332 def __eq__(self, their):
3333 if isinstance(their, tuple) and len(their) == 2:
3334 return self._value == their
3335 if not isinstance(their, self.__class__):
3338 self.specs == their.specs and
3339 self._value == their._value
3349 return self.__class__(
3352 expl=self._expl if expl is None else expl,
3353 default=self.default if default is None else default,
3354 optional=self.optional if optional is None else optional,
3359 self._assert_ready()
3360 return self._value[0]
3364 self._assert_ready()
3365 return self._value[1]
3367 def __getitem__(self, key):
3368 if key not in self.specs:
3369 raise ObjUnknown(key)
3370 if self._value is None:
3372 choice, value = self._value
3377 def __setitem__(self, key, value):
3378 spec = self.specs.get(key)
3380 raise ObjUnknown(key)
3381 if not isinstance(value, spec.__class__):
3382 raise InvalidValueType((spec.__class__,))
3383 self._value = (key, spec(value))
3391 return self._value[1].decoded if self.ready else False
3394 self._assert_ready()
3395 return self._value[1].encode()
3397 def _decode(self, tlv, offset, decode_path, ctx):
3398 for choice, spec in self.specs.items():
3400 value, tail = spec.decode(
3404 decode_path=decode_path + (choice,),
3409 obj = self.__class__(
3412 default=self.default,
3413 optional=self.optional,
3414 _decoded=(offset, 0, value.tlvlen),
3416 obj._value = (choice, value)
3419 klass=self.__class__,
3420 decode_path=decode_path,
3425 value = pp_console_row(next(self.pps()))
3427 value = "%s[%r]" % (value, self.value)
3430 def pps(self, decode_path=()):
3432 asn1_type_name=self.asn1_type_name,
3433 obj_name=self.__class__.__name__,
3434 decode_path=decode_path,
3435 value=self.choice if self.ready else None,
3436 optional=self.optional,
3437 default=self == self.default,
3438 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3439 expl=None if self._expl is None else tag_decode(self._expl),
3446 yield self.value.pps(decode_path=decode_path + (self.choice,))
3449 class PrimitiveTypes(Choice):
3450 """Predefined ``CHOICE`` for all generic primitive types
3452 It could be useful for general decoding of some unspecified values:
3454 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3455 OCTET STRING 3 bytes 666f6f
3456 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3460 schema = tuple((klass.__name__, klass()) for klass in (
3485 """``ANY`` special type
3487 >>> Any(Integer(-123))
3489 >>> a = Any(OctetString(b"hello world").encode())
3490 ANY 040b68656c6c6f20776f726c64
3491 >>> hexenc(bytes(a))
3492 b'0x040x0bhello world'
3494 __slots__ = ("defined",)
3495 tag_default = tag_encode(0)
3496 asn1_type_name = "ANY"
3506 :param value: set the value. Either any kind of pyderasn's
3507 **ready** object, or bytes. Pay attention that
3508 **no** validation is performed is raw binary value
3510 :param bytes expl: override default tag with ``EXPLICIT`` one
3511 :param bool optional: is object ``OPTIONAL`` in sequence
3513 super(Any, self).__init__(None, expl, None, optional, _decoded)
3514 self._value = None if value is None else self._value_sanitize(value)
3517 def _value_sanitize(self, value):
3518 if isinstance(value, self.__class__):
3520 if isinstance(value, Obj):
3521 return value.encode()
3522 if isinstance(value, binary_type):
3524 raise InvalidValueType((self.__class__, Obj, binary_type))
3528 return self._value is not None
3531 obj = self.__class__()
3532 obj._value = self._value
3534 obj._expl = self._expl
3535 obj.optional = self.optional
3536 obj.offset = self.offset
3537 obj.llen = self.llen
3538 obj.vlen = self.vlen
3541 def __eq__(self, their):
3542 if isinstance(their, binary_type):
3543 return self._value == their
3544 if issubclass(their.__class__, Any):
3545 return self._value == their._value
3554 return self.__class__(
3556 expl=self._expl if expl is None else expl,
3557 optional=self.optional if optional is None else optional,
3560 def __bytes__(self):
3561 self._assert_ready()
3569 self._assert_ready()
3572 def _decode(self, tlv, offset, decode_path, ctx):
3574 t, tlen, lv = tag_strip(tlv)
3575 l, llen, v = len_decode(lv)
3576 except DecodeError as err:
3577 raise err.__class__(
3579 klass=self.__class__,
3580 decode_path=decode_path,
3584 raise NotEnoughData(
3585 "encoded length is longer than data",
3586 klass=self.__class__,
3587 decode_path=decode_path,
3590 tlvlen = tlen + llen + l
3591 v, tail = tlv[:tlvlen], v[l:]
3592 obj = self.__class__(
3595 optional=self.optional,
3596 _decoded=(offset, 0, tlvlen),
3602 return pp_console_row(next(self.pps()))
3604 def pps(self, decode_path=()):
3606 asn1_type_name=self.asn1_type_name,
3607 obj_name=self.__class__.__name__,
3608 decode_path=decode_path,
3609 blob=self._value if self.ready else None,
3610 optional=self.optional,
3611 default=self == self.default,
3612 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3613 expl=None if self._expl is None else tag_decode(self._expl),
3618 expl_offset=self.expl_offset if self.expled else None,
3619 expl_tlen=self.expl_tlen if self.expled else None,
3620 expl_llen=self.expl_llen if self.expled else None,
3621 expl_vlen=self.expl_vlen if self.expled else None,
3623 defined_by, defined = self.defined or (None, None)
3624 if defined_by is not None:
3626 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3630 ########################################################################
3631 # ASN.1 constructed types
3632 ########################################################################
3634 def get_def_by_path(defines_by_path, sub_decode_path):
3635 """Get define by decode path
3637 for path, define in defines_by_path:
3638 if len(path) != len(sub_decode_path):
3640 for p1, p2 in zip(path, sub_decode_path):
3641 if (p1 != any) and (p1 != p2):
3647 def abs_decode_path(decode_path, rel_path):
3648 """Create an absolute decode path from current and relative ones
3650 :param decode_path: current decode path, starting point.
3652 :param rel_path: relative path to ``decode_path``. Tuple of strings.
3653 If first tuple's element is "/", then treat it as
3654 an absolute path, ignoring ``decode_path`` as
3655 starting point. Also this tuple can contain ".."
3656 elements, stripping the leading element from
3659 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
3660 ("foo", "bar", "baz", "whatever")
3661 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
3663 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
3666 if rel_path[0] == "/":
3668 if rel_path[0] == "..":
3669 return abs_decode_path(decode_path[:-1], rel_path[1:])
3670 return decode_path + rel_path
3673 class Sequence(Obj):
3674 """``SEQUENCE`` structure type
3676 You have to make specification of sequence::
3678 class Extension(Sequence):
3680 ("extnID", ObjectIdentifier()),
3681 ("critical", Boolean(default=False)),
3682 ("extnValue", OctetString()),
3685 Then, you can work with it as with dictionary.
3687 >>> ext = Extension()
3688 >>> Extension().specs
3690 ('extnID', OBJECT IDENTIFIER),
3691 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3692 ('extnValue', OCTET STRING),
3694 >>> ext["extnID"] = "1.2.3"
3695 Traceback (most recent call last):
3696 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3697 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3699 You can know if sequence is ready to be encoded:
3704 Traceback (most recent call last):
3705 pyderasn.ObjNotReady: object is not ready: extnValue
3706 >>> ext["extnValue"] = OctetString(b"foobar")
3710 Value you want to assign, must have the same **type** as in
3711 corresponding specification, but it can have different tags,
3712 optional/default attributes -- they will be taken from specification
3715 class TBSCertificate(Sequence):
3717 ("version", Version(expl=tag_ctxc(0), default="v1")),
3720 >>> tbs = TBSCertificate()
3721 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3723 Assign ``None`` to remove value from sequence.
3725 You can know if value exists/set in the sequence and take its value:
3727 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3730 OBJECT IDENTIFIER 1.2.3
3732 But pay attention that if value has default, then it won't be (not
3733 in) in the sequence (because ``DEFAULT`` must not be encoded in
3734 DER), but you can read its value:
3736 >>> "critical" in ext, ext["critical"]
3737 (False, BOOLEAN False)
3738 >>> ext["critical"] = Boolean(True)
3739 >>> "critical" in ext, ext["critical"]
3740 (True, BOOLEAN True)
3742 All defaulted values are always optional.
3744 .. _strict_default_existence_ctx:
3748 When decoded DER contains defaulted value inside, then
3749 technically this is not valid DER encoding. But we allow and pass
3750 it **by default**. Of course reencoding of that kind of DER will
3751 result in different binary representation (validly without
3752 defaulted value inside). You can enable strict defaulted values
3753 existence validation by setting ``"strict_default_existence":
3754 True`` :ref:`context <ctx>` option -- decoding process will raise
3755 an exception if defaulted value is met.
3757 Two sequences are equal if they have equal specification (schema),
3758 implicit/explicit tagging and the same values.
3760 __slots__ = ("specs",)
3761 tag_default = tag_encode(form=TagFormConstructed, num=16)
3762 asn1_type_name = "SEQUENCE"
3774 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3776 schema = getattr(self, "schema", ())
3778 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3781 if value is not None:
3782 self._value = self._value_sanitize(value)
3783 if default is not None:
3784 default_value = self._value_sanitize(default)
3785 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3786 default_obj.specs = self.specs
3787 default_obj._value = default_value
3788 self.default = default_obj
3790 self._value = default_obj.copy()._value
3792 def _value_sanitize(self, value):
3793 if not issubclass(value.__class__, Sequence):
3794 raise InvalidValueType((Sequence,))
3799 for name, spec in self.specs.items():
3800 value = self._value.get(name)
3811 obj = self.__class__(schema=self.specs)
3813 obj._expl = self._expl
3814 obj.default = self.default
3815 obj.optional = self.optional
3816 obj.offset = self.offset
3817 obj.llen = self.llen
3818 obj.vlen = self.vlen
3819 obj._value = {k: v.copy() for k, v in self._value.items()}
3822 def __eq__(self, their):
3823 if not isinstance(their, self.__class__):
3826 self.specs == their.specs and
3827 self.tag == their.tag and
3828 self._expl == their._expl and
3829 self._value == their._value
3840 return self.__class__(
3843 impl=self.tag if impl is None else impl,
3844 expl=self._expl if expl is None else expl,
3845 default=self.default if default is None else default,
3846 optional=self.optional if optional is None else optional,
3849 def __contains__(self, key):
3850 return key in self._value
3852 def __setitem__(self, key, value):
3853 spec = self.specs.get(key)
3855 raise ObjUnknown(key)
3857 self._value.pop(key, None)
3859 if not isinstance(value, spec.__class__):
3860 raise InvalidValueType((spec.__class__,))
3861 value = spec(value=value)
3862 if spec.default is not None and value == spec.default:
3863 self._value.pop(key, None)
3865 self._value[key] = value
3867 def __getitem__(self, key):
3868 value = self._value.get(key)
3869 if value is not None:
3871 spec = self.specs.get(key)
3873 raise ObjUnknown(key)
3874 if spec.default is not None:
3878 def _encoded_values(self):
3880 for name, spec in self.specs.items():
3881 value = self._value.get(name)
3885 raise ObjNotReady(name)
3886 raws.append(value.encode())
3890 v = b"".join(self._encoded_values())
3891 return b"".join((self.tag, len_encode(len(v)), v))
3893 def _decode(self, tlv, offset, decode_path, ctx):
3895 t, tlen, lv = tag_strip(tlv)
3896 except DecodeError as err:
3897 raise err.__class__(
3899 klass=self.__class__,
3900 decode_path=decode_path,
3905 klass=self.__class__,
3906 decode_path=decode_path,
3910 l, llen, v = len_decode(lv)
3911 except DecodeError as err:
3912 raise err.__class__(
3914 klass=self.__class__,
3915 decode_path=decode_path,
3919 raise NotEnoughData(
3920 "encoded length is longer than data",
3921 klass=self.__class__,
3922 decode_path=decode_path,
3925 v, tail = v[:l], v[l:]
3926 sub_offset = offset + tlen + llen
3928 for name, spec in self.specs.items():
3929 if len(v) == 0 and spec.optional:
3931 sub_decode_path = decode_path + (name,)
3933 value, v_tail = spec.decode(
3937 decode_path=sub_decode_path,
3945 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
3946 if defined is not None:
3947 defined_by, defined_spec = defined
3948 if issubclass(value.__class__, SequenceOf):
3949 for i, _value in enumerate(value):
3950 sub_sub_decode_path = sub_decode_path + (
3952 DecodePathDefBy(defined_by),
3954 defined_value, defined_tail = defined_spec.decode(
3955 memoryview(bytes(_value)),
3957 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
3958 if value.expled else (value.tlen + value.llen)
3961 decode_path=sub_sub_decode_path,
3964 if len(defined_tail) > 0:
3967 klass=self.__class__,
3968 decode_path=sub_sub_decode_path,
3971 _value.defined = (defined_by, defined_value)
3973 defined_value, defined_tail = defined_spec.decode(
3974 memoryview(bytes(value)),
3976 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
3977 if value.expled else (value.tlen + value.llen)
3980 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
3983 if len(defined_tail) > 0:
3986 klass=self.__class__,
3987 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
3990 value.defined = (defined_by, defined_value)
3992 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3994 if spec.default is not None and value == spec.default:
3995 if ctx.get("strict_default_existence", False):
3997 "DEFAULT value met",
3998 klass=self.__class__,
3999 decode_path=sub_decode_path,
4004 values[name] = value
4006 spec_defines = getattr(spec, "defines", ())
4007 if len(spec_defines) == 0:
4008 defines_by_path = ctx.get("defines_by_path", ())
4009 if len(defines_by_path) > 0:
4010 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4011 if spec_defines is not None and len(spec_defines) > 0:
4012 for rel_path, schema in spec_defines:
4013 defined = schema.get(value, None)
4014 if defined is not None:
4015 ctx.setdefault("defines", []).append((
4016 abs_decode_path(sub_decode_path[:-1], rel_path),
4022 klass=self.__class__,
4023 decode_path=decode_path,
4026 obj = self.__class__(
4030 default=self.default,
4031 optional=self.optional,
4032 _decoded=(offset, llen, l),
4038 value = pp_console_row(next(self.pps()))
4040 for name in self.specs:
4041 _value = self._value.get(name)
4044 cols.append(repr(_value))
4045 return "%s[%s]" % (value, ", ".join(cols))
4047 def pps(self, decode_path=()):
4049 asn1_type_name=self.asn1_type_name,
4050 obj_name=self.__class__.__name__,
4051 decode_path=decode_path,
4052 optional=self.optional,
4053 default=self == self.default,
4054 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4055 expl=None if self._expl is None else tag_decode(self._expl),
4060 expl_offset=self.expl_offset if self.expled else None,
4061 expl_tlen=self.expl_tlen if self.expled else None,
4062 expl_llen=self.expl_llen if self.expled else None,
4063 expl_vlen=self.expl_vlen if self.expled else None,
4065 for name in self.specs:
4066 value = self._value.get(name)
4069 yield value.pps(decode_path=decode_path + (name,))
4072 class Set(Sequence):
4073 """``SET`` structure type
4075 Its usage is identical to :py:class:`pyderasn.Sequence`.
4078 tag_default = tag_encode(form=TagFormConstructed, num=17)
4079 asn1_type_name = "SET"
4082 raws = self._encoded_values()
4085 return b"".join((self.tag, len_encode(len(v)), v))
4087 def _decode(self, tlv, offset, decode_path, ctx):
4089 t, tlen, lv = tag_strip(tlv)
4090 except DecodeError as err:
4091 raise err.__class__(
4093 klass=self.__class__,
4094 decode_path=decode_path,
4099 klass=self.__class__,
4100 decode_path=decode_path,
4104 l, llen, v = len_decode(lv)
4105 except DecodeError as err:
4106 raise err.__class__(
4108 klass=self.__class__,
4109 decode_path=decode_path,
4113 raise NotEnoughData(
4114 "encoded length is longer than data",
4115 klass=self.__class__,
4118 v, tail = v[:l], v[l:]
4119 sub_offset = offset + tlen + llen
4121 specs_items = self.specs.items
4123 for name, spec in specs_items():
4125 value, v_tail = spec.decode(
4129 decode_path=decode_path + (name,),
4135 value.expl_tlvlen if value.expled else value.tlvlen
4138 if spec.default is None or value != spec.default: # pragma: no cover
4139 # SeqMixing.test_encoded_default_accepted covers that place
4140 values[name] = value
4144 klass=self.__class__,
4145 decode_path=decode_path,
4148 obj = self.__class__(
4152 default=self.default,
4153 optional=self.optional,
4154 _decoded=(offset, llen, l),
4160 class SequenceOf(Obj):
4161 """``SEQUENCE OF`` sequence type
4163 For that kind of type you must specify the object it will carry on
4164 (bounds are for example here, not required)::
4166 class Ints(SequenceOf):
4171 >>> ints.append(Integer(123))
4172 >>> ints.append(Integer(234))
4174 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4175 >>> [int(i) for i in ints]
4177 >>> ints.append(Integer(345))
4178 Traceback (most recent call last):
4179 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4182 >>> ints[1] = Integer(345)
4184 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4186 Also you can initialize sequence with preinitialized values:
4188 >>> ints = Ints([Integer(123), Integer(234)])
4190 __slots__ = ("spec", "_bound_min", "_bound_max")
4191 tag_default = tag_encode(form=TagFormConstructed, num=16)
4192 asn1_type_name = "SEQUENCE OF"
4205 super(SequenceOf, self).__init__(
4213 schema = getattr(self, "schema", None)
4215 raise ValueError("schema must be specified")
4217 self._bound_min, self._bound_max = getattr(
4221 ) if bounds is None else bounds
4223 if value is not None:
4224 self._value = self._value_sanitize(value)
4225 if default is not None:
4226 default_value = self._value_sanitize(default)
4227 default_obj = self.__class__(
4232 default_obj._value = default_value
4233 self.default = default_obj
4235 self._value = default_obj.copy()._value
4237 def _value_sanitize(self, value):
4238 if issubclass(value.__class__, SequenceOf):
4239 value = value._value
4240 elif hasattr(value, "__iter__"):
4243 raise InvalidValueType((self.__class__, iter))
4244 if not self._bound_min <= len(value) <= self._bound_max:
4245 raise BoundsError(self._bound_min, len(value), self._bound_max)
4247 if not isinstance(v, self.spec.__class__):
4248 raise InvalidValueType((self.spec.__class__,))
4253 return all(v.ready for v in self._value)
4256 obj = self.__class__(schema=self.spec)
4257 obj._bound_min = self._bound_min
4258 obj._bound_max = self._bound_max
4260 obj._expl = self._expl
4261 obj.default = self.default
4262 obj.optional = self.optional
4263 obj.offset = self.offset
4264 obj.llen = self.llen
4265 obj.vlen = self.vlen
4266 obj._value = [v.copy() for v in self._value]
4269 def __eq__(self, their):
4270 if isinstance(their, self.__class__):
4272 self.spec == their.spec and
4273 self.tag == their.tag and
4274 self._expl == their._expl and
4275 self._value == their._value
4277 if hasattr(their, "__iter__"):
4278 return self._value == list(their)
4290 return self.__class__(
4294 (self._bound_min, self._bound_max)
4295 if bounds is None else bounds
4297 impl=self.tag if impl is None else impl,
4298 expl=self._expl if expl is None else expl,
4299 default=self.default if default is None else default,
4300 optional=self.optional if optional is None else optional,
4303 def __contains__(self, key):
4304 return key in self._value
4306 def append(self, value):
4307 if not isinstance(value, self.spec.__class__):
4308 raise InvalidValueType((self.spec.__class__,))
4309 if len(self._value) + 1 > self._bound_max:
4312 len(self._value) + 1,
4315 self._value.append(value)
4318 self._assert_ready()
4319 return iter(self._value)
4322 self._assert_ready()
4323 return len(self._value)
4325 def __setitem__(self, key, value):
4326 if not isinstance(value, self.spec.__class__):
4327 raise InvalidValueType((self.spec.__class__,))
4328 self._value[key] = self.spec(value=value)
4330 def __getitem__(self, key):
4331 return self._value[key]
4333 def _encoded_values(self):
4334 return [v.encode() for v in self._value]
4337 v = b"".join(self._encoded_values())
4338 return b"".join((self.tag, len_encode(len(v)), v))
4340 def _decode(self, tlv, offset, decode_path, ctx):
4342 t, tlen, lv = tag_strip(tlv)
4343 except DecodeError as err:
4344 raise err.__class__(
4346 klass=self.__class__,
4347 decode_path=decode_path,
4352 klass=self.__class__,
4353 decode_path=decode_path,
4357 l, llen, v = len_decode(lv)
4358 except DecodeError as err:
4359 raise err.__class__(
4361 klass=self.__class__,
4362 decode_path=decode_path,
4366 raise NotEnoughData(
4367 "encoded length is longer than data",
4368 klass=self.__class__,
4369 decode_path=decode_path,
4372 v, tail = v[:l], v[l:]
4373 sub_offset = offset + tlen + llen
4377 value, v_tail = spec.decode(
4381 decode_path=decode_path + (str(len(_value)),),
4384 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4386 _value.append(value)
4387 obj = self.__class__(
4390 bounds=(self._bound_min, self._bound_max),
4393 default=self.default,
4394 optional=self.optional,
4395 _decoded=(offset, llen, l),
4401 pp_console_row(next(self.pps())),
4402 ", ".join(repr(v) for v in self._value),
4405 def pps(self, decode_path=()):
4407 asn1_type_name=self.asn1_type_name,
4408 obj_name=self.__class__.__name__,
4409 decode_path=decode_path,
4410 optional=self.optional,
4411 default=self == self.default,
4412 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4413 expl=None if self._expl is None else tag_decode(self._expl),
4418 expl_offset=self.expl_offset if self.expled else None,
4419 expl_tlen=self.expl_tlen if self.expled else None,
4420 expl_llen=self.expl_llen if self.expled else None,
4421 expl_vlen=self.expl_vlen if self.expled else None,
4423 for i, value in enumerate(self._value):
4424 yield value.pps(decode_path=decode_path + (str(i),))
4427 class SetOf(SequenceOf):
4428 """``SET OF`` sequence type
4430 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4433 tag_default = tag_encode(form=TagFormConstructed, num=17)
4434 asn1_type_name = "SET OF"
4437 raws = self._encoded_values()
4440 return b"".join((self.tag, len_encode(len(v)), v))
4443 def obj_by_path(pypath): # pragma: no cover
4444 """Import object specified as string Python path
4446 Modules must be separated from classes/functions with ``:``.
4448 >>> obj_by_path("foo.bar:Baz")
4449 <class 'foo.bar.Baz'>
4450 >>> obj_by_path("foo.bar:Baz.boo")
4451 <classmethod 'foo.bar.Baz.boo'>
4453 mod, objs = pypath.rsplit(":", 1)
4454 from importlib import import_module
4455 obj = import_module(mod)
4456 for obj_name in objs.split("."):
4457 obj = getattr(obj, obj_name)
4461 def generic_decoder(): # pragma: no cover
4462 # All of this below is a big hack with self references
4463 choice = PrimitiveTypes()
4464 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4465 choice.specs["SetOf"] = SetOf(schema=choice)
4467 choice.specs["SequenceOf%d" % i] = SequenceOf(
4471 choice.specs["Any"] = Any()
4473 # Class name equals to type name, to omit it from output
4474 class SEQUENCEOF(SequenceOf):
4478 def pprint_any(obj, oids=None, with_colours=False):
4479 def _pprint_pps(pps):
4481 if hasattr(pp, "_fields"):
4482 if pp.asn1_type_name == Choice.asn1_type_name:
4484 pp_kwargs = pp._asdict()
4485 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4486 pp = _pp(**pp_kwargs)
4487 yield pp_console_row(
4492 with_colours=with_colours,
4494 for row in pp_console_blob(pp):
4497 for row in _pprint_pps(pp):
4499 return "\n".join(_pprint_pps(obj.pps()))
4500 return SEQUENCEOF(), pprint_any
4503 def main(): # pragma: no cover
4505 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4506 parser.add_argument(
4510 help="Skip that number of bytes from the beginning",
4512 parser.add_argument(
4514 help="Python path to dictionary with OIDs",
4516 parser.add_argument(
4518 help="Python path to schema definition to use",
4520 parser.add_argument(
4521 "--defines-by-path",
4522 help="Python path to decoder's defines_by_path",
4524 parser.add_argument(
4526 type=argparse.FileType("rb"),
4527 help="Path to DER file you want to decode",
4529 args = parser.parse_args()
4530 args.DERFile.seek(args.skip)
4531 der = memoryview(args.DERFile.read())
4532 args.DERFile.close()
4533 oids = obj_by_path(args.oids) if args.oids else {}
4535 schema = obj_by_path(args.schema)
4536 from functools import partial
4537 pprinter = partial(pprint, big_blobs=True)
4539 schema, pprinter = generic_decoder()
4540 obj, tail = schema().decode(
4543 None if args.defines_by_path is None else
4544 {"defines_by_path": obj_by_path(args.defines_by_path)}
4550 with_colours=True if environ.get("NO_COLOR") is None else False,
4553 print("\nTrailing data: %s" % hexenc(tail))
4556 if __name__ == "__main__":