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, tag_only): # pragma: no cover
916 raise NotImplementedError()
920 if self._expl is None:
922 return b"".join((self._expl, len_encode(len(raw)), raw))
935 :param data: either binary or memoryview
936 :param int offset: initial data's offset
937 :param bool leavemm: do we need to leave memoryview of remaining
938 data as is, or convert it to bytes otherwise
939 :param ctx: optional :ref:`context <ctx>` governing decoding process.
940 :param tag_only: decode only the tag, without length and contents
941 (used only in Choice and Set structures, trying to
942 determine if tag satisfies the scheme)
943 :returns: (Obj, remaining data)
947 tlv = memoryview(data)
948 if self._expl is None:
949 result = self._decode(
952 decode_path=decode_path,
961 t, tlen, lv = tag_strip(tlv)
962 except DecodeError as err:
965 klass=self.__class__,
966 decode_path=decode_path,
971 klass=self.__class__,
972 decode_path=decode_path,
976 l, llen, v = len_decode(lv)
977 except DecodeError as err:
980 klass=self.__class__,
981 decode_path=decode_path,
986 "encoded length is longer than data",
987 klass=self.__class__,
988 decode_path=decode_path,
991 result = self._decode(
993 offset=offset + tlen + llen,
994 decode_path=decode_path,
1001 return obj, (tail if leavemm else tail.tobytes())
1005 return self._expl is not None
1012 def expl_tlen(self):
1013 return len(self._expl)
1016 def expl_llen(self):
1017 return len(len_encode(self.tlvlen))
1020 def expl_offset(self):
1021 return self.offset - self.expl_tlen - self.expl_llen
1024 def expl_vlen(self):
1028 def expl_tlvlen(self):
1029 return self.expl_tlen + self.expl_llen + self.expl_vlen
1032 class DecodePathDefBy(object):
1033 """DEFINED BY representation inside decode path
1035 __slots__ = ("defined_by",)
1037 def __init__(self, defined_by):
1038 self.defined_by = defined_by
1040 def __ne__(self, their):
1041 return not(self == their)
1043 def __eq__(self, their):
1044 if not isinstance(their, self.__class__):
1046 return self.defined_by == their.defined_by
1049 return "DEFINED BY " + str(self.defined_by)
1052 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1055 ########################################################################
1057 ########################################################################
1059 PP = namedtuple("PP", (
1081 asn1_type_name="unknown",
1120 def _colorize(what, colour, with_colours, attrs=("bold",)):
1121 return colored(what, colour, attrs=attrs) if with_colours else what
1136 " " if pp.expl_offset is None else
1137 ("-%d" % (pp.offset - pp.expl_offset))
1140 cols.append(_colorize(col, "red", with_colours, ()))
1141 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1142 cols.append(_colorize(col, "green", with_colours, ()))
1143 if len(pp.decode_path) > 0:
1144 cols.append(" ." * (len(pp.decode_path)))
1145 ent = pp.decode_path[-1]
1146 if isinstance(ent, DecodePathDefBy):
1147 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1148 value = str(ent.defined_by)
1150 oids is not None and
1151 ent.defined_by.asn1_type_name ==
1152 ObjectIdentifier.asn1_type_name and
1155 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1157 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1159 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1160 if pp.expl is not None:
1161 klass, _, num = pp.expl
1162 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1163 cols.append(_colorize(col, "blue", with_colours))
1164 if pp.impl is not None:
1165 klass, _, num = pp.impl
1166 col = "[%s%d]" % (TagClassReprs[klass], num)
1167 cols.append(_colorize(col, "blue", with_colours))
1168 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1169 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1170 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1171 if pp.value is not None:
1173 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1175 oids is not None and
1176 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1179 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1181 if isinstance(pp.blob, binary_type):
1182 cols.append(hexenc(pp.blob))
1183 elif isinstance(pp.blob, tuple):
1184 cols.append(", ".join(pp.blob))
1186 cols.append(_colorize("OPTIONAL", "red", with_colours))
1188 cols.append(_colorize("DEFAULT", "red", with_colours))
1189 return " ".join(cols)
1192 def pp_console_blob(pp):
1193 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
1194 if len(pp.decode_path) > 0:
1195 cols.append(" ." * (len(pp.decode_path) + 1))
1196 if isinstance(pp.blob, binary_type):
1197 blob = hexenc(pp.blob).upper()
1198 for i in range(0, len(blob), 32):
1199 chunk = blob[i:i + 32]
1200 yield " ".join(cols + [":".join(
1201 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1203 elif isinstance(pp.blob, tuple):
1204 yield " ".join(cols + [", ".join(pp.blob)])
1207 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1208 """Pretty print object
1210 :param Obj obj: object you want to pretty print
1211 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1212 from it is met, then its humand readable form is printed
1213 :param big_blobs: if large binary objects are met (like OctetString
1214 values), do we need to print them too, on separate
1216 :param with_colours: colourize output, if ``termcolor`` library
1219 def _pprint_pps(pps):
1221 if hasattr(pp, "_fields"):
1223 yield pp_console_row(
1228 with_colours=with_colours,
1230 for row in pp_console_blob(pp):
1233 yield pp_console_row(
1238 with_colours=with_colours,
1241 for row in _pprint_pps(pp):
1243 return "\n".join(_pprint_pps(obj.pps()))
1246 ########################################################################
1247 # ASN.1 primitive types
1248 ########################################################################
1251 """``BOOLEAN`` boolean type
1253 >>> b = Boolean(True)
1255 >>> b == Boolean(True)
1261 tag_default = tag_encode(1)
1262 asn1_type_name = "BOOLEAN"
1274 :param value: set the value. Either boolean type, or
1275 :py:class:`pyderasn.Boolean` object
1276 :param bytes impl: override default tag with ``IMPLICIT`` one
1277 :param bytes expl: override default tag with ``EXPLICIT`` one
1278 :param default: set default value. Type same as in ``value``
1279 :param bool optional: is object ``OPTIONAL`` in sequence
1281 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1282 self._value = None if value is None else self._value_sanitize(value)
1283 if default is not None:
1284 default = self._value_sanitize(default)
1285 self.default = self.__class__(
1291 self._value = default
1293 def _value_sanitize(self, value):
1294 if issubclass(value.__class__, Boolean):
1296 if isinstance(value, bool):
1298 raise InvalidValueType((self.__class__, bool))
1302 return self._value is not None
1305 obj = self.__class__()
1306 obj._value = self._value
1308 obj._expl = self._expl
1309 obj.default = self.default
1310 obj.optional = self.optional
1311 obj.offset = self.offset
1312 obj.llen = self.llen
1313 obj.vlen = self.vlen
1316 def __nonzero__(self):
1317 self._assert_ready()
1321 self._assert_ready()
1324 def __eq__(self, their):
1325 if isinstance(their, bool):
1326 return self._value == their
1327 if not issubclass(their.__class__, Boolean):
1330 self._value == their._value and
1331 self.tag == their.tag and
1332 self._expl == their._expl
1343 return self.__class__(
1345 impl=self.tag if impl is None else impl,
1346 expl=self._expl if expl is None else expl,
1347 default=self.default if default is None else default,
1348 optional=self.optional if optional is None else optional,
1352 self._assert_ready()
1356 (b"\xFF" if self._value else b"\x00"),
1359 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1361 t, _, lv = tag_strip(tlv)
1362 except DecodeError as err:
1363 raise err.__class__(
1365 klass=self.__class__,
1366 decode_path=decode_path,
1371 klass=self.__class__,
1372 decode_path=decode_path,
1378 l, _, v = len_decode(lv)
1379 except DecodeError as err:
1380 raise err.__class__(
1382 klass=self.__class__,
1383 decode_path=decode_path,
1387 raise InvalidLength(
1388 "Boolean's length must be equal to 1",
1389 klass=self.__class__,
1390 decode_path=decode_path,
1394 raise NotEnoughData(
1395 "encoded length is longer than data",
1396 klass=self.__class__,
1397 decode_path=decode_path,
1400 first_octet = byte2int(v)
1401 if first_octet == 0:
1403 elif first_octet == 0xFF:
1407 "unacceptable Boolean value",
1408 klass=self.__class__,
1409 decode_path=decode_path,
1412 obj = self.__class__(
1416 default=self.default,
1417 optional=self.optional,
1418 _decoded=(offset, 1, 1),
1423 return pp_console_row(next(self.pps()))
1425 def pps(self, decode_path=()):
1427 asn1_type_name=self.asn1_type_name,
1428 obj_name=self.__class__.__name__,
1429 decode_path=decode_path,
1430 value=str(self._value) if self.ready else None,
1431 optional=self.optional,
1432 default=self == self.default,
1433 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1434 expl=None if self._expl is None else tag_decode(self._expl),
1439 expl_offset=self.expl_offset if self.expled else None,
1440 expl_tlen=self.expl_tlen if self.expled else None,
1441 expl_llen=self.expl_llen if self.expled else None,
1442 expl_vlen=self.expl_vlen if self.expled else None,
1447 """``INTEGER`` integer type
1449 >>> b = Integer(-123)
1451 >>> b == Integer(-123)
1456 >>> Integer(2, bounds=(1, 3))
1458 >>> Integer(5, bounds=(1, 3))
1459 Traceback (most recent call last):
1460 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1464 class Version(Integer):
1471 >>> v = Version("v1")
1478 {'v3': 2, 'v1': 0, 'v2': 1}
1480 __slots__ = ("specs", "_bound_min", "_bound_max")
1481 tag_default = tag_encode(2)
1482 asn1_type_name = "INTEGER"
1496 :param value: set the value. Either integer type, named value
1497 (if ``schema`` is specified in the class), or
1498 :py:class:`pyderasn.Integer` object
1499 :param bounds: set ``(MIN, MAX)`` value constraint.
1500 (-inf, +inf) by default
1501 :param bytes impl: override default tag with ``IMPLICIT`` one
1502 :param bytes expl: override default tag with ``EXPLICIT`` one
1503 :param default: set default value. Type same as in ``value``
1504 :param bool optional: is object ``OPTIONAL`` in sequence
1506 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1508 specs = getattr(self, "schema", {}) if _specs is None else _specs
1509 self.specs = specs if isinstance(specs, dict) else dict(specs)
1510 self._bound_min, self._bound_max = getattr(
1513 (float("-inf"), float("+inf")),
1514 ) if bounds is None else bounds
1515 if value is not None:
1516 self._value = self._value_sanitize(value)
1517 if default is not None:
1518 default = self._value_sanitize(default)
1519 self.default = self.__class__(
1525 if self._value is None:
1526 self._value = default
1528 def _value_sanitize(self, value):
1529 if issubclass(value.__class__, Integer):
1530 value = value._value
1531 elif isinstance(value, integer_types):
1533 elif isinstance(value, str):
1534 value = self.specs.get(value)
1536 raise ObjUnknown("integer value: %s" % value)
1538 raise InvalidValueType((self.__class__, int, str))
1539 if not self._bound_min <= value <= self._bound_max:
1540 raise BoundsError(self._bound_min, value, self._bound_max)
1545 return self._value is not None
1548 obj = self.__class__(_specs=self.specs)
1549 obj._value = self._value
1550 obj._bound_min = self._bound_min
1551 obj._bound_max = self._bound_max
1553 obj._expl = self._expl
1554 obj.default = self.default
1555 obj.optional = self.optional
1556 obj.offset = self.offset
1557 obj.llen = self.llen
1558 obj.vlen = self.vlen
1562 self._assert_ready()
1563 return int(self._value)
1566 self._assert_ready()
1569 bytes(self._expl or b"") +
1570 str(self._value).encode("ascii"),
1573 def __eq__(self, their):
1574 if isinstance(their, integer_types):
1575 return self._value == their
1576 if not issubclass(their.__class__, Integer):
1579 self._value == their._value and
1580 self.tag == their.tag and
1581 self._expl == their._expl
1584 def __lt__(self, their):
1585 return self._value < their._value
1589 for name, value in self.specs.items():
1590 if value == self._value:
1602 return self.__class__(
1605 (self._bound_min, self._bound_max)
1606 if bounds is None else bounds
1608 impl=self.tag if impl is None else impl,
1609 expl=self._expl if expl is None else expl,
1610 default=self.default if default is None else default,
1611 optional=self.optional if optional is None else optional,
1616 self._assert_ready()
1620 octets = bytearray([0])
1624 octets = bytearray()
1626 octets.append((value & 0xFF) ^ 0xFF)
1628 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1631 octets = bytearray()
1633 octets.append(value & 0xFF)
1635 if octets[-1] & 0x80 > 0:
1638 octets = bytes(octets)
1640 bytes_len = ceil(value.bit_length() / 8) or 1
1643 octets = value.to_bytes(
1648 except OverflowError:
1652 return b"".join((self.tag, len_encode(len(octets)), octets))
1654 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1656 t, _, lv = tag_strip(tlv)
1657 except DecodeError as err:
1658 raise err.__class__(
1660 klass=self.__class__,
1661 decode_path=decode_path,
1666 klass=self.__class__,
1667 decode_path=decode_path,
1673 l, llen, v = len_decode(lv)
1674 except DecodeError as err:
1675 raise err.__class__(
1677 klass=self.__class__,
1678 decode_path=decode_path,
1682 raise NotEnoughData(
1683 "encoded length is longer than data",
1684 klass=self.__class__,
1685 decode_path=decode_path,
1689 raise NotEnoughData(
1691 klass=self.__class__,
1692 decode_path=decode_path,
1695 v, tail = v[:l], v[l:]
1696 first_octet = byte2int(v)
1698 second_octet = byte2int(v[1:])
1700 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1701 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1704 "non normalized integer",
1705 klass=self.__class__,
1706 decode_path=decode_path,
1711 if first_octet & 0x80 > 0:
1712 octets = bytearray()
1713 for octet in bytearray(v):
1714 octets.append(octet ^ 0xFF)
1715 for octet in octets:
1716 value = (value << 8) | octet
1720 for octet in bytearray(v):
1721 value = (value << 8) | octet
1723 value = int.from_bytes(v, byteorder="big", signed=True)
1725 obj = self.__class__(
1727 bounds=(self._bound_min, self._bound_max),
1730 default=self.default,
1731 optional=self.optional,
1733 _decoded=(offset, llen, l),
1735 except BoundsError as err:
1738 klass=self.__class__,
1739 decode_path=decode_path,
1745 return pp_console_row(next(self.pps()))
1747 def pps(self, decode_path=()):
1749 asn1_type_name=self.asn1_type_name,
1750 obj_name=self.__class__.__name__,
1751 decode_path=decode_path,
1752 value=(self.named or str(self._value)) if self.ready else None,
1753 optional=self.optional,
1754 default=self == self.default,
1755 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1756 expl=None if self._expl is None else tag_decode(self._expl),
1761 expl_offset=self.expl_offset if self.expled else None,
1762 expl_tlen=self.expl_tlen if self.expled else None,
1763 expl_llen=self.expl_llen if self.expled else None,
1764 expl_vlen=self.expl_vlen if self.expled else None,
1768 class BitString(Obj):
1769 """``BIT STRING`` bit string type
1771 >>> BitString(b"hello world")
1772 BIT STRING 88 bits 68656c6c6f20776f726c64
1775 >>> b == b"hello world"
1780 >>> b = BitString("'010110000000'B")
1781 BIT STRING 12 bits 5800
1784 >>> b[0], b[1], b[2], b[3]
1785 (False, True, False, True)
1789 [False, True, False, True, True, False, False, False, False, False, False, False]
1793 class KeyUsage(BitString):
1795 ("digitalSignature", 0),
1796 ("nonRepudiation", 1),
1797 ("keyEncipherment", 2),
1800 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1801 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1803 ['nonRepudiation', 'keyEncipherment']
1805 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1807 __slots__ = ("specs", "defined")
1808 tag_default = tag_encode(3)
1809 asn1_type_name = "BIT STRING"
1822 :param value: set the value. Either binary type, tuple of named
1823 values (if ``schema`` is specified in the class),
1824 string in ``'XXX...'B`` form, or
1825 :py:class:`pyderasn.BitString` object
1826 :param bytes impl: override default tag with ``IMPLICIT`` one
1827 :param bytes expl: override default tag with ``EXPLICIT`` one
1828 :param default: set default value. Type same as in ``value``
1829 :param bool optional: is object ``OPTIONAL`` in sequence
1831 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1832 specs = getattr(self, "schema", {}) if _specs is None else _specs
1833 self.specs = specs if isinstance(specs, dict) else dict(specs)
1834 self._value = None if value is None else self._value_sanitize(value)
1835 if default is not None:
1836 default = self._value_sanitize(default)
1837 self.default = self.__class__(
1843 self._value = default
1846 def _bits2octets(self, bits):
1847 if len(self.specs) > 0:
1848 bits = bits.rstrip("0")
1850 bits += "0" * ((8 - (bit_len % 8)) % 8)
1851 octets = bytearray(len(bits) // 8)
1852 for i in six_xrange(len(octets)):
1853 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1854 return bit_len, bytes(octets)
1856 def _value_sanitize(self, value):
1857 if issubclass(value.__class__, BitString):
1859 if isinstance(value, (string_types, binary_type)):
1861 isinstance(value, string_types) and
1862 value.startswith("'") and
1863 value.endswith("'B")
1866 if not set(value) <= set(("0", "1")):
1867 raise ValueError("B's coding contains unacceptable chars")
1868 return self._bits2octets(value)
1869 elif isinstance(value, binary_type):
1870 return (len(value) * 8, value)
1872 raise InvalidValueType((
1877 if isinstance(value, tuple):
1880 isinstance(value[0], integer_types) and
1881 isinstance(value[1], binary_type)
1886 bit = self.specs.get(name)
1888 raise ObjUnknown("BitString value: %s" % name)
1891 return self._bits2octets("")
1893 return self._bits2octets("".join(
1894 ("1" if bit in bits else "0")
1895 for bit in six_xrange(max(bits) + 1)
1897 raise InvalidValueType((self.__class__, binary_type, string_types))
1901 return self._value is not None
1904 obj = self.__class__(_specs=self.specs)
1906 if value is not None:
1907 value = (value[0], value[1])
1910 obj._expl = self._expl
1911 obj.default = self.default
1912 obj.optional = self.optional
1913 obj.offset = self.offset
1914 obj.llen = self.llen
1915 obj.vlen = self.vlen
1919 self._assert_ready()
1920 for i in six_xrange(self._value[0]):
1925 self._assert_ready()
1926 return self._value[0]
1928 def __bytes__(self):
1929 self._assert_ready()
1930 return self._value[1]
1932 def __eq__(self, their):
1933 if isinstance(their, bytes):
1934 return self._value[1] == their
1935 if not issubclass(their.__class__, BitString):
1938 self._value == their._value and
1939 self.tag == their.tag and
1940 self._expl == their._expl
1945 return [name for name, bit in self.specs.items() if self[bit]]
1955 return self.__class__(
1957 impl=self.tag if impl is None else impl,
1958 expl=self._expl if expl is None else expl,
1959 default=self.default if default is None else default,
1960 optional=self.optional if optional is None else optional,
1964 def __getitem__(self, key):
1965 if isinstance(key, int):
1966 bit_len, octets = self._value
1970 byte2int(memoryview(octets)[key // 8:]) >>
1973 if isinstance(key, string_types):
1974 value = self.specs.get(key)
1976 raise ObjUnknown("BitString value: %s" % key)
1978 raise InvalidValueType((int, str))
1981 self._assert_ready()
1982 bit_len, octets = self._value
1985 len_encode(len(octets) + 1),
1986 int2byte((8 - bit_len % 8) % 8),
1990 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1992 t, _, lv = tag_strip(tlv)
1993 except DecodeError as err:
1994 raise err.__class__(
1996 klass=self.__class__,
1997 decode_path=decode_path,
2002 klass=self.__class__,
2003 decode_path=decode_path,
2009 l, llen, v = len_decode(lv)
2010 except DecodeError as err:
2011 raise err.__class__(
2013 klass=self.__class__,
2014 decode_path=decode_path,
2018 raise NotEnoughData(
2019 "encoded length is longer than data",
2020 klass=self.__class__,
2021 decode_path=decode_path,
2025 raise NotEnoughData(
2027 klass=self.__class__,
2028 decode_path=decode_path,
2031 pad_size = byte2int(v)
2032 if l == 1 and pad_size != 0:
2034 "invalid empty value",
2035 klass=self.__class__,
2036 decode_path=decode_path,
2042 klass=self.__class__,
2043 decode_path=decode_path,
2046 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2049 klass=self.__class__,
2050 decode_path=decode_path,
2053 v, tail = v[:l], v[l:]
2054 obj = self.__class__(
2055 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2058 default=self.default,
2059 optional=self.optional,
2061 _decoded=(offset, llen, l),
2066 return pp_console_row(next(self.pps()))
2068 def pps(self, decode_path=()):
2072 bit_len, blob = self._value
2073 value = "%d bits" % bit_len
2074 if len(self.specs) > 0:
2075 blob = tuple(self.named)
2077 asn1_type_name=self.asn1_type_name,
2078 obj_name=self.__class__.__name__,
2079 decode_path=decode_path,
2082 optional=self.optional,
2083 default=self == self.default,
2084 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2085 expl=None if self._expl is None else tag_decode(self._expl),
2090 expl_offset=self.expl_offset if self.expled else None,
2091 expl_tlen=self.expl_tlen if self.expled else None,
2092 expl_llen=self.expl_llen if self.expled else None,
2093 expl_vlen=self.expl_vlen if self.expled else None,
2095 defined_by, defined = self.defined or (None, None)
2096 if defined_by is not None:
2098 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2102 class OctetString(Obj):
2103 """``OCTET STRING`` binary string type
2105 >>> s = OctetString(b"hello world")
2106 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2107 >>> s == OctetString(b"hello world")
2112 >>> OctetString(b"hello", bounds=(4, 4))
2113 Traceback (most recent call last):
2114 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2115 >>> OctetString(b"hell", bounds=(4, 4))
2116 OCTET STRING 4 bytes 68656c6c
2118 __slots__ = ("_bound_min", "_bound_max", "defined")
2119 tag_default = tag_encode(4)
2120 asn1_type_name = "OCTET STRING"
2133 :param value: set the value. Either binary type, or
2134 :py:class:`pyderasn.OctetString` object
2135 :param bounds: set ``(MIN, MAX)`` value size constraint.
2136 (-inf, +inf) by default
2137 :param bytes impl: override default tag with ``IMPLICIT`` one
2138 :param bytes expl: override default tag with ``EXPLICIT`` one
2139 :param default: set default value. Type same as in ``value``
2140 :param bool optional: is object ``OPTIONAL`` in sequence
2142 super(OctetString, self).__init__(
2150 self._bound_min, self._bound_max = getattr(
2154 ) if bounds is None else bounds
2155 if value is not None:
2156 self._value = self._value_sanitize(value)
2157 if default is not None:
2158 default = self._value_sanitize(default)
2159 self.default = self.__class__(
2164 if self._value is None:
2165 self._value = default
2168 def _value_sanitize(self, value):
2169 if issubclass(value.__class__, OctetString):
2170 value = value._value
2171 elif isinstance(value, binary_type):
2174 raise InvalidValueType((self.__class__, bytes))
2175 if not self._bound_min <= len(value) <= self._bound_max:
2176 raise BoundsError(self._bound_min, len(value), self._bound_max)
2181 return self._value is not None
2184 obj = self.__class__()
2185 obj._value = self._value
2186 obj._bound_min = self._bound_min
2187 obj._bound_max = self._bound_max
2189 obj._expl = self._expl
2190 obj.default = self.default
2191 obj.optional = self.optional
2192 obj.offset = self.offset
2193 obj.llen = self.llen
2194 obj.vlen = self.vlen
2197 def __bytes__(self):
2198 self._assert_ready()
2201 def __eq__(self, their):
2202 if isinstance(their, binary_type):
2203 return self._value == their
2204 if not issubclass(their.__class__, OctetString):
2207 self._value == their._value and
2208 self.tag == their.tag and
2209 self._expl == their._expl
2212 def __lt__(self, their):
2213 return self._value < their._value
2224 return self.__class__(
2227 (self._bound_min, self._bound_max)
2228 if bounds is None else bounds
2230 impl=self.tag if impl is None else impl,
2231 expl=self._expl if expl is None else expl,
2232 default=self.default if default is None else default,
2233 optional=self.optional if optional is None else optional,
2237 self._assert_ready()
2240 len_encode(len(self._value)),
2244 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2246 t, _, lv = tag_strip(tlv)
2247 except DecodeError as err:
2248 raise err.__class__(
2250 klass=self.__class__,
2251 decode_path=decode_path,
2256 klass=self.__class__,
2257 decode_path=decode_path,
2263 l, llen, v = len_decode(lv)
2264 except DecodeError as err:
2265 raise err.__class__(
2267 klass=self.__class__,
2268 decode_path=decode_path,
2272 raise NotEnoughData(
2273 "encoded length is longer than data",
2274 klass=self.__class__,
2275 decode_path=decode_path,
2278 v, tail = v[:l], v[l:]
2280 obj = self.__class__(
2282 bounds=(self._bound_min, self._bound_max),
2285 default=self.default,
2286 optional=self.optional,
2287 _decoded=(offset, llen, l),
2289 except DecodeError as err:
2292 klass=self.__class__,
2293 decode_path=decode_path,
2296 except BoundsError as err:
2299 klass=self.__class__,
2300 decode_path=decode_path,
2306 return pp_console_row(next(self.pps()))
2308 def pps(self, decode_path=()):
2310 asn1_type_name=self.asn1_type_name,
2311 obj_name=self.__class__.__name__,
2312 decode_path=decode_path,
2313 value=("%d bytes" % len(self._value)) if self.ready else None,
2314 blob=self._value if self.ready else None,
2315 optional=self.optional,
2316 default=self == self.default,
2317 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2318 expl=None if self._expl is None else tag_decode(self._expl),
2323 expl_offset=self.expl_offset if self.expled else None,
2324 expl_tlen=self.expl_tlen if self.expled else None,
2325 expl_llen=self.expl_llen if self.expled else None,
2326 expl_vlen=self.expl_vlen if self.expled else None,
2328 defined_by, defined = self.defined or (None, None)
2329 if defined_by is not None:
2331 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2336 """``NULL`` null object
2344 tag_default = tag_encode(5)
2345 asn1_type_name = "NULL"
2349 value=None, # unused, but Sequence passes it
2356 :param bytes impl: override default tag with ``IMPLICIT`` one
2357 :param bytes expl: override default tag with ``EXPLICIT`` one
2358 :param bool optional: is object ``OPTIONAL`` in sequence
2360 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2368 obj = self.__class__()
2370 obj._expl = self._expl
2371 obj.default = self.default
2372 obj.optional = self.optional
2373 obj.offset = self.offset
2374 obj.llen = self.llen
2375 obj.vlen = self.vlen
2378 def __eq__(self, their):
2379 if not issubclass(their.__class__, Null):
2382 self.tag == their.tag and
2383 self._expl == their._expl
2393 return self.__class__(
2394 impl=self.tag if impl is None else impl,
2395 expl=self._expl if expl is None else expl,
2396 optional=self.optional if optional is None else optional,
2400 return self.tag + len_encode(0)
2402 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2404 t, _, lv = tag_strip(tlv)
2405 except DecodeError as err:
2406 raise err.__class__(
2408 klass=self.__class__,
2409 decode_path=decode_path,
2414 klass=self.__class__,
2415 decode_path=decode_path,
2421 l, _, v = len_decode(lv)
2422 except DecodeError as err:
2423 raise err.__class__(
2425 klass=self.__class__,
2426 decode_path=decode_path,
2430 raise InvalidLength(
2431 "Null must have zero length",
2432 klass=self.__class__,
2433 decode_path=decode_path,
2436 obj = self.__class__(
2439 optional=self.optional,
2440 _decoded=(offset, 1, 0),
2445 return pp_console_row(next(self.pps()))
2447 def pps(self, decode_path=()):
2449 asn1_type_name=self.asn1_type_name,
2450 obj_name=self.__class__.__name__,
2451 decode_path=decode_path,
2452 optional=self.optional,
2453 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2454 expl=None if self._expl is None else tag_decode(self._expl),
2459 expl_offset=self.expl_offset if self.expled else None,
2460 expl_tlen=self.expl_tlen if self.expled else None,
2461 expl_llen=self.expl_llen if self.expled else None,
2462 expl_vlen=self.expl_vlen if self.expled else None,
2466 class ObjectIdentifier(Obj):
2467 """``OBJECT IDENTIFIER`` OID type
2469 >>> oid = ObjectIdentifier((1, 2, 3))
2470 OBJECT IDENTIFIER 1.2.3
2471 >>> oid == ObjectIdentifier("1.2.3")
2477 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2478 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2480 >>> str(ObjectIdentifier((3, 1)))
2481 Traceback (most recent call last):
2482 pyderasn.InvalidOID: unacceptable first arc value
2484 __slots__ = ("defines",)
2485 tag_default = tag_encode(6)
2486 asn1_type_name = "OBJECT IDENTIFIER"
2499 :param value: set the value. Either tuples of integers,
2500 string of "."-concatenated integers, or
2501 :py:class:`pyderasn.ObjectIdentifier` object
2502 :param defines: sequence of tuples. Each tuple has two elements.
2503 First one is relative to current one decode
2504 path, aiming to the field defined by that OID.
2505 Read about relative path in
2506 :py:func:`pyderasn.abs_decode_path`. Second
2507 tuple element is ``{OID: pyderasn.Obj()}``
2508 dictionary, mapping between current OID value
2509 and structure applied to defined field.
2510 :ref:`Read about DEFINED BY <definedby>`
2511 :param bytes impl: override default tag with ``IMPLICIT`` one
2512 :param bytes expl: override default tag with ``EXPLICIT`` one
2513 :param default: set default value. Type same as in ``value``
2514 :param bool optional: is object ``OPTIONAL`` in sequence
2516 super(ObjectIdentifier, self).__init__(
2524 if value is not None:
2525 self._value = self._value_sanitize(value)
2526 if default is not None:
2527 default = self._value_sanitize(default)
2528 self.default = self.__class__(
2533 if self._value is None:
2534 self._value = default
2535 self.defines = defines
2537 def __add__(self, their):
2538 if isinstance(their, self.__class__):
2539 return self.__class__(self._value + their._value)
2540 if isinstance(their, tuple):
2541 return self.__class__(self._value + their)
2542 raise InvalidValueType((self.__class__, tuple))
2544 def _value_sanitize(self, value):
2545 if issubclass(value.__class__, ObjectIdentifier):
2547 if isinstance(value, string_types):
2549 value = tuple(int(arc) for arc in value.split("."))
2551 raise InvalidOID("unacceptable arcs values")
2552 if isinstance(value, tuple):
2554 raise InvalidOID("less than 2 arcs")
2555 first_arc = value[0]
2556 if first_arc in (0, 1):
2557 if not (0 <= value[1] <= 39):
2558 raise InvalidOID("second arc is too wide")
2559 elif first_arc == 2:
2562 raise InvalidOID("unacceptable first arc value")
2564 raise InvalidValueType((self.__class__, str, tuple))
2568 return self._value is not None
2571 obj = self.__class__()
2572 obj._value = self._value
2573 obj.defines = self.defines
2575 obj._expl = self._expl
2576 obj.default = self.default
2577 obj.optional = self.optional
2578 obj.offset = self.offset
2579 obj.llen = self.llen
2580 obj.vlen = self.vlen
2584 self._assert_ready()
2585 return iter(self._value)
2588 return ".".join(str(arc) for arc in self._value or ())
2591 self._assert_ready()
2594 bytes(self._expl or b"") +
2595 str(self._value).encode("ascii"),
2598 def __eq__(self, their):
2599 if isinstance(their, tuple):
2600 return self._value == their
2601 if not issubclass(their.__class__, ObjectIdentifier):
2604 self.tag == their.tag and
2605 self._expl == their._expl and
2606 self._value == their._value
2609 def __lt__(self, their):
2610 return self._value < their._value
2621 return self.__class__(
2623 defines=self.defines if defines is None else defines,
2624 impl=self.tag if impl is None else impl,
2625 expl=self._expl if expl is None else expl,
2626 default=self.default if default is None else default,
2627 optional=self.optional if optional is None else optional,
2631 self._assert_ready()
2633 first_value = value[1]
2634 first_arc = value[0]
2637 elif first_arc == 1:
2639 elif first_arc == 2:
2641 else: # pragma: no cover
2642 raise RuntimeError("invalid arc is stored")
2643 octets = [zero_ended_encode(first_value)]
2644 for arc in value[2:]:
2645 octets.append(zero_ended_encode(arc))
2646 v = b"".join(octets)
2647 return b"".join((self.tag, len_encode(len(v)), v))
2649 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2651 t, _, lv = tag_strip(tlv)
2652 except DecodeError as err:
2653 raise err.__class__(
2655 klass=self.__class__,
2656 decode_path=decode_path,
2661 klass=self.__class__,
2662 decode_path=decode_path,
2668 l, llen, v = len_decode(lv)
2669 except DecodeError as err:
2670 raise err.__class__(
2672 klass=self.__class__,
2673 decode_path=decode_path,
2677 raise NotEnoughData(
2678 "encoded length is longer than data",
2679 klass=self.__class__,
2680 decode_path=decode_path,
2684 raise NotEnoughData(
2686 klass=self.__class__,
2687 decode_path=decode_path,
2690 v, tail = v[:l], v[l:]
2696 octet = indexbytes(v, i)
2697 arc = (arc << 7) | (octet & 0x7F)
2698 if octet & 0x80 == 0:
2706 klass=self.__class__,
2707 decode_path=decode_path,
2711 second_arc = arcs[0]
2712 if 0 <= second_arc <= 39:
2714 elif 40 <= second_arc <= 79:
2720 obj = self.__class__(
2721 value=tuple([first_arc, second_arc] + arcs[1:]),
2724 default=self.default,
2725 optional=self.optional,
2726 _decoded=(offset, llen, l),
2731 return pp_console_row(next(self.pps()))
2733 def pps(self, decode_path=()):
2735 asn1_type_name=self.asn1_type_name,
2736 obj_name=self.__class__.__name__,
2737 decode_path=decode_path,
2738 value=str(self) if self.ready else None,
2739 optional=self.optional,
2740 default=self == self.default,
2741 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2742 expl=None if self._expl is None else tag_decode(self._expl),
2747 expl_offset=self.expl_offset if self.expled else None,
2748 expl_tlen=self.expl_tlen if self.expled else None,
2749 expl_llen=self.expl_llen if self.expled else None,
2750 expl_vlen=self.expl_vlen if self.expled else None,
2754 class Enumerated(Integer):
2755 """``ENUMERATED`` integer type
2757 This type is identical to :py:class:`pyderasn.Integer`, but requires
2758 schema to be specified and does not accept values missing from it.
2761 tag_default = tag_encode(10)
2762 asn1_type_name = "ENUMERATED"
2773 bounds=None, # dummy argument, workability for Integer.decode
2775 super(Enumerated, self).__init__(
2784 if len(self.specs) == 0:
2785 raise ValueError("schema must be specified")
2787 def _value_sanitize(self, value):
2788 if isinstance(value, self.__class__):
2789 value = value._value
2790 elif isinstance(value, integer_types):
2791 if value not in list(self.specs.values()):
2793 "unknown integer value: %s" % value,
2794 klass=self.__class__,
2796 elif isinstance(value, string_types):
2797 value = self.specs.get(value)
2799 raise ObjUnknown("integer value: %s" % value)
2801 raise InvalidValueType((self.__class__, int, str))
2805 obj = self.__class__(_specs=self.specs)
2806 obj._value = self._value
2807 obj._bound_min = self._bound_min
2808 obj._bound_max = self._bound_max
2810 obj._expl = self._expl
2811 obj.default = self.default
2812 obj.optional = self.optional
2813 obj.offset = self.offset
2814 obj.llen = self.llen
2815 obj.vlen = self.vlen
2827 return self.__class__(
2829 impl=self.tag if impl is None else impl,
2830 expl=self._expl if expl is None else expl,
2831 default=self.default if default is None else default,
2832 optional=self.optional if optional is None else optional,
2837 class CommonString(OctetString):
2838 """Common class for all strings
2840 Everything resembles :py:class:`pyderasn.OctetString`, except
2841 ability to deal with unicode text strings.
2843 >>> hexenc("привет мир".encode("utf-8"))
2844 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2845 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2847 >>> s = UTF8String("привет мир")
2848 UTF8String UTF8String привет мир
2850 'привет мир'
2851 >>> hexenc(bytes(s))
2852 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2854 >>> PrintableString("привет мир")
2855 Traceback (most recent call last):
2856 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2858 >>> BMPString("ада", bounds=(2, 2))
2859 Traceback (most recent call last):
2860 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2861 >>> s = BMPString("ад", bounds=(2, 2))
2864 >>> hexenc(bytes(s))
2872 * - :py:class:`pyderasn.UTF8String`
2874 * - :py:class:`pyderasn.NumericString`
2876 * - :py:class:`pyderasn.PrintableString`
2878 * - :py:class:`pyderasn.TeletexString`
2880 * - :py:class:`pyderasn.T61String`
2882 * - :py:class:`pyderasn.VideotexString`
2884 * - :py:class:`pyderasn.IA5String`
2886 * - :py:class:`pyderasn.GraphicString`
2888 * - :py:class:`pyderasn.VisibleString`
2890 * - :py:class:`pyderasn.ISO646String`
2892 * - :py:class:`pyderasn.GeneralString`
2894 * - :py:class:`pyderasn.UniversalString`
2896 * - :py:class:`pyderasn.BMPString`
2899 __slots__ = ("encoding",)
2901 def _value_sanitize(self, value):
2903 value_decoded = None
2904 if isinstance(value, self.__class__):
2905 value_raw = value._value
2906 elif isinstance(value, text_type):
2907 value_decoded = value
2908 elif isinstance(value, binary_type):
2911 raise InvalidValueType((self.__class__, text_type, binary_type))
2914 value_decoded.encode(self.encoding)
2915 if value_raw is None else value_raw
2918 value_raw.decode(self.encoding)
2919 if value_decoded is None else value_decoded
2921 except (UnicodeEncodeError, UnicodeDecodeError) as err:
2922 raise DecodeError(str(err))
2923 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2931 def __eq__(self, their):
2932 if isinstance(their, binary_type):
2933 return self._value == their
2934 if isinstance(their, text_type):
2935 return self._value == their.encode(self.encoding)
2936 if not isinstance(their, self.__class__):
2939 self._value == their._value and
2940 self.tag == their.tag and
2941 self._expl == their._expl
2944 def __unicode__(self):
2946 return self._value.decode(self.encoding)
2947 return text_type(self._value)
2950 return pp_console_row(next(self.pps(no_unicode=PY2)))
2952 def pps(self, decode_path=(), no_unicode=False):
2955 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2957 asn1_type_name=self.asn1_type_name,
2958 obj_name=self.__class__.__name__,
2959 decode_path=decode_path,
2961 optional=self.optional,
2962 default=self == self.default,
2963 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2964 expl=None if self._expl is None else tag_decode(self._expl),
2972 class UTF8String(CommonString):
2974 tag_default = tag_encode(12)
2976 asn1_type_name = "UTF8String"
2979 class NumericString(CommonString):
2981 tag_default = tag_encode(18)
2983 asn1_type_name = "NumericString"
2984 allowable_chars = set(digits.encode("ascii"))
2986 def _value_sanitize(self, value):
2987 value = super(NumericString, self)._value_sanitize(value)
2988 if not set(value) <= self.allowable_chars:
2989 raise DecodeError("non-numeric value")
2993 class PrintableString(CommonString):
2995 tag_default = tag_encode(19)
2997 asn1_type_name = "PrintableString"
3000 class TeletexString(CommonString):
3002 tag_default = tag_encode(20)
3004 asn1_type_name = "TeletexString"
3007 class T61String(TeletexString):
3009 asn1_type_name = "T61String"
3012 class VideotexString(CommonString):
3014 tag_default = tag_encode(21)
3015 encoding = "iso-8859-1"
3016 asn1_type_name = "VideotexString"
3019 class IA5String(CommonString):
3021 tag_default = tag_encode(22)
3023 asn1_type_name = "IA5"
3026 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3027 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3028 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3031 class UTCTime(CommonString):
3032 """``UTCTime`` datetime type
3034 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3035 UTCTime UTCTime 2017-09-30T22:07:50
3041 datetime.datetime(2017, 9, 30, 22, 7, 50)
3042 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3043 datetime.datetime(1957, 9, 30, 22, 7, 50)
3046 tag_default = tag_encode(23)
3048 asn1_type_name = "UTCTime"
3050 fmt = "%y%m%d%H%M%SZ"
3060 bounds=None, # dummy argument, workability for OctetString.decode
3063 :param value: set the value. Either datetime type, or
3064 :py:class:`pyderasn.UTCTime` object
3065 :param bytes impl: override default tag with ``IMPLICIT`` one
3066 :param bytes expl: override default tag with ``EXPLICIT`` one
3067 :param default: set default value. Type same as in ``value``
3068 :param bool optional: is object ``OPTIONAL`` in sequence
3070 super(UTCTime, self).__init__(
3078 if value is not None:
3079 self._value = self._value_sanitize(value)
3080 if default is not None:
3081 default = self._value_sanitize(default)
3082 self.default = self.__class__(
3087 if self._value is None:
3088 self._value = default
3090 def _value_sanitize(self, value):
3091 if isinstance(value, self.__class__):
3093 if isinstance(value, datetime):
3094 return value.strftime(self.fmt).encode("ascii")
3095 if isinstance(value, binary_type):
3096 value_decoded = value.decode("ascii")
3097 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3099 datetime.strptime(value_decoded, self.fmt)
3101 raise DecodeError("invalid UTCTime format")
3104 raise DecodeError("invalid UTCTime length")
3105 raise InvalidValueType((self.__class__, datetime))
3107 def __eq__(self, their):
3108 if isinstance(their, binary_type):
3109 return self._value == their
3110 if isinstance(their, datetime):
3111 return self.todatetime() == their
3112 if not isinstance(their, self.__class__):
3115 self._value == their._value and
3116 self.tag == their.tag and
3117 self._expl == their._expl
3120 def todatetime(self):
3121 """Convert to datetime
3125 Pay attention that UTCTime can not hold full year, so all years
3126 having < 50 years are treated as 20xx, 19xx otherwise, according
3127 to X.509 recomendation.
3129 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3130 year = value.year % 100
3132 year=(2000 + year) if year < 50 else (1900 + year),
3136 minute=value.minute,
3137 second=value.second,
3141 return pp_console_row(next(self.pps()))
3143 def pps(self, decode_path=()):
3145 asn1_type_name=self.asn1_type_name,
3146 obj_name=self.__class__.__name__,
3147 decode_path=decode_path,
3148 value=self.todatetime().isoformat() if self.ready else None,
3149 optional=self.optional,
3150 default=self == self.default,
3151 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3152 expl=None if self._expl is None else tag_decode(self._expl),
3160 class GeneralizedTime(UTCTime):
3161 """``GeneralizedTime`` datetime type
3163 This type is similar to :py:class:`pyderasn.UTCTime`.
3165 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3166 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3168 '20170930220750.000123Z'
3169 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3170 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3173 tag_default = tag_encode(24)
3174 asn1_type_name = "GeneralizedTime"
3176 fmt = "%Y%m%d%H%M%SZ"
3177 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3179 def _value_sanitize(self, value):
3180 if isinstance(value, self.__class__):
3182 if isinstance(value, datetime):
3183 return value.strftime(
3184 self.fmt_ms if value.microsecond > 0 else self.fmt
3186 if isinstance(value, binary_type):
3187 value_decoded = value.decode("ascii")
3188 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3190 datetime.strptime(value_decoded, self.fmt)
3193 "invalid GeneralizedTime (without ms) format",
3196 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3198 datetime.strptime(value_decoded, self.fmt_ms)
3201 "invalid GeneralizedTime (with ms) format",
3206 "invalid GeneralizedTime length",
3207 klass=self.__class__,
3209 raise InvalidValueType((self.__class__, datetime))
3211 def todatetime(self):
3212 value = self._value.decode("ascii")
3213 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3214 return datetime.strptime(value, self.fmt)
3215 return datetime.strptime(value, self.fmt_ms)
3218 class GraphicString(CommonString):
3220 tag_default = tag_encode(25)
3221 encoding = "iso-8859-1"
3222 asn1_type_name = "GraphicString"
3225 class VisibleString(CommonString):
3227 tag_default = tag_encode(26)
3229 asn1_type_name = "VisibleString"
3232 class ISO646String(VisibleString):
3234 asn1_type_name = "ISO646String"
3237 class GeneralString(CommonString):
3239 tag_default = tag_encode(27)
3240 encoding = "iso-8859-1"
3241 asn1_type_name = "GeneralString"
3244 class UniversalString(CommonString):
3246 tag_default = tag_encode(28)
3247 encoding = "utf-32-be"
3248 asn1_type_name = "UniversalString"
3251 class BMPString(CommonString):
3253 tag_default = tag_encode(30)
3254 encoding = "utf-16-be"
3255 asn1_type_name = "BMPString"
3259 """``CHOICE`` special type
3263 class GeneralName(Choice):
3265 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3266 ("dNSName", IA5String(impl=tag_ctxp(2))),
3269 >>> gn = GeneralName()
3271 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3272 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3273 >>> gn["dNSName"] = IA5String("bar.baz")
3274 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3275 >>> gn["rfc822Name"]
3278 [2] IA5String IA5 bar.baz
3281 >>> gn.value == gn["dNSName"]
3284 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3286 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3287 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3289 __slots__ = ("specs",)
3291 asn1_type_name = "CHOICE"
3304 :param value: set the value. Either ``(choice, value)`` tuple, or
3305 :py:class:`pyderasn.Choice` object
3306 :param bytes impl: can not be set, do **not** use it
3307 :param bytes expl: override default tag with ``EXPLICIT`` one
3308 :param default: set default value. Type same as in ``value``
3309 :param bool optional: is object ``OPTIONAL`` in sequence
3311 if impl is not None:
3312 raise ValueError("no implicit tag allowed for CHOICE")
3313 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3315 schema = getattr(self, "schema", ())
3316 if len(schema) == 0:
3317 raise ValueError("schema must be specified")
3319 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3322 if value is not None:
3323 self._value = self._value_sanitize(value)
3324 if default is not None:
3325 default_value = self._value_sanitize(default)
3326 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3327 default_obj.specs = self.specs
3328 default_obj._value = default_value
3329 self.default = default_obj
3331 self._value = default_obj.copy()._value
3333 def _value_sanitize(self, value):
3334 if isinstance(value, self.__class__):
3336 if isinstance(value, tuple) and len(value) == 2:
3338 spec = self.specs.get(choice)
3340 raise ObjUnknown(choice)
3341 if not isinstance(obj, spec.__class__):
3342 raise InvalidValueType((spec,))
3343 return (choice, spec(obj))
3344 raise InvalidValueType((self.__class__, tuple))
3348 return self._value is not None and self._value[1].ready
3351 obj = self.__class__(schema=self.specs)
3352 obj._expl = self._expl
3353 obj.default = self.default
3354 obj.optional = self.optional
3355 obj.offset = self.offset
3356 obj.llen = self.llen
3357 obj.vlen = self.vlen
3359 if value is not None:
3360 obj._value = (value[0], value[1].copy())
3363 def __eq__(self, their):
3364 if isinstance(their, tuple) and len(their) == 2:
3365 return self._value == their
3366 if not isinstance(their, self.__class__):
3369 self.specs == their.specs and
3370 self._value == their._value
3380 return self.__class__(
3383 expl=self._expl if expl is None else expl,
3384 default=self.default if default is None else default,
3385 optional=self.optional if optional is None else optional,
3390 self._assert_ready()
3391 return self._value[0]
3395 self._assert_ready()
3396 return self._value[1]
3398 def __getitem__(self, key):
3399 if key not in self.specs:
3400 raise ObjUnknown(key)
3401 if self._value is None:
3403 choice, value = self._value
3408 def __setitem__(self, key, value):
3409 spec = self.specs.get(key)
3411 raise ObjUnknown(key)
3412 if not isinstance(value, spec.__class__):
3413 raise InvalidValueType((spec.__class__,))
3414 self._value = (key, spec(value))
3422 return self._value[1].decoded if self.ready else False
3425 self._assert_ready()
3426 return self._value[1].encode()
3428 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3429 for choice, spec in self.specs.items():
3430 sub_decode_path = decode_path + (choice,)
3436 decode_path=sub_decode_path,
3445 klass=self.__class__,
3446 decode_path=decode_path,
3451 value, tail = spec.decode(
3455 decode_path=sub_decode_path,
3458 obj = self.__class__(
3461 default=self.default,
3462 optional=self.optional,
3463 _decoded=(offset, 0, value.tlvlen),
3465 obj._value = (choice, value)
3469 value = pp_console_row(next(self.pps()))
3471 value = "%s[%r]" % (value, self.value)
3474 def pps(self, decode_path=()):
3476 asn1_type_name=self.asn1_type_name,
3477 obj_name=self.__class__.__name__,
3478 decode_path=decode_path,
3479 value=self.choice if self.ready else None,
3480 optional=self.optional,
3481 default=self == self.default,
3482 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3483 expl=None if self._expl is None else tag_decode(self._expl),
3490 yield self.value.pps(decode_path=decode_path + (self.choice,))
3493 class PrimitiveTypes(Choice):
3494 """Predefined ``CHOICE`` for all generic primitive types
3496 It could be useful for general decoding of some unspecified values:
3498 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3499 OCTET STRING 3 bytes 666f6f
3500 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3504 schema = tuple((klass.__name__, klass()) for klass in (
3529 """``ANY`` special type
3531 >>> Any(Integer(-123))
3533 >>> a = Any(OctetString(b"hello world").encode())
3534 ANY 040b68656c6c6f20776f726c64
3535 >>> hexenc(bytes(a))
3536 b'0x040x0bhello world'
3538 __slots__ = ("defined",)
3539 tag_default = tag_encode(0)
3540 asn1_type_name = "ANY"
3550 :param value: set the value. Either any kind of pyderasn's
3551 **ready** object, or bytes. Pay attention that
3552 **no** validation is performed is raw binary value
3554 :param bytes expl: override default tag with ``EXPLICIT`` one
3555 :param bool optional: is object ``OPTIONAL`` in sequence
3557 super(Any, self).__init__(None, expl, None, optional, _decoded)
3558 self._value = None if value is None else self._value_sanitize(value)
3561 def _value_sanitize(self, value):
3562 if isinstance(value, self.__class__):
3564 if isinstance(value, Obj):
3565 return value.encode()
3566 if isinstance(value, binary_type):
3568 raise InvalidValueType((self.__class__, Obj, binary_type))
3572 return self._value is not None
3575 obj = self.__class__()
3576 obj._value = self._value
3578 obj._expl = self._expl
3579 obj.optional = self.optional
3580 obj.offset = self.offset
3581 obj.llen = self.llen
3582 obj.vlen = self.vlen
3585 def __eq__(self, their):
3586 if isinstance(their, binary_type):
3587 return self._value == their
3588 if issubclass(their.__class__, Any):
3589 return self._value == their._value
3598 return self.__class__(
3600 expl=self._expl if expl is None else expl,
3601 optional=self.optional if optional is None else optional,
3604 def __bytes__(self):
3605 self._assert_ready()
3613 self._assert_ready()
3616 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3618 t, tlen, lv = tag_strip(tlv)
3619 l, llen, v = len_decode(lv)
3620 except DecodeError as err:
3621 raise err.__class__(
3623 klass=self.__class__,
3624 decode_path=decode_path,
3628 raise NotEnoughData(
3629 "encoded length is longer than data",
3630 klass=self.__class__,
3631 decode_path=decode_path,
3634 tlvlen = tlen + llen + l
3635 v, tail = tlv[:tlvlen], v[l:]
3636 obj = self.__class__(
3639 optional=self.optional,
3640 _decoded=(offset, 0, tlvlen),
3646 return pp_console_row(next(self.pps()))
3648 def pps(self, decode_path=()):
3650 asn1_type_name=self.asn1_type_name,
3651 obj_name=self.__class__.__name__,
3652 decode_path=decode_path,
3653 blob=self._value if self.ready else None,
3654 optional=self.optional,
3655 default=self == self.default,
3656 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3657 expl=None if self._expl is None else tag_decode(self._expl),
3662 expl_offset=self.expl_offset if self.expled else None,
3663 expl_tlen=self.expl_tlen if self.expled else None,
3664 expl_llen=self.expl_llen if self.expled else None,
3665 expl_vlen=self.expl_vlen if self.expled else None,
3667 defined_by, defined = self.defined or (None, None)
3668 if defined_by is not None:
3670 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3674 ########################################################################
3675 # ASN.1 constructed types
3676 ########################################################################
3678 def get_def_by_path(defines_by_path, sub_decode_path):
3679 """Get define by decode path
3681 for path, define in defines_by_path:
3682 if len(path) != len(sub_decode_path):
3684 for p1, p2 in zip(path, sub_decode_path):
3685 if (p1 != any) and (p1 != p2):
3691 def abs_decode_path(decode_path, rel_path):
3692 """Create an absolute decode path from current and relative ones
3694 :param decode_path: current decode path, starting point.
3696 :param rel_path: relative path to ``decode_path``. Tuple of strings.
3697 If first tuple's element is "/", then treat it as
3698 an absolute path, ignoring ``decode_path`` as
3699 starting point. Also this tuple can contain ".."
3700 elements, stripping the leading element from
3703 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
3704 ("foo", "bar", "baz", "whatever")
3705 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
3707 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
3710 if rel_path[0] == "/":
3712 if rel_path[0] == "..":
3713 return abs_decode_path(decode_path[:-1], rel_path[1:])
3714 return decode_path + rel_path
3717 class Sequence(Obj):
3718 """``SEQUENCE`` structure type
3720 You have to make specification of sequence::
3722 class Extension(Sequence):
3724 ("extnID", ObjectIdentifier()),
3725 ("critical", Boolean(default=False)),
3726 ("extnValue", OctetString()),
3729 Then, you can work with it as with dictionary.
3731 >>> ext = Extension()
3732 >>> Extension().specs
3734 ('extnID', OBJECT IDENTIFIER),
3735 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3736 ('extnValue', OCTET STRING),
3738 >>> ext["extnID"] = "1.2.3"
3739 Traceback (most recent call last):
3740 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3741 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3743 You can know if sequence is ready to be encoded:
3748 Traceback (most recent call last):
3749 pyderasn.ObjNotReady: object is not ready: extnValue
3750 >>> ext["extnValue"] = OctetString(b"foobar")
3754 Value you want to assign, must have the same **type** as in
3755 corresponding specification, but it can have different tags,
3756 optional/default attributes -- they will be taken from specification
3759 class TBSCertificate(Sequence):
3761 ("version", Version(expl=tag_ctxc(0), default="v1")),
3764 >>> tbs = TBSCertificate()
3765 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3767 Assign ``None`` to remove value from sequence.
3769 You can know if value exists/set in the sequence and take its value:
3771 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3774 OBJECT IDENTIFIER 1.2.3
3776 But pay attention that if value has default, then it won't be (not
3777 in) in the sequence (because ``DEFAULT`` must not be encoded in
3778 DER), but you can read its value:
3780 >>> "critical" in ext, ext["critical"]
3781 (False, BOOLEAN False)
3782 >>> ext["critical"] = Boolean(True)
3783 >>> "critical" in ext, ext["critical"]
3784 (True, BOOLEAN True)
3786 All defaulted values are always optional.
3788 .. _strict_default_existence_ctx:
3792 When decoded DER contains defaulted value inside, then
3793 technically this is not valid DER encoding. But we allow and pass
3794 it **by default**. Of course reencoding of that kind of DER will
3795 result in different binary representation (validly without
3796 defaulted value inside). You can enable strict defaulted values
3797 existence validation by setting ``"strict_default_existence":
3798 True`` :ref:`context <ctx>` option -- decoding process will raise
3799 an exception if defaulted value is met.
3801 Two sequences are equal if they have equal specification (schema),
3802 implicit/explicit tagging and the same values.
3804 __slots__ = ("specs",)
3805 tag_default = tag_encode(form=TagFormConstructed, num=16)
3806 asn1_type_name = "SEQUENCE"
3818 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3820 schema = getattr(self, "schema", ())
3822 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3825 if value is not None:
3826 self._value = self._value_sanitize(value)
3827 if default is not None:
3828 default_value = self._value_sanitize(default)
3829 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3830 default_obj.specs = self.specs
3831 default_obj._value = default_value
3832 self.default = default_obj
3834 self._value = default_obj.copy()._value
3836 def _value_sanitize(self, value):
3837 if not issubclass(value.__class__, Sequence):
3838 raise InvalidValueType((Sequence,))
3843 for name, spec in self.specs.items():
3844 value = self._value.get(name)
3855 obj = self.__class__(schema=self.specs)
3857 obj._expl = self._expl
3858 obj.default = self.default
3859 obj.optional = self.optional
3860 obj.offset = self.offset
3861 obj.llen = self.llen
3862 obj.vlen = self.vlen
3863 obj._value = {k: v.copy() for k, v in self._value.items()}
3866 def __eq__(self, their):
3867 if not isinstance(their, self.__class__):
3870 self.specs == their.specs and
3871 self.tag == their.tag and
3872 self._expl == their._expl and
3873 self._value == their._value
3884 return self.__class__(
3887 impl=self.tag if impl is None else impl,
3888 expl=self._expl if expl is None else expl,
3889 default=self.default if default is None else default,
3890 optional=self.optional if optional is None else optional,
3893 def __contains__(self, key):
3894 return key in self._value
3896 def __setitem__(self, key, value):
3897 spec = self.specs.get(key)
3899 raise ObjUnknown(key)
3901 self._value.pop(key, None)
3903 if not isinstance(value, spec.__class__):
3904 raise InvalidValueType((spec.__class__,))
3905 value = spec(value=value)
3906 if spec.default is not None and value == spec.default:
3907 self._value.pop(key, None)
3909 self._value[key] = value
3911 def __getitem__(self, key):
3912 value = self._value.get(key)
3913 if value is not None:
3915 spec = self.specs.get(key)
3917 raise ObjUnknown(key)
3918 if spec.default is not None:
3922 def _encoded_values(self):
3924 for name, spec in self.specs.items():
3925 value = self._value.get(name)
3929 raise ObjNotReady(name)
3930 raws.append(value.encode())
3934 v = b"".join(self._encoded_values())
3935 return b"".join((self.tag, len_encode(len(v)), v))
3937 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3939 t, tlen, lv = tag_strip(tlv)
3940 except DecodeError as err:
3941 raise err.__class__(
3943 klass=self.__class__,
3944 decode_path=decode_path,
3949 klass=self.__class__,
3950 decode_path=decode_path,
3956 l, llen, v = len_decode(lv)
3957 except DecodeError as err:
3958 raise err.__class__(
3960 klass=self.__class__,
3961 decode_path=decode_path,
3965 raise NotEnoughData(
3966 "encoded length is longer than data",
3967 klass=self.__class__,
3968 decode_path=decode_path,
3971 v, tail = v[:l], v[l:]
3972 sub_offset = offset + tlen + llen
3974 for name, spec in self.specs.items():
3975 if len(v) == 0 and spec.optional:
3977 sub_decode_path = decode_path + (name,)
3979 value, v_tail = spec.decode(
3983 decode_path=sub_decode_path,
3991 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
3992 if defined is not None:
3993 defined_by, defined_spec = defined
3994 if issubclass(value.__class__, SequenceOf):
3995 for i, _value in enumerate(value):
3996 sub_sub_decode_path = sub_decode_path + (
3998 DecodePathDefBy(defined_by),
4000 defined_value, defined_tail = defined_spec.decode(
4001 memoryview(bytes(_value)),
4003 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4004 if value.expled else (value.tlen + value.llen)
4007 decode_path=sub_sub_decode_path,
4010 if len(defined_tail) > 0:
4013 klass=self.__class__,
4014 decode_path=sub_sub_decode_path,
4017 _value.defined = (defined_by, defined_value)
4019 defined_value, defined_tail = defined_spec.decode(
4020 memoryview(bytes(value)),
4022 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4023 if value.expled else (value.tlen + value.llen)
4026 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4029 if len(defined_tail) > 0:
4032 klass=self.__class__,
4033 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4036 value.defined = (defined_by, defined_value)
4038 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4040 if spec.default is not None and value == spec.default:
4041 if ctx.get("strict_default_existence", False):
4043 "DEFAULT value met",
4044 klass=self.__class__,
4045 decode_path=sub_decode_path,
4050 values[name] = value
4052 spec_defines = getattr(spec, "defines", ())
4053 if len(spec_defines) == 0:
4054 defines_by_path = ctx.get("defines_by_path", ())
4055 if len(defines_by_path) > 0:
4056 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4057 if spec_defines is not None and len(spec_defines) > 0:
4058 for rel_path, schema in spec_defines:
4059 defined = schema.get(value, None)
4060 if defined is not None:
4061 ctx.setdefault("defines", []).append((
4062 abs_decode_path(sub_decode_path[:-1], rel_path),
4068 klass=self.__class__,
4069 decode_path=decode_path,
4072 obj = self.__class__(
4076 default=self.default,
4077 optional=self.optional,
4078 _decoded=(offset, llen, l),
4084 value = pp_console_row(next(self.pps()))
4086 for name in self.specs:
4087 _value = self._value.get(name)
4090 cols.append(repr(_value))
4091 return "%s[%s]" % (value, ", ".join(cols))
4093 def pps(self, decode_path=()):
4095 asn1_type_name=self.asn1_type_name,
4096 obj_name=self.__class__.__name__,
4097 decode_path=decode_path,
4098 optional=self.optional,
4099 default=self == self.default,
4100 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4101 expl=None if self._expl is None else tag_decode(self._expl),
4106 expl_offset=self.expl_offset if self.expled else None,
4107 expl_tlen=self.expl_tlen if self.expled else None,
4108 expl_llen=self.expl_llen if self.expled else None,
4109 expl_vlen=self.expl_vlen if self.expled else None,
4111 for name in self.specs:
4112 value = self._value.get(name)
4115 yield value.pps(decode_path=decode_path + (name,))
4118 class Set(Sequence):
4119 """``SET`` structure type
4121 Its usage is identical to :py:class:`pyderasn.Sequence`.
4124 tag_default = tag_encode(form=TagFormConstructed, num=17)
4125 asn1_type_name = "SET"
4128 raws = self._encoded_values()
4131 return b"".join((self.tag, len_encode(len(v)), v))
4133 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4135 t, tlen, lv = tag_strip(tlv)
4136 except DecodeError as err:
4137 raise err.__class__(
4139 klass=self.__class__,
4140 decode_path=decode_path,
4145 klass=self.__class__,
4146 decode_path=decode_path,
4152 l, llen, v = len_decode(lv)
4153 except DecodeError as err:
4154 raise err.__class__(
4156 klass=self.__class__,
4157 decode_path=decode_path,
4161 raise NotEnoughData(
4162 "encoded length is longer than data",
4163 klass=self.__class__,
4166 v, tail = v[:l], v[l:]
4167 sub_offset = offset + tlen + llen
4169 specs_items = self.specs.items
4171 for name, spec in specs_items():
4172 sub_decode_path = decode_path + (name,)
4178 decode_path=sub_decode_path,
4187 klass=self.__class__,
4188 decode_path=decode_path,
4191 value, v_tail = spec.decode(
4195 decode_path=sub_decode_path,
4199 value.expl_tlvlen if value.expled else value.tlvlen
4202 if spec.default is None or value != spec.default: # pragma: no cover
4203 # SeqMixing.test_encoded_default_accepted covers that place
4204 values[name] = value
4205 obj = self.__class__(
4209 default=self.default,
4210 optional=self.optional,
4211 _decoded=(offset, llen, l),
4217 class SequenceOf(Obj):
4218 """``SEQUENCE OF`` sequence type
4220 For that kind of type you must specify the object it will carry on
4221 (bounds are for example here, not required)::
4223 class Ints(SequenceOf):
4228 >>> ints.append(Integer(123))
4229 >>> ints.append(Integer(234))
4231 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4232 >>> [int(i) for i in ints]
4234 >>> ints.append(Integer(345))
4235 Traceback (most recent call last):
4236 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4239 >>> ints[1] = Integer(345)
4241 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4243 Also you can initialize sequence with preinitialized values:
4245 >>> ints = Ints([Integer(123), Integer(234)])
4247 __slots__ = ("spec", "_bound_min", "_bound_max")
4248 tag_default = tag_encode(form=TagFormConstructed, num=16)
4249 asn1_type_name = "SEQUENCE OF"
4262 super(SequenceOf, self).__init__(
4270 schema = getattr(self, "schema", None)
4272 raise ValueError("schema must be specified")
4274 self._bound_min, self._bound_max = getattr(
4278 ) if bounds is None else bounds
4280 if value is not None:
4281 self._value = self._value_sanitize(value)
4282 if default is not None:
4283 default_value = self._value_sanitize(default)
4284 default_obj = self.__class__(
4289 default_obj._value = default_value
4290 self.default = default_obj
4292 self._value = default_obj.copy()._value
4294 def _value_sanitize(self, value):
4295 if issubclass(value.__class__, SequenceOf):
4296 value = value._value
4297 elif hasattr(value, "__iter__"):
4300 raise InvalidValueType((self.__class__, iter))
4301 if not self._bound_min <= len(value) <= self._bound_max:
4302 raise BoundsError(self._bound_min, len(value), self._bound_max)
4304 if not isinstance(v, self.spec.__class__):
4305 raise InvalidValueType((self.spec.__class__,))
4310 return all(v.ready for v in self._value)
4313 obj = self.__class__(schema=self.spec)
4314 obj._bound_min = self._bound_min
4315 obj._bound_max = self._bound_max
4317 obj._expl = self._expl
4318 obj.default = self.default
4319 obj.optional = self.optional
4320 obj.offset = self.offset
4321 obj.llen = self.llen
4322 obj.vlen = self.vlen
4323 obj._value = [v.copy() for v in self._value]
4326 def __eq__(self, their):
4327 if isinstance(their, self.__class__):
4329 self.spec == their.spec and
4330 self.tag == their.tag and
4331 self._expl == their._expl and
4332 self._value == their._value
4334 if hasattr(their, "__iter__"):
4335 return self._value == list(their)
4347 return self.__class__(
4351 (self._bound_min, self._bound_max)
4352 if bounds is None else bounds
4354 impl=self.tag if impl is None else impl,
4355 expl=self._expl if expl is None else expl,
4356 default=self.default if default is None else default,
4357 optional=self.optional if optional is None else optional,
4360 def __contains__(self, key):
4361 return key in self._value
4363 def append(self, value):
4364 if not isinstance(value, self.spec.__class__):
4365 raise InvalidValueType((self.spec.__class__,))
4366 if len(self._value) + 1 > self._bound_max:
4369 len(self._value) + 1,
4372 self._value.append(value)
4375 self._assert_ready()
4376 return iter(self._value)
4379 self._assert_ready()
4380 return len(self._value)
4382 def __setitem__(self, key, value):
4383 if not isinstance(value, self.spec.__class__):
4384 raise InvalidValueType((self.spec.__class__,))
4385 self._value[key] = self.spec(value=value)
4387 def __getitem__(self, key):
4388 return self._value[key]
4390 def _encoded_values(self):
4391 return [v.encode() for v in self._value]
4394 v = b"".join(self._encoded_values())
4395 return b"".join((self.tag, len_encode(len(v)), v))
4397 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4399 t, tlen, lv = tag_strip(tlv)
4400 except DecodeError as err:
4401 raise err.__class__(
4403 klass=self.__class__,
4404 decode_path=decode_path,
4409 klass=self.__class__,
4410 decode_path=decode_path,
4416 l, llen, v = len_decode(lv)
4417 except DecodeError as err:
4418 raise err.__class__(
4420 klass=self.__class__,
4421 decode_path=decode_path,
4425 raise NotEnoughData(
4426 "encoded length is longer than data",
4427 klass=self.__class__,
4428 decode_path=decode_path,
4431 v, tail = v[:l], v[l:]
4432 sub_offset = offset + tlen + llen
4436 value, v_tail = spec.decode(
4440 decode_path=decode_path + (str(len(_value)),),
4443 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4445 _value.append(value)
4446 obj = self.__class__(
4449 bounds=(self._bound_min, self._bound_max),
4452 default=self.default,
4453 optional=self.optional,
4454 _decoded=(offset, llen, l),
4460 pp_console_row(next(self.pps())),
4461 ", ".join(repr(v) for v in self._value),
4464 def pps(self, decode_path=()):
4466 asn1_type_name=self.asn1_type_name,
4467 obj_name=self.__class__.__name__,
4468 decode_path=decode_path,
4469 optional=self.optional,
4470 default=self == self.default,
4471 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4472 expl=None if self._expl is None else tag_decode(self._expl),
4477 expl_offset=self.expl_offset if self.expled else None,
4478 expl_tlen=self.expl_tlen if self.expled else None,
4479 expl_llen=self.expl_llen if self.expled else None,
4480 expl_vlen=self.expl_vlen if self.expled else None,
4482 for i, value in enumerate(self._value):
4483 yield value.pps(decode_path=decode_path + (str(i),))
4486 class SetOf(SequenceOf):
4487 """``SET OF`` sequence type
4489 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4492 tag_default = tag_encode(form=TagFormConstructed, num=17)
4493 asn1_type_name = "SET OF"
4496 raws = self._encoded_values()
4499 return b"".join((self.tag, len_encode(len(v)), v))
4502 def obj_by_path(pypath): # pragma: no cover
4503 """Import object specified as string Python path
4505 Modules must be separated from classes/functions with ``:``.
4507 >>> obj_by_path("foo.bar:Baz")
4508 <class 'foo.bar.Baz'>
4509 >>> obj_by_path("foo.bar:Baz.boo")
4510 <classmethod 'foo.bar.Baz.boo'>
4512 mod, objs = pypath.rsplit(":", 1)
4513 from importlib import import_module
4514 obj = import_module(mod)
4515 for obj_name in objs.split("."):
4516 obj = getattr(obj, obj_name)
4520 def generic_decoder(): # pragma: no cover
4521 # All of this below is a big hack with self references
4522 choice = PrimitiveTypes()
4523 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4524 choice.specs["SetOf"] = SetOf(schema=choice)
4526 choice.specs["SequenceOf%d" % i] = SequenceOf(
4530 choice.specs["Any"] = Any()
4532 # Class name equals to type name, to omit it from output
4533 class SEQUENCEOF(SequenceOf):
4537 def pprint_any(obj, oids=None, with_colours=False):
4538 def _pprint_pps(pps):
4540 if hasattr(pp, "_fields"):
4541 if pp.asn1_type_name == Choice.asn1_type_name:
4543 pp_kwargs = pp._asdict()
4544 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4545 pp = _pp(**pp_kwargs)
4546 yield pp_console_row(
4551 with_colours=with_colours,
4553 for row in pp_console_blob(pp):
4556 for row in _pprint_pps(pp):
4558 return "\n".join(_pprint_pps(obj.pps()))
4559 return SEQUENCEOF(), pprint_any
4562 def main(): # pragma: no cover
4564 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4565 parser.add_argument(
4569 help="Skip that number of bytes from the beginning",
4571 parser.add_argument(
4573 help="Python path to dictionary with OIDs",
4575 parser.add_argument(
4577 help="Python path to schema definition to use",
4579 parser.add_argument(
4580 "--defines-by-path",
4581 help="Python path to decoder's defines_by_path",
4583 parser.add_argument(
4585 type=argparse.FileType("rb"),
4586 help="Path to DER file you want to decode",
4588 args = parser.parse_args()
4589 args.DERFile.seek(args.skip)
4590 der = memoryview(args.DERFile.read())
4591 args.DERFile.close()
4592 oids = obj_by_path(args.oids) if args.oids else {}
4594 schema = obj_by_path(args.schema)
4595 from functools import partial
4596 pprinter = partial(pprint, big_blobs=True)
4598 schema, pprinter = generic_decoder()
4599 obj, tail = schema().decode(
4602 None if args.defines_by_path is None else
4603 {"defines_by_path": obj_by_path(args.defines_by_path)}
4609 with_colours=True if environ.get("NO_COLOR") is None else False,
4612 print("\nTrailing data: %s" % hexenc(tail))
4615 if __name__ == "__main__":