3 # PyDERASN -- Python ASN.1 DER codec with abstract structures
4 # Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 """Python ASN.1 DER codec with abstract structures
21 This library allows you to marshal and unmarshal various structures in
22 ASN.1 DER format, like this:
26 >>> Integer().decode(raw) == i
29 There are primitive types, holding single values
30 (:py:class:`pyderasn.BitString`,
31 :py:class:`pyderasn.Boolean`,
32 :py:class:`pyderasn.Enumerated`,
33 :py:class:`pyderasn.GeneralizedTime`,
34 :py:class:`pyderasn.Integer`,
35 :py:class:`pyderasn.Null`,
36 :py:class:`pyderasn.ObjectIdentifier`,
37 :py:class:`pyderasn.OctetString`,
38 :py:class:`pyderasn.UTCTime`,
39 :py:class:`various strings <pyderasn.CommonString>`
40 (:py:class:`pyderasn.BMPString`,
41 :py:class:`pyderasn.GeneralString`,
42 :py:class:`pyderasn.GraphicString`,
43 :py:class:`pyderasn.IA5String`,
44 :py:class:`pyderasn.ISO646String`,
45 :py:class:`pyderasn.NumericString`,
46 :py:class:`pyderasn.PrintableString`,
47 :py:class:`pyderasn.T61String`,
48 :py:class:`pyderasn.TeletexString`,
49 :py:class:`pyderasn.UniversalString`,
50 :py:class:`pyderasn.UTF8String`,
51 :py:class:`pyderasn.VideotexString`,
52 :py:class:`pyderasn.VisibleString`)),
53 constructed types, holding multiple primitive types
54 (:py:class:`pyderasn.Sequence`,
55 :py:class:`pyderasn.SequenceOf`,
56 :py:class:`pyderasn.Set`,
57 :py:class:`pyderasn.SetOf`),
58 and special types like
59 :py:class:`pyderasn.Any` and
60 :py:class:`pyderasn.Choice`.
68 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
69 the default tag used during coding process. You can override it with
70 either ``IMPLICIT`` (using ``impl`` keyword argument), or
71 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments take
72 raw binary string, containing that tag. You can **not** set implicit and
73 explicit tags simultaneously.
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
78 number. Pay attention that explicit tags always have *constructed* tag
79 (``tag_ctxc``), but implicit tags for primitive types are primitive
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two objects of the same type, but with different implicit/explicit tags
94 You can get object's effective tag (either default or implicited) through
95 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
112 Many objects in sequences could be ``OPTIONAL`` and could have
113 ``DEFAULT`` value. You can specify that object's property using
114 corresponding keyword arguments.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
119 Those specifications do not play any role in primitive value encoding,
120 but are taken into account when dealing with sequences holding them. For
121 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
143 Some objects give ability to set value size constraints. This is either
144 possible integer value, or allowed length of various strings and
145 sequences. Constraints are set in the following way::
150 And values satisfaction is checked as: ``MIN <= X <= MAX``.
152 For simplicity you can also set bounds the following way::
154 bounded_x = X(bounds=(MIN, MAX))
156 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
162 All objects have ``ready`` boolean property, that tells if object is
163 ready to be encoded. If that kind of action is performed on unready
164 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
166 All objects have ``copy()`` method, that returns their copy, that can be
174 Decoding is performed using ``decode()`` method. ``offset`` optional
175 argument could be used to set initial object's offset in the binary
176 data, for convenience. It returns decoded object and remaining
177 unmarshalled data (tail). Internally all work is done on
178 ``memoryview(data)``, and you can leave returning tail as a memoryview,
179 by specifying ``leavemm=True`` argument.
181 When object is decoded, ``decoded`` property is true and you can safely
182 use following properties:
184 * ``offset`` -- position including initial offset where object's tag starts
185 * ``tlen`` -- length of object's tag
186 * ``llen`` -- length of object's length value
187 * ``vlen`` -- length of object's value
188 * ``tlvlen`` -- length of the whole object
190 Pay attention that those values do **not** include anything related to
191 explicit tag. If you want to know information about it, then use:
192 ``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
193 lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
194 (that actually equals to ordinary ``tlvlen``).
196 When error occurs, then :py:exc:`pyderasn.DecodeError` is raised.
203 You can specify so called context keyword argument during ``decode()``
204 invocation. It is dictionary containing various options governing
207 Currently available context options:
209 * :ref:`defines_by_path <defines_by_path_ctx>`
210 * :ref:`strict_default_existence <strict_default_existence_ctx>`
217 All objects have ``pps()`` method, that is a generator of
218 :py:class:`pyderasn.PP` namedtuple, holding various raw information
219 about the object. If ``pps`` is called on sequences, then all underlying
220 ``PP`` will be yielded.
222 You can use :py:func:`pyderasn.pp_console_row` function, converting
223 those ``PP`` to human readable string. Actually exactly it is used for
224 all object ``repr``. But it is easy to write custom formatters.
226 >>> from pyderasn import pprint
227 >>> encoded = Integer(-12345).encode()
228 >>> obj, tail = Integer().decode(encoded)
229 >>> print(pprint(obj))
230 0 [1,1, 2] INTEGER -12345
237 ASN.1 structures often have ANY and OCTET STRING fields, that are
238 DEFINED BY some previously met ObjectIdentifier. This library provides
239 ability to specify mapping between some OID and field that must be
240 decoded with specific specification.
245 :py:class:`pyderasn.ObjectIdentifier` field inside
246 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
247 necessary for decoding structures. For example, CMS (:rfc:`5652`)
250 class ContentInfo(Sequence):
252 ("contentType", ContentType(defines=((("content",), {
253 id_digestedData: DigestedData(),
254 id_signedData: SignedData(),
256 ("content", Any(expl=tag_ctxc(0))),
259 ``contentType`` field tells that it defines that ``content`` must be
260 decoded with ``SignedData`` specification, if ``contentType`` equals to
261 ``id-signedData``. The same applies to ``DigestedData``. If
262 ``contentType`` contains unknown OID, then no automatic decoding is
265 You can specify multiple fields, that will be autodecoded -- that is why
266 ``defines`` kwarg is a sequence. You can specify defined field
267 relatively or absolutely to current decode path. For example ``defines``
268 for AlgorithmIdentifier of X.509's
269 ``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
273 id_ecPublicKey: ECParameters(),
274 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
276 (('..', 'subjectPublicKey'), {
277 id_rsaEncryption: RSAPublicKey(),
278 id_GostR3410_2001: OctetString(),
282 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
283 autodecode its parameters inside SPKI's algorithm and its public key
286 Following types can be automatically decoded (DEFINED BY):
288 * :py:class:`pyderasn.Any`
289 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
290 * :py:class:`pyderasn.OctetString`
291 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
292 ``Any``/``BitString``/``OctetString``-s
294 When any of those fields is automatically decoded, then ``.defined``
295 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
296 was defined, ``value`` contains corresponding decoded value. For example
297 above, ``content_info["content"].defined == (id_signedData,
300 .. _defines_by_path_ctx:
302 defines_by_path context option
303 ______________________________
305 Sometimes you either can not or do not want to explicitly set *defines*
306 in the scheme. You can dynamically apply those definitions when calling
307 ``.decode()`` method.
309 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
310 value must be sequence of following tuples::
312 (decode_path, defines)
314 where ``decode_path`` is a tuple holding so-called decode path to the
315 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
316 ``defines``, holding exactly the same value as accepted in its keyword
319 For example, again for CMS, you want to automatically decode
320 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
321 structures it may hold. Also, automatically decode ``controlSequence``
324 content_info, tail = ContentInfo().decode(data, defines_by_path=(
327 ((("content",), {id_signedData: SignedData()}),),
332 DecodePathDefBy(id_signedData),
337 id_cct_PKIData: PKIData(),
338 id_cct_PKIResponse: PKIResponse(),
344 DecodePathDefBy(id_signedData),
347 DecodePathDefBy(id_cct_PKIResponse),
353 id_cmc_recipientNonce: RecipientNonce(),
354 id_cmc_senderNonce: SenderNonce(),
355 id_cmc_statusInfoV2: CMCStatusInfoV2(),
356 id_cmc_transactionId: TransactionId(),
361 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
362 First function is useful for path construction when some automatic
363 decoding is already done. ``any`` means literally any value it meet --
364 useful for SEQUENCE/SET OF-s.
371 .. autoclass:: pyderasn.Boolean
376 .. autoclass:: pyderasn.Integer
381 .. autoclass:: pyderasn.BitString
386 .. autoclass:: pyderasn.OctetString
391 .. autoclass:: pyderasn.Null
396 .. autoclass:: pyderasn.ObjectIdentifier
401 .. autoclass:: pyderasn.Enumerated
405 .. autoclass:: pyderasn.CommonString
409 .. autoclass:: pyderasn.UTCTime
410 :members: __init__, todatetime
414 .. autoclass:: pyderasn.GeneralizedTime
421 .. autoclass:: pyderasn.Choice
426 .. autoclass:: PrimitiveTypes
430 .. autoclass:: pyderasn.Any
438 .. autoclass:: pyderasn.Sequence
443 .. autoclass:: pyderasn.Set
448 .. autoclass:: pyderasn.SequenceOf
453 .. autoclass:: pyderasn.SetOf
459 .. autofunction:: pyderasn.abs_decode_path
460 .. autofunction:: pyderasn.hexenc
461 .. autofunction:: pyderasn.hexdec
462 .. autofunction:: pyderasn.tag_encode
463 .. autofunction:: pyderasn.tag_decode
464 .. autofunction:: pyderasn.tag_ctxp
465 .. autofunction:: pyderasn.tag_ctxc
466 .. autoclass:: pyderasn.Obj
469 from codecs import getdecoder
470 from codecs import getencoder
471 from collections import namedtuple
472 from collections import OrderedDict
473 from datetime import datetime
474 from math import ceil
475 from os import environ
476 from string import digits
478 from six import add_metaclass
479 from six import binary_type
480 from six import byte2int
481 from six import indexbytes
482 from six import int2byte
483 from six import integer_types
484 from six import iterbytes
486 from six import string_types
487 from six import text_type
488 from six.moves import xrange as six_xrange
492 from termcolor import colored
494 def colored(what, *args):
537 "TagClassApplication",
541 "TagFormConstructed",
552 TagClassUniversal = 0
553 TagClassApplication = 1 << 6
554 TagClassContext = 1 << 7
555 TagClassPrivate = 1 << 6 | 1 << 7
557 TagFormConstructed = 1 << 5
560 TagClassApplication: "APPLICATION ",
561 TagClassPrivate: "PRIVATE ",
562 TagClassUniversal: "UNIV ",
566 ########################################################################
568 ########################################################################
570 class DecodeError(Exception):
571 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
573 :param str msg: reason of decode failing
574 :param klass: optional exact DecodeError inherited class (like
575 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
576 :py:exc:`InvalidLength`)
577 :param decode_path: tuple of strings. It contains human
578 readable names of the fields through which
579 decoding process has passed
580 :param int offset: binary offset where failure happened
582 super(DecodeError, self).__init__()
585 self.decode_path = decode_path
591 "" if self.klass is None else self.klass.__name__,
593 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
594 if len(self.decode_path) > 0 else ""
596 ("(at %d)" % self.offset) if self.offset > 0 else "",
602 return "%s(%s)" % (self.__class__.__name__, self)
605 class NotEnoughData(DecodeError):
609 class TagMismatch(DecodeError):
613 class InvalidLength(DecodeError):
617 class InvalidOID(DecodeError):
621 class ObjUnknown(ValueError):
622 def __init__(self, name):
623 super(ObjUnknown, self).__init__()
627 return "object is unknown: %s" % self.name
630 return "%s(%s)" % (self.__class__.__name__, self)
633 class ObjNotReady(ValueError):
634 def __init__(self, name):
635 super(ObjNotReady, self).__init__()
639 return "object is not ready: %s" % self.name
642 return "%s(%s)" % (self.__class__.__name__, self)
645 class InvalidValueType(ValueError):
646 def __init__(self, expected_types):
647 super(InvalidValueType, self).__init__()
648 self.expected_types = expected_types
651 return "invalid value type, expected: %s" % ", ".join(
652 [repr(t) for t in self.expected_types]
656 return "%s(%s)" % (self.__class__.__name__, self)
659 class BoundsError(ValueError):
660 def __init__(self, bound_min, value, bound_max):
661 super(BoundsError, self).__init__()
662 self.bound_min = bound_min
664 self.bound_max = bound_max
667 return "unsatisfied bounds: %s <= %s <= %s" % (
674 return "%s(%s)" % (self.__class__.__name__, self)
677 ########################################################################
679 ########################################################################
681 _hexdecoder = getdecoder("hex")
682 _hexencoder = getencoder("hex")
686 """Binary data to hexadecimal string convert
688 return _hexdecoder(data)[0]
692 """Hexadecimal string to binary data convert
694 return _hexencoder(data)[0].decode("ascii")
697 def int_bytes_len(num, byte_len=8):
700 return int(ceil(float(num.bit_length()) / byte_len))
703 def zero_ended_encode(num):
704 octets = bytearray(int_bytes_len(num, 7))
706 octets[i] = num & 0x7F
710 octets[i] = 0x80 | (num & 0x7F)
716 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
717 """Encode tag to binary form
719 :param int num: tag's number
720 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
721 :py:data:`pyderasn.TagClassContext`,
722 :py:data:`pyderasn.TagClassApplication`,
723 :py:data:`pyderasn.TagClassPrivate`)
724 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
725 :py:data:`pyderasn.TagFormConstructed`)
729 return int2byte(klass | form | num)
730 # [XX|X|11111][1.......][1.......] ... [0.......]
731 return int2byte(klass | form | 31) + zero_ended_encode(num)
735 """Decode tag from binary form
739 No validation is performed, assuming that it has already passed.
741 It returns tuple with three integers, as
742 :py:func:`pyderasn.tag_encode` accepts.
744 first_octet = byte2int(tag)
745 klass = first_octet & 0xC0
746 form = first_octet & 0x20
747 if first_octet & 0x1F < 0x1F:
748 return (klass, form, first_octet & 0x1F)
750 for octet in iterbytes(tag[1:]):
753 return (klass, form, num)
757 """Create CONTEXT PRIMITIVE tag
759 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
763 """Create CONTEXT CONSTRUCTED tag
765 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
769 """Take off tag from the data
771 :returns: (encoded tag, tag length, remaining data)
774 raise NotEnoughData("no data at all")
775 if byte2int(data) & 0x1F < 31:
776 return data[:1], 1, data[1:]
781 raise DecodeError("unfinished tag")
782 if indexbytes(data, i) & 0x80 == 0:
785 return data[:i], i, data[i:]
791 octets = bytearray(int_bytes_len(l) + 1)
792 octets[0] = 0x80 | (len(octets) - 1)
793 for i in six_xrange(len(octets) - 1, 0, -1):
799 def len_decode(data):
801 raise NotEnoughData("no data at all")
802 first_octet = byte2int(data)
803 if first_octet & 0x80 == 0:
804 return first_octet, 1, data[1:]
805 octets_num = first_octet & 0x7F
806 if octets_num + 1 > len(data):
807 raise NotEnoughData("encoded length is longer than data")
809 raise DecodeError("long form instead of short one")
810 if byte2int(data[1:]) == 0:
811 raise DecodeError("leading zeros")
813 for v in iterbytes(data[1:1 + octets_num]):
816 raise DecodeError("long form instead of short one")
817 return l, 1 + octets_num, data[1 + octets_num:]
820 ########################################################################
822 ########################################################################
824 class AutoAddSlots(type):
825 def __new__(mcs, name, bases, _dict):
826 _dict["__slots__"] = _dict.get("__slots__", ())
827 return type.__new__(mcs, name, bases, _dict)
830 @add_metaclass(AutoAddSlots)
832 """Common ASN.1 object class
834 All ASN.1 types are inherited from it. It has metaclass that
835 automatically adds ``__slots__`` to all inherited classes.
856 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
857 self._expl = getattr(self, "expl", None) if expl is None else expl
858 if self.tag != self.tag_default and self._expl is not None:
860 "implicit and explicit tags can not be set simultaneously"
862 if default is not None:
864 self.optional = optional
865 self.offset, self.llen, self.vlen = _decoded
869 def ready(self): # pragma: no cover
870 """Is object ready to be encoded?
872 raise NotImplementedError()
874 def _assert_ready(self):
876 raise ObjNotReady(self.__class__.__name__)
880 """Is object decoded?
882 return (self.llen + self.vlen) > 0
884 def copy(self): # pragma: no cover
885 """Make a copy of object, safe to be mutated
887 raise NotImplementedError()
895 return self.tlen + self.llen + self.vlen
897 def __str__(self): # pragma: no cover
898 return self.__bytes__() if PY2 else self.__unicode__()
900 def __ne__(self, their):
901 return not(self == their)
903 def __gt__(self, their): # pragma: no cover
904 return not(self < their)
906 def __le__(self, their): # pragma: no cover
907 return (self == their) or (self < their)
909 def __ge__(self, their): # pragma: no cover
910 return (self == their) or (self > their)
912 def _encode(self): # pragma: no cover
913 raise NotImplementedError()
915 def _decode(self, tlv, offset, decode_path, ctx): # pragma: no cover
916 raise NotImplementedError()
920 if self._expl is None:
922 return b"".join((self._expl, len_encode(len(raw)), raw))
924 def decode(self, data, offset=0, leavemm=False, decode_path=(), ctx=None):
927 :param data: either binary or memoryview
928 :param int offset: initial data's offset
929 :param bool leavemm: do we need to leave memoryview of remaining
930 data as is, or convert it to bytes otherwise
931 :param ctx: optional :ref:`context <ctx>` governing decoding process.
932 :returns: (Obj, remaining data)
936 tlv = memoryview(data)
937 if self._expl is None:
938 obj, tail = self._decode(
941 decode_path=decode_path,
946 t, tlen, lv = tag_strip(tlv)
947 except DecodeError as err:
950 klass=self.__class__,
951 decode_path=decode_path,
956 klass=self.__class__,
957 decode_path=decode_path,
961 l, llen, v = len_decode(lv)
962 except DecodeError as err:
965 klass=self.__class__,
966 decode_path=decode_path,
971 "encoded length is longer than data",
972 klass=self.__class__,
973 decode_path=decode_path,
976 obj, tail = self._decode(
978 offset=offset + tlen + llen,
979 decode_path=decode_path,
982 return obj, (tail if leavemm else tail.tobytes())
986 return self._expl is not None
994 return len(self._expl)
998 return len(len_encode(self.tlvlen))
1001 def expl_offset(self):
1002 return self.offset - self.expl_tlen - self.expl_llen
1005 def expl_vlen(self):
1009 def expl_tlvlen(self):
1010 return self.expl_tlen + self.expl_llen + self.expl_vlen
1013 class DecodePathDefBy(object):
1014 """DEFINED BY representation inside decode path
1016 __slots__ = ('defined_by',)
1018 def __init__(self, defined_by):
1019 self.defined_by = defined_by
1021 def __eq__(self, their):
1022 if not isinstance(their, self.__class__):
1024 return self.defined_by == their.defined_by
1027 return "DEFINED BY " + str(self.defined_by)
1030 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1033 ########################################################################
1035 ########################################################################
1037 PP = namedtuple("PP", (
1059 asn1_type_name="unknown",
1098 def _colorize(what, colour, with_colours, attrs=("bold",)):
1099 return colored(what, colour, attrs=attrs) if with_colours else what
1114 " " if pp.expl_offset is None else
1115 ("-%d" % (pp.offset - pp.expl_offset))
1118 cols.append(_colorize(col, "red", with_colours, ()))
1119 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1120 cols.append(_colorize(col, "green", with_colours, ()))
1121 if len(pp.decode_path) > 0:
1122 cols.append(" ." * (len(pp.decode_path)))
1123 ent = pp.decode_path[-1]
1124 if isinstance(ent, DecodePathDefBy):
1125 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1126 value = str(ent.defined_by)
1128 oids is not None and
1129 ent.defined_by.asn1_type_name ==
1130 ObjectIdentifier.asn1_type_name and
1133 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1135 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1137 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1138 if pp.expl is not None:
1139 klass, _, num = pp.expl
1140 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1141 cols.append(_colorize(col, "blue", with_colours))
1142 if pp.impl is not None:
1143 klass, _, num = pp.impl
1144 col = "[%s%d]" % (TagClassReprs[klass], num)
1145 cols.append(_colorize(col, "blue", with_colours))
1146 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1147 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1148 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1149 if pp.value is not None:
1151 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1153 oids is not None and
1154 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1157 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1159 if isinstance(pp.blob, binary_type):
1160 cols.append(hexenc(pp.blob))
1161 elif isinstance(pp.blob, tuple):
1162 cols.append(", ".join(pp.blob))
1164 cols.append(_colorize("OPTIONAL", "red", with_colours))
1166 cols.append(_colorize("DEFAULT", "red", with_colours))
1167 return " ".join(cols)
1170 def pp_console_blob(pp):
1171 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
1172 if len(pp.decode_path) > 0:
1173 cols.append(" ." * (len(pp.decode_path) + 1))
1174 if isinstance(pp.blob, binary_type):
1175 blob = hexenc(pp.blob).upper()
1176 for i in range(0, len(blob), 32):
1177 chunk = blob[i:i + 32]
1178 yield " ".join(cols + [":".join(
1179 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1181 elif isinstance(pp.blob, tuple):
1182 yield " ".join(cols + [", ".join(pp.blob)])
1185 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1186 """Pretty print object
1188 :param Obj obj: object you want to pretty print
1189 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1190 from it is met, then its humand readable form is printed
1191 :param big_blobs: if large binary objects are met (like OctetString
1192 values), do we need to print them too, on separate
1194 :param with_colours: colourize output, if ``termcolor`` library
1197 def _pprint_pps(pps):
1199 if hasattr(pp, "_fields"):
1201 yield pp_console_row(
1206 with_colours=with_colours,
1208 for row in pp_console_blob(pp):
1211 yield pp_console_row(
1216 with_colours=with_colours,
1219 for row in _pprint_pps(pp):
1221 return "\n".join(_pprint_pps(obj.pps()))
1224 ########################################################################
1225 # ASN.1 primitive types
1226 ########################################################################
1229 """``BOOLEAN`` boolean type
1231 >>> b = Boolean(True)
1233 >>> b == Boolean(True)
1239 tag_default = tag_encode(1)
1240 asn1_type_name = "BOOLEAN"
1252 :param value: set the value. Either boolean type, or
1253 :py:class:`pyderasn.Boolean` object
1254 :param bytes impl: override default tag with ``IMPLICIT`` one
1255 :param bytes expl: override default tag with ``EXPLICIT`` one
1256 :param default: set default value. Type same as in ``value``
1257 :param bool optional: is object ``OPTIONAL`` in sequence
1259 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1260 self._value = None if value is None else self._value_sanitize(value)
1261 if default is not None:
1262 default = self._value_sanitize(default)
1263 self.default = self.__class__(
1269 self._value = default
1271 def _value_sanitize(self, value):
1272 if issubclass(value.__class__, Boolean):
1274 if isinstance(value, bool):
1276 raise InvalidValueType((self.__class__, bool))
1280 return self._value is not None
1283 obj = self.__class__()
1284 obj._value = self._value
1286 obj._expl = self._expl
1287 obj.default = self.default
1288 obj.optional = self.optional
1289 obj.offset = self.offset
1290 obj.llen = self.llen
1291 obj.vlen = self.vlen
1294 def __nonzero__(self):
1295 self._assert_ready()
1299 self._assert_ready()
1302 def __eq__(self, their):
1303 if isinstance(their, bool):
1304 return self._value == their
1305 if not issubclass(their.__class__, Boolean):
1308 self._value == their._value and
1309 self.tag == their.tag and
1310 self._expl == their._expl
1321 return self.__class__(
1323 impl=self.tag if impl is None else impl,
1324 expl=self._expl if expl is None else expl,
1325 default=self.default if default is None else default,
1326 optional=self.optional if optional is None else optional,
1330 self._assert_ready()
1334 (b"\xFF" if self._value else b"\x00"),
1337 def _decode(self, tlv, offset, decode_path, ctx):
1339 t, _, lv = tag_strip(tlv)
1340 except DecodeError as err:
1341 raise err.__class__(
1343 klass=self.__class__,
1344 decode_path=decode_path,
1349 klass=self.__class__,
1350 decode_path=decode_path,
1354 l, _, v = len_decode(lv)
1355 except DecodeError as err:
1356 raise err.__class__(
1358 klass=self.__class__,
1359 decode_path=decode_path,
1363 raise InvalidLength(
1364 "Boolean's length must be equal to 1",
1365 klass=self.__class__,
1366 decode_path=decode_path,
1370 raise NotEnoughData(
1371 "encoded length is longer than data",
1372 klass=self.__class__,
1373 decode_path=decode_path,
1376 first_octet = byte2int(v)
1377 if first_octet == 0:
1379 elif first_octet == 0xFF:
1383 "unacceptable Boolean value",
1384 klass=self.__class__,
1385 decode_path=decode_path,
1388 obj = self.__class__(
1392 default=self.default,
1393 optional=self.optional,
1394 _decoded=(offset, 1, 1),
1399 return pp_console_row(next(self.pps()))
1401 def pps(self, decode_path=()):
1403 asn1_type_name=self.asn1_type_name,
1404 obj_name=self.__class__.__name__,
1405 decode_path=decode_path,
1406 value=str(self._value) if self.ready else None,
1407 optional=self.optional,
1408 default=self == self.default,
1409 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1410 expl=None if self._expl is None else tag_decode(self._expl),
1415 expl_offset=self.expl_offset if self.expled else None,
1416 expl_tlen=self.expl_tlen if self.expled else None,
1417 expl_llen=self.expl_llen if self.expled else None,
1418 expl_vlen=self.expl_vlen if self.expled else None,
1423 """``INTEGER`` integer type
1425 >>> b = Integer(-123)
1427 >>> b == Integer(-123)
1432 >>> Integer(2, bounds=(1, 3))
1434 >>> Integer(5, bounds=(1, 3))
1435 Traceback (most recent call last):
1436 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1440 class Version(Integer):
1447 >>> v = Version("v1")
1454 {'v3': 2, 'v1': 0, 'v2': 1}
1456 __slots__ = ("specs", "_bound_min", "_bound_max")
1457 tag_default = tag_encode(2)
1458 asn1_type_name = "INTEGER"
1472 :param value: set the value. Either integer type, named value
1473 (if ``schema`` is specified in the class), or
1474 :py:class:`pyderasn.Integer` object
1475 :param bounds: set ``(MIN, MAX)`` value constraint.
1476 (-inf, +inf) by default
1477 :param bytes impl: override default tag with ``IMPLICIT`` one
1478 :param bytes expl: override default tag with ``EXPLICIT`` one
1479 :param default: set default value. Type same as in ``value``
1480 :param bool optional: is object ``OPTIONAL`` in sequence
1482 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1484 specs = getattr(self, "schema", {}) if _specs is None else _specs
1485 self.specs = specs if isinstance(specs, dict) else dict(specs)
1486 self._bound_min, self._bound_max = getattr(
1489 (float("-inf"), float("+inf")),
1490 ) if bounds is None else bounds
1491 if value is not None:
1492 self._value = self._value_sanitize(value)
1493 if default is not None:
1494 default = self._value_sanitize(default)
1495 self.default = self.__class__(
1501 if self._value is None:
1502 self._value = default
1504 def _value_sanitize(self, value):
1505 if issubclass(value.__class__, Integer):
1506 value = value._value
1507 elif isinstance(value, integer_types):
1509 elif isinstance(value, str):
1510 value = self.specs.get(value)
1512 raise ObjUnknown("integer value: %s" % value)
1514 raise InvalidValueType((self.__class__, int, str))
1515 if not self._bound_min <= value <= self._bound_max:
1516 raise BoundsError(self._bound_min, value, self._bound_max)
1521 return self._value is not None
1524 obj = self.__class__(_specs=self.specs)
1525 obj._value = self._value
1526 obj._bound_min = self._bound_min
1527 obj._bound_max = self._bound_max
1529 obj._expl = self._expl
1530 obj.default = self.default
1531 obj.optional = self.optional
1532 obj.offset = self.offset
1533 obj.llen = self.llen
1534 obj.vlen = self.vlen
1538 self._assert_ready()
1539 return int(self._value)
1542 self._assert_ready()
1545 bytes(self._expl or b"") +
1546 str(self._value).encode("ascii"),
1549 def __eq__(self, their):
1550 if isinstance(their, integer_types):
1551 return self._value == their
1552 if not issubclass(their.__class__, Integer):
1555 self._value == their._value and
1556 self.tag == their.tag and
1557 self._expl == their._expl
1560 def __lt__(self, their):
1561 return self._value < their._value
1565 for name, value in self.specs.items():
1566 if value == self._value:
1578 return self.__class__(
1581 (self._bound_min, self._bound_max)
1582 if bounds is None else bounds
1584 impl=self.tag if impl is None else impl,
1585 expl=self._expl if expl is None else expl,
1586 default=self.default if default is None else default,
1587 optional=self.optional if optional is None else optional,
1592 self._assert_ready()
1596 octets = bytearray([0])
1600 octets = bytearray()
1602 octets.append((value & 0xFF) ^ 0xFF)
1604 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1607 octets = bytearray()
1609 octets.append(value & 0xFF)
1611 if octets[-1] & 0x80 > 0:
1614 octets = bytes(octets)
1616 bytes_len = ceil(value.bit_length() / 8) or 1
1619 octets = value.to_bytes(
1624 except OverflowError:
1628 return b"".join((self.tag, len_encode(len(octets)), octets))
1630 def _decode(self, tlv, offset, decode_path, ctx):
1632 t, _, lv = tag_strip(tlv)
1633 except DecodeError as err:
1634 raise err.__class__(
1636 klass=self.__class__,
1637 decode_path=decode_path,
1642 klass=self.__class__,
1643 decode_path=decode_path,
1647 l, llen, v = len_decode(lv)
1648 except DecodeError as err:
1649 raise err.__class__(
1651 klass=self.__class__,
1652 decode_path=decode_path,
1656 raise NotEnoughData(
1657 "encoded length is longer than data",
1658 klass=self.__class__,
1659 decode_path=decode_path,
1663 raise NotEnoughData(
1665 klass=self.__class__,
1666 decode_path=decode_path,
1669 v, tail = v[:l], v[l:]
1670 first_octet = byte2int(v)
1672 second_octet = byte2int(v[1:])
1674 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1675 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1678 "non normalized integer",
1679 klass=self.__class__,
1680 decode_path=decode_path,
1685 if first_octet & 0x80 > 0:
1686 octets = bytearray()
1687 for octet in bytearray(v):
1688 octets.append(octet ^ 0xFF)
1689 for octet in octets:
1690 value = (value << 8) | octet
1694 for octet in bytearray(v):
1695 value = (value << 8) | octet
1697 value = int.from_bytes(v, byteorder="big", signed=True)
1699 obj = self.__class__(
1701 bounds=(self._bound_min, self._bound_max),
1704 default=self.default,
1705 optional=self.optional,
1707 _decoded=(offset, llen, l),
1709 except BoundsError as err:
1712 klass=self.__class__,
1713 decode_path=decode_path,
1719 return pp_console_row(next(self.pps()))
1721 def pps(self, decode_path=()):
1723 asn1_type_name=self.asn1_type_name,
1724 obj_name=self.__class__.__name__,
1725 decode_path=decode_path,
1726 value=(self.named or str(self._value)) if self.ready else None,
1727 optional=self.optional,
1728 default=self == self.default,
1729 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1730 expl=None if self._expl is None else tag_decode(self._expl),
1735 expl_offset=self.expl_offset if self.expled else None,
1736 expl_tlen=self.expl_tlen if self.expled else None,
1737 expl_llen=self.expl_llen if self.expled else None,
1738 expl_vlen=self.expl_vlen if self.expled else None,
1742 class BitString(Obj):
1743 """``BIT STRING`` bit string type
1745 >>> BitString(b"hello world")
1746 BIT STRING 88 bits 68656c6c6f20776f726c64
1749 >>> b == b"hello world"
1754 >>> b = BitString("'010110000000'B")
1755 BIT STRING 12 bits 5800
1758 >>> b[0], b[1], b[2], b[3]
1759 (False, True, False, True)
1763 [False, True, False, True, True, False, False, False, False, False, False, False]
1767 class KeyUsage(BitString):
1769 ('digitalSignature', 0),
1770 ('nonRepudiation', 1),
1771 ('keyEncipherment', 2),
1774 >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1775 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1777 ['nonRepudiation', 'keyEncipherment']
1779 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1781 __slots__ = ("specs", "defined")
1782 tag_default = tag_encode(3)
1783 asn1_type_name = "BIT STRING"
1796 :param value: set the value. Either binary type, tuple of named
1797 values (if ``schema`` is specified in the class),
1798 string in ``'XXX...'B`` form, or
1799 :py:class:`pyderasn.BitString` object
1800 :param bytes impl: override default tag with ``IMPLICIT`` one
1801 :param bytes expl: override default tag with ``EXPLICIT`` one
1802 :param default: set default value. Type same as in ``value``
1803 :param bool optional: is object ``OPTIONAL`` in sequence
1805 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1806 specs = getattr(self, "schema", {}) if _specs is None else _specs
1807 self.specs = specs if isinstance(specs, dict) else dict(specs)
1808 self._value = None if value is None else self._value_sanitize(value)
1809 if default is not None:
1810 default = self._value_sanitize(default)
1811 self.default = self.__class__(
1817 self._value = default
1820 def _bits2octets(self, bits):
1821 if len(self.specs) > 0:
1822 bits = bits.rstrip("0")
1824 bits += "0" * ((8 - (bit_len % 8)) % 8)
1825 octets = bytearray(len(bits) // 8)
1826 for i in six_xrange(len(octets)):
1827 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1828 return bit_len, bytes(octets)
1830 def _value_sanitize(self, value):
1831 if issubclass(value.__class__, BitString):
1833 if isinstance(value, (string_types, binary_type)):
1835 isinstance(value, string_types) and
1836 value.startswith("'") and
1837 value.endswith("'B")
1840 if not set(value) <= set(("0", "1")):
1841 raise ValueError("B's coding contains unacceptable chars")
1842 return self._bits2octets(value)
1843 elif isinstance(value, binary_type):
1844 return (len(value) * 8, value)
1846 raise InvalidValueType((
1851 if isinstance(value, tuple):
1854 isinstance(value[0], integer_types) and
1855 isinstance(value[1], binary_type)
1860 bit = self.specs.get(name)
1862 raise ObjUnknown("BitString value: %s" % name)
1865 return self._bits2octets("")
1867 return self._bits2octets("".join(
1868 ("1" if bit in bits else "0")
1869 for bit in six_xrange(max(bits) + 1)
1871 raise InvalidValueType((self.__class__, binary_type, string_types))
1875 return self._value is not None
1878 obj = self.__class__(_specs=self.specs)
1880 if value is not None:
1881 value = (value[0], value[1])
1884 obj._expl = self._expl
1885 obj.default = self.default
1886 obj.optional = self.optional
1887 obj.offset = self.offset
1888 obj.llen = self.llen
1889 obj.vlen = self.vlen
1893 self._assert_ready()
1894 for i in six_xrange(self._value[0]):
1899 self._assert_ready()
1900 return self._value[0]
1902 def __bytes__(self):
1903 self._assert_ready()
1904 return self._value[1]
1906 def __eq__(self, their):
1907 if isinstance(their, bytes):
1908 return self._value[1] == their
1909 if not issubclass(their.__class__, BitString):
1912 self._value == their._value and
1913 self.tag == their.tag and
1914 self._expl == their._expl
1919 return [name for name, bit in self.specs.items() if self[bit]]
1929 return self.__class__(
1931 impl=self.tag if impl is None else impl,
1932 expl=self._expl if expl is None else expl,
1933 default=self.default if default is None else default,
1934 optional=self.optional if optional is None else optional,
1938 def __getitem__(self, key):
1939 if isinstance(key, int):
1940 bit_len, octets = self._value
1944 byte2int(memoryview(octets)[key // 8:]) >>
1947 if isinstance(key, string_types):
1948 value = self.specs.get(key)
1950 raise ObjUnknown("BitString value: %s" % key)
1952 raise InvalidValueType((int, str))
1955 self._assert_ready()
1956 bit_len, octets = self._value
1959 len_encode(len(octets) + 1),
1960 int2byte((8 - bit_len % 8) % 8),
1964 def _decode(self, tlv, offset, decode_path, ctx):
1966 t, _, lv = tag_strip(tlv)
1967 except DecodeError as err:
1968 raise err.__class__(
1970 klass=self.__class__,
1971 decode_path=decode_path,
1976 klass=self.__class__,
1977 decode_path=decode_path,
1981 l, llen, v = len_decode(lv)
1982 except DecodeError as err:
1983 raise err.__class__(
1985 klass=self.__class__,
1986 decode_path=decode_path,
1990 raise NotEnoughData(
1991 "encoded length is longer than data",
1992 klass=self.__class__,
1993 decode_path=decode_path,
1997 raise NotEnoughData(
1999 klass=self.__class__,
2000 decode_path=decode_path,
2003 pad_size = byte2int(v)
2004 if l == 1 and pad_size != 0:
2006 "invalid empty value",
2007 klass=self.__class__,
2008 decode_path=decode_path,
2014 klass=self.__class__,
2015 decode_path=decode_path,
2018 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2021 klass=self.__class__,
2022 decode_path=decode_path,
2025 v, tail = v[:l], v[l:]
2026 obj = self.__class__(
2027 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2030 default=self.default,
2031 optional=self.optional,
2033 _decoded=(offset, llen, l),
2038 return pp_console_row(next(self.pps()))
2040 def pps(self, decode_path=()):
2044 bit_len, blob = self._value
2045 value = "%d bits" % bit_len
2046 if len(self.specs) > 0:
2047 blob = tuple(self.named)
2049 asn1_type_name=self.asn1_type_name,
2050 obj_name=self.__class__.__name__,
2051 decode_path=decode_path,
2054 optional=self.optional,
2055 default=self == self.default,
2056 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2057 expl=None if self._expl is None else tag_decode(self._expl),
2062 expl_offset=self.expl_offset if self.expled else None,
2063 expl_tlen=self.expl_tlen if self.expled else None,
2064 expl_llen=self.expl_llen if self.expled else None,
2065 expl_vlen=self.expl_vlen if self.expled else None,
2067 defined_by, defined = self.defined or (None, None)
2068 if defined_by is not None:
2070 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2074 class OctetString(Obj):
2075 """``OCTET STRING`` binary string type
2077 >>> s = OctetString(b"hello world")
2078 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2079 >>> s == OctetString(b"hello world")
2084 >>> OctetString(b"hello", bounds=(4, 4))
2085 Traceback (most recent call last):
2086 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2087 >>> OctetString(b"hell", bounds=(4, 4))
2088 OCTET STRING 4 bytes 68656c6c
2090 __slots__ = ("_bound_min", "_bound_max", "defined")
2091 tag_default = tag_encode(4)
2092 asn1_type_name = "OCTET STRING"
2105 :param value: set the value. Either binary type, or
2106 :py:class:`pyderasn.OctetString` object
2107 :param bounds: set ``(MIN, MAX)`` value size constraint.
2108 (-inf, +inf) by default
2109 :param bytes impl: override default tag with ``IMPLICIT`` one
2110 :param bytes expl: override default tag with ``EXPLICIT`` one
2111 :param default: set default value. Type same as in ``value``
2112 :param bool optional: is object ``OPTIONAL`` in sequence
2114 super(OctetString, self).__init__(
2122 self._bound_min, self._bound_max = getattr(
2126 ) if bounds is None else bounds
2127 if value is not None:
2128 self._value = self._value_sanitize(value)
2129 if default is not None:
2130 default = self._value_sanitize(default)
2131 self.default = self.__class__(
2136 if self._value is None:
2137 self._value = default
2140 def _value_sanitize(self, value):
2141 if issubclass(value.__class__, OctetString):
2142 value = value._value
2143 elif isinstance(value, binary_type):
2146 raise InvalidValueType((self.__class__, bytes))
2147 if not self._bound_min <= len(value) <= self._bound_max:
2148 raise BoundsError(self._bound_min, len(value), self._bound_max)
2153 return self._value is not None
2156 obj = self.__class__()
2157 obj._value = self._value
2158 obj._bound_min = self._bound_min
2159 obj._bound_max = self._bound_max
2161 obj._expl = self._expl
2162 obj.default = self.default
2163 obj.optional = self.optional
2164 obj.offset = self.offset
2165 obj.llen = self.llen
2166 obj.vlen = self.vlen
2169 def __bytes__(self):
2170 self._assert_ready()
2173 def __eq__(self, their):
2174 if isinstance(their, binary_type):
2175 return self._value == their
2176 if not issubclass(their.__class__, OctetString):
2179 self._value == their._value and
2180 self.tag == their.tag and
2181 self._expl == their._expl
2184 def __lt__(self, their):
2185 return self._value < their._value
2196 return self.__class__(
2199 (self._bound_min, self._bound_max)
2200 if bounds is None else bounds
2202 impl=self.tag if impl is None else impl,
2203 expl=self._expl if expl is None else expl,
2204 default=self.default if default is None else default,
2205 optional=self.optional if optional is None else optional,
2209 self._assert_ready()
2212 len_encode(len(self._value)),
2216 def _decode(self, tlv, offset, decode_path, ctx):
2218 t, _, lv = tag_strip(tlv)
2219 except DecodeError as err:
2220 raise err.__class__(
2222 klass=self.__class__,
2223 decode_path=decode_path,
2228 klass=self.__class__,
2229 decode_path=decode_path,
2233 l, llen, v = len_decode(lv)
2234 except DecodeError as err:
2235 raise err.__class__(
2237 klass=self.__class__,
2238 decode_path=decode_path,
2242 raise NotEnoughData(
2243 "encoded length is longer than data",
2244 klass=self.__class__,
2245 decode_path=decode_path,
2248 v, tail = v[:l], v[l:]
2250 obj = self.__class__(
2252 bounds=(self._bound_min, self._bound_max),
2255 default=self.default,
2256 optional=self.optional,
2257 _decoded=(offset, llen, l),
2259 except DecodeError as err:
2262 klass=self.__class__,
2263 decode_path=decode_path,
2266 except BoundsError as err:
2269 klass=self.__class__,
2270 decode_path=decode_path,
2276 return pp_console_row(next(self.pps()))
2278 def pps(self, decode_path=()):
2280 asn1_type_name=self.asn1_type_name,
2281 obj_name=self.__class__.__name__,
2282 decode_path=decode_path,
2283 value=("%d bytes" % len(self._value)) if self.ready else None,
2284 blob=self._value if self.ready else None,
2285 optional=self.optional,
2286 default=self == self.default,
2287 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2288 expl=None if self._expl is None else tag_decode(self._expl),
2293 expl_offset=self.expl_offset if self.expled else None,
2294 expl_tlen=self.expl_tlen if self.expled else None,
2295 expl_llen=self.expl_llen if self.expled else None,
2296 expl_vlen=self.expl_vlen if self.expled else None,
2298 defined_by, defined = self.defined or (None, None)
2299 if defined_by is not None:
2301 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2306 """``NULL`` null object
2314 tag_default = tag_encode(5)
2315 asn1_type_name = "NULL"
2319 value=None, # unused, but Sequence passes it
2326 :param bytes impl: override default tag with ``IMPLICIT`` one
2327 :param bytes expl: override default tag with ``EXPLICIT`` one
2328 :param bool optional: is object ``OPTIONAL`` in sequence
2330 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2338 obj = self.__class__()
2340 obj._expl = self._expl
2341 obj.default = self.default
2342 obj.optional = self.optional
2343 obj.offset = self.offset
2344 obj.llen = self.llen
2345 obj.vlen = self.vlen
2348 def __eq__(self, their):
2349 if not issubclass(their.__class__, Null):
2352 self.tag == their.tag and
2353 self._expl == their._expl
2363 return self.__class__(
2364 impl=self.tag if impl is None else impl,
2365 expl=self._expl if expl is None else expl,
2366 optional=self.optional if optional is None else optional,
2370 return self.tag + len_encode(0)
2372 def _decode(self, tlv, offset, decode_path, ctx):
2374 t, _, lv = tag_strip(tlv)
2375 except DecodeError as err:
2376 raise err.__class__(
2378 klass=self.__class__,
2379 decode_path=decode_path,
2384 klass=self.__class__,
2385 decode_path=decode_path,
2389 l, _, v = len_decode(lv)
2390 except DecodeError as err:
2391 raise err.__class__(
2393 klass=self.__class__,
2394 decode_path=decode_path,
2398 raise InvalidLength(
2399 "Null must have zero length",
2400 klass=self.__class__,
2401 decode_path=decode_path,
2404 obj = self.__class__(
2407 optional=self.optional,
2408 _decoded=(offset, 1, 0),
2413 return pp_console_row(next(self.pps()))
2415 def pps(self, decode_path=()):
2417 asn1_type_name=self.asn1_type_name,
2418 obj_name=self.__class__.__name__,
2419 decode_path=decode_path,
2420 optional=self.optional,
2421 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2422 expl=None if self._expl is None else tag_decode(self._expl),
2427 expl_offset=self.expl_offset if self.expled else None,
2428 expl_tlen=self.expl_tlen if self.expled else None,
2429 expl_llen=self.expl_llen if self.expled else None,
2430 expl_vlen=self.expl_vlen if self.expled else None,
2434 class ObjectIdentifier(Obj):
2435 """``OBJECT IDENTIFIER`` OID type
2437 >>> oid = ObjectIdentifier((1, 2, 3))
2438 OBJECT IDENTIFIER 1.2.3
2439 >>> oid == ObjectIdentifier("1.2.3")
2445 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2446 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2448 >>> str(ObjectIdentifier((3, 1)))
2449 Traceback (most recent call last):
2450 pyderasn.InvalidOID: unacceptable first arc value
2452 __slots__ = ("defines",)
2453 tag_default = tag_encode(6)
2454 asn1_type_name = "OBJECT IDENTIFIER"
2467 :param value: set the value. Either tuples of integers,
2468 string of "."-concatenated integers, or
2469 :py:class:`pyderasn.ObjectIdentifier` object
2470 :param defines: sequence of tuples. Each tuple has two elements.
2471 First one is relative to current one decode
2472 path, aiming to the field defined by that OID.
2473 Read about relative path in
2474 :py:func:`pyderasn.abs_decode_path`. Second
2475 tuple element is ``{OID: pyderasn.Obj()}``
2476 dictionary, mapping between current OID value
2477 and structure applied to defined field.
2478 :ref:`Read about DEFINED BY <definedby>`
2479 :param bytes impl: override default tag with ``IMPLICIT`` one
2480 :param bytes expl: override default tag with ``EXPLICIT`` one
2481 :param default: set default value. Type same as in ``value``
2482 :param bool optional: is object ``OPTIONAL`` in sequence
2484 super(ObjectIdentifier, self).__init__(
2492 if value is not None:
2493 self._value = self._value_sanitize(value)
2494 if default is not None:
2495 default = self._value_sanitize(default)
2496 self.default = self.__class__(
2501 if self._value is None:
2502 self._value = default
2503 self.defines = defines
2505 def __add__(self, their):
2506 if isinstance(their, self.__class__):
2507 return self.__class__(self._value + their._value)
2508 if isinstance(their, tuple):
2509 return self.__class__(self._value + their)
2510 raise InvalidValueType((self.__class__, tuple))
2512 def _value_sanitize(self, value):
2513 if issubclass(value.__class__, ObjectIdentifier):
2515 if isinstance(value, string_types):
2517 value = tuple(int(arc) for arc in value.split("."))
2519 raise InvalidOID("unacceptable arcs values")
2520 if isinstance(value, tuple):
2522 raise InvalidOID("less than 2 arcs")
2523 first_arc = value[0]
2524 if first_arc in (0, 1):
2525 if not (0 <= value[1] <= 39):
2526 raise InvalidOID("second arc is too wide")
2527 elif first_arc == 2:
2530 raise InvalidOID("unacceptable first arc value")
2532 raise InvalidValueType((self.__class__, str, tuple))
2536 return self._value is not None
2539 obj = self.__class__()
2540 obj._value = self._value
2541 obj.defines = self.defines
2543 obj._expl = self._expl
2544 obj.default = self.default
2545 obj.optional = self.optional
2546 obj.offset = self.offset
2547 obj.llen = self.llen
2548 obj.vlen = self.vlen
2552 self._assert_ready()
2553 return iter(self._value)
2556 return ".".join(str(arc) for arc in self._value or ())
2559 self._assert_ready()
2562 bytes(self._expl or b"") +
2563 str(self._value).encode("ascii"),
2566 def __eq__(self, their):
2567 if isinstance(their, tuple):
2568 return self._value == their
2569 if not issubclass(their.__class__, ObjectIdentifier):
2572 self.tag == their.tag and
2573 self._expl == their._expl and
2574 self._value == their._value
2577 def __lt__(self, their):
2578 return self._value < their._value
2589 return self.__class__(
2591 defines=self.defines if defines is None else defines,
2592 impl=self.tag if impl is None else impl,
2593 expl=self._expl if expl is None else expl,
2594 default=self.default if default is None else default,
2595 optional=self.optional if optional is None else optional,
2599 self._assert_ready()
2601 first_value = value[1]
2602 first_arc = value[0]
2605 elif first_arc == 1:
2607 elif first_arc == 2:
2609 else: # pragma: no cover
2610 raise RuntimeError("invalid arc is stored")
2611 octets = [zero_ended_encode(first_value)]
2612 for arc in value[2:]:
2613 octets.append(zero_ended_encode(arc))
2614 v = b"".join(octets)
2615 return b"".join((self.tag, len_encode(len(v)), v))
2617 def _decode(self, tlv, offset, decode_path, ctx):
2619 t, _, lv = tag_strip(tlv)
2620 except DecodeError as err:
2621 raise err.__class__(
2623 klass=self.__class__,
2624 decode_path=decode_path,
2629 klass=self.__class__,
2630 decode_path=decode_path,
2634 l, llen, v = len_decode(lv)
2635 except DecodeError as err:
2636 raise err.__class__(
2638 klass=self.__class__,
2639 decode_path=decode_path,
2643 raise NotEnoughData(
2644 "encoded length is longer than data",
2645 klass=self.__class__,
2646 decode_path=decode_path,
2650 raise NotEnoughData(
2652 klass=self.__class__,
2653 decode_path=decode_path,
2656 v, tail = v[:l], v[l:]
2662 octet = indexbytes(v, i)
2663 arc = (arc << 7) | (octet & 0x7F)
2664 if octet & 0x80 == 0:
2672 klass=self.__class__,
2673 decode_path=decode_path,
2677 second_arc = arcs[0]
2678 if 0 <= second_arc <= 39:
2680 elif 40 <= second_arc <= 79:
2686 obj = self.__class__(
2687 value=tuple([first_arc, second_arc] + arcs[1:]),
2690 default=self.default,
2691 optional=self.optional,
2692 _decoded=(offset, llen, l),
2697 return pp_console_row(next(self.pps()))
2699 def pps(self, decode_path=()):
2701 asn1_type_name=self.asn1_type_name,
2702 obj_name=self.__class__.__name__,
2703 decode_path=decode_path,
2704 value=str(self) if self.ready else None,
2705 optional=self.optional,
2706 default=self == self.default,
2707 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2708 expl=None if self._expl is None else tag_decode(self._expl),
2713 expl_offset=self.expl_offset if self.expled else None,
2714 expl_tlen=self.expl_tlen if self.expled else None,
2715 expl_llen=self.expl_llen if self.expled else None,
2716 expl_vlen=self.expl_vlen if self.expled else None,
2720 class Enumerated(Integer):
2721 """``ENUMERATED`` integer type
2723 This type is identical to :py:class:`pyderasn.Integer`, but requires
2724 schema to be specified and does not accept values missing from it.
2727 tag_default = tag_encode(10)
2728 asn1_type_name = "ENUMERATED"
2739 bounds=None, # dummy argument, workability for Integer.decode
2741 super(Enumerated, self).__init__(
2750 if len(self.specs) == 0:
2751 raise ValueError("schema must be specified")
2753 def _value_sanitize(self, value):
2754 if isinstance(value, self.__class__):
2755 value = value._value
2756 elif isinstance(value, integer_types):
2757 if value not in list(self.specs.values()):
2759 "unknown integer value: %s" % value,
2760 klass=self.__class__,
2762 elif isinstance(value, string_types):
2763 value = self.specs.get(value)
2765 raise ObjUnknown("integer value: %s" % value)
2767 raise InvalidValueType((self.__class__, int, str))
2771 obj = self.__class__(_specs=self.specs)
2772 obj._value = self._value
2773 obj._bound_min = self._bound_min
2774 obj._bound_max = self._bound_max
2776 obj._expl = self._expl
2777 obj.default = self.default
2778 obj.optional = self.optional
2779 obj.offset = self.offset
2780 obj.llen = self.llen
2781 obj.vlen = self.vlen
2793 return self.__class__(
2795 impl=self.tag if impl is None else impl,
2796 expl=self._expl if expl is None else expl,
2797 default=self.default if default is None else default,
2798 optional=self.optional if optional is None else optional,
2803 class CommonString(OctetString):
2804 """Common class for all strings
2806 Everything resembles :py:class:`pyderasn.OctetString`, except
2807 ability to deal with unicode text strings.
2809 >>> hexenc("привет мир".encode("utf-8"))
2810 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2811 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2813 >>> s = UTF8String("привет мир")
2814 UTF8String UTF8String привет мир
2816 'привет мир'
2817 >>> hexenc(bytes(s))
2818 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2820 >>> PrintableString("привет мир")
2821 Traceback (most recent call last):
2822 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2824 >>> BMPString("ада", bounds=(2, 2))
2825 Traceback (most recent call last):
2826 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2827 >>> s = BMPString("ад", bounds=(2, 2))
2830 >>> hexenc(bytes(s))
2838 * - :py:class:`pyderasn.UTF8String`
2840 * - :py:class:`pyderasn.NumericString`
2842 * - :py:class:`pyderasn.PrintableString`
2844 * - :py:class:`pyderasn.TeletexString`
2846 * - :py:class:`pyderasn.T61String`
2848 * - :py:class:`pyderasn.VideotexString`
2850 * - :py:class:`pyderasn.IA5String`
2852 * - :py:class:`pyderasn.GraphicString`
2854 * - :py:class:`pyderasn.VisibleString`
2856 * - :py:class:`pyderasn.ISO646String`
2858 * - :py:class:`pyderasn.GeneralString`
2860 * - :py:class:`pyderasn.UniversalString`
2862 * - :py:class:`pyderasn.BMPString`
2865 __slots__ = ("encoding",)
2867 def _value_sanitize(self, value):
2869 value_decoded = None
2870 if isinstance(value, self.__class__):
2871 value_raw = value._value
2872 elif isinstance(value, text_type):
2873 value_decoded = value
2874 elif isinstance(value, binary_type):
2877 raise InvalidValueType((self.__class__, text_type, binary_type))
2880 value_decoded.encode(self.encoding)
2881 if value_raw is None else value_raw
2884 value_raw.decode(self.encoding)
2885 if value_decoded is None else value_decoded
2887 except (UnicodeEncodeError, UnicodeDecodeError) as err:
2888 raise DecodeError(str(err))
2889 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2897 def __eq__(self, their):
2898 if isinstance(their, binary_type):
2899 return self._value == their
2900 if isinstance(their, text_type):
2901 return self._value == their.encode(self.encoding)
2902 if not isinstance(their, self.__class__):
2905 self._value == their._value and
2906 self.tag == their.tag and
2907 self._expl == their._expl
2910 def __unicode__(self):
2912 return self._value.decode(self.encoding)
2913 return text_type(self._value)
2916 return pp_console_row(next(self.pps(no_unicode=PY2)))
2918 def pps(self, decode_path=(), no_unicode=False):
2921 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2923 asn1_type_name=self.asn1_type_name,
2924 obj_name=self.__class__.__name__,
2925 decode_path=decode_path,
2927 optional=self.optional,
2928 default=self == self.default,
2929 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2930 expl=None if self._expl is None else tag_decode(self._expl),
2938 class UTF8String(CommonString):
2940 tag_default = tag_encode(12)
2942 asn1_type_name = "UTF8String"
2945 class NumericString(CommonString):
2947 tag_default = tag_encode(18)
2949 asn1_type_name = "NumericString"
2950 allowable_chars = set(digits.encode("ascii"))
2952 def _value_sanitize(self, value):
2953 value = super(NumericString, self)._value_sanitize(value)
2954 if not set(value) <= self.allowable_chars:
2955 raise DecodeError("non-numeric value")
2959 class PrintableString(CommonString):
2961 tag_default = tag_encode(19)
2963 asn1_type_name = "PrintableString"
2966 class TeletexString(CommonString):
2968 tag_default = tag_encode(20)
2970 asn1_type_name = "TeletexString"
2973 class T61String(TeletexString):
2975 asn1_type_name = "T61String"
2978 class VideotexString(CommonString):
2980 tag_default = tag_encode(21)
2981 encoding = "iso-8859-1"
2982 asn1_type_name = "VideotexString"
2985 class IA5String(CommonString):
2987 tag_default = tag_encode(22)
2989 asn1_type_name = "IA5"
2992 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
2993 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
2994 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
2997 class UTCTime(CommonString):
2998 """``UTCTime`` datetime type
3000 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3001 UTCTime UTCTime 2017-09-30T22:07:50
3007 datetime.datetime(2017, 9, 30, 22, 7, 50)
3008 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3009 datetime.datetime(1957, 9, 30, 22, 7, 50)
3012 tag_default = tag_encode(23)
3014 asn1_type_name = "UTCTime"
3016 fmt = "%y%m%d%H%M%SZ"
3026 bounds=None, # dummy argument, workability for OctetString.decode
3029 :param value: set the value. Either datetime type, or
3030 :py:class:`pyderasn.UTCTime` object
3031 :param bytes impl: override default tag with ``IMPLICIT`` one
3032 :param bytes expl: override default tag with ``EXPLICIT`` one
3033 :param default: set default value. Type same as in ``value``
3034 :param bool optional: is object ``OPTIONAL`` in sequence
3036 super(UTCTime, self).__init__(
3044 if value is not None:
3045 self._value = self._value_sanitize(value)
3046 if default is not None:
3047 default = self._value_sanitize(default)
3048 self.default = self.__class__(
3053 if self._value is None:
3054 self._value = default
3056 def _value_sanitize(self, value):
3057 if isinstance(value, self.__class__):
3059 if isinstance(value, datetime):
3060 return value.strftime(self.fmt).encode("ascii")
3061 if isinstance(value, binary_type):
3062 value_decoded = value.decode("ascii")
3063 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3065 datetime.strptime(value_decoded, self.fmt)
3067 raise DecodeError("invalid UTCTime format")
3070 raise DecodeError("invalid UTCTime length")
3071 raise InvalidValueType((self.__class__, datetime))
3073 def __eq__(self, their):
3074 if isinstance(their, binary_type):
3075 return self._value == their
3076 if isinstance(their, datetime):
3077 return self.todatetime() == their
3078 if not isinstance(their, self.__class__):
3081 self._value == their._value and
3082 self.tag == their.tag and
3083 self._expl == their._expl
3086 def todatetime(self):
3087 """Convert to datetime
3091 Pay attention that UTCTime can not hold full year, so all years
3092 having < 50 years are treated as 20xx, 19xx otherwise, according
3093 to X.509 recomendation.
3095 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3096 year = value.year % 100
3098 year=(2000 + year) if year < 50 else (1900 + year),
3102 minute=value.minute,
3103 second=value.second,
3107 return pp_console_row(next(self.pps()))
3109 def pps(self, decode_path=()):
3111 asn1_type_name=self.asn1_type_name,
3112 obj_name=self.__class__.__name__,
3113 decode_path=decode_path,
3114 value=self.todatetime().isoformat() if self.ready else None,
3115 optional=self.optional,
3116 default=self == self.default,
3117 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3118 expl=None if self._expl is None else tag_decode(self._expl),
3126 class GeneralizedTime(UTCTime):
3127 """``GeneralizedTime`` datetime type
3129 This type is similar to :py:class:`pyderasn.UTCTime`.
3131 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3132 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3134 '20170930220750.000123Z'
3135 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3136 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3139 tag_default = tag_encode(24)
3140 asn1_type_name = "GeneralizedTime"
3142 fmt = "%Y%m%d%H%M%SZ"
3143 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3145 def _value_sanitize(self, value):
3146 if isinstance(value, self.__class__):
3148 if isinstance(value, datetime):
3149 return value.strftime(
3150 self.fmt_ms if value.microsecond > 0 else self.fmt
3152 if isinstance(value, binary_type):
3153 value_decoded = value.decode("ascii")
3154 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3156 datetime.strptime(value_decoded, self.fmt)
3159 "invalid GeneralizedTime (without ms) format",
3162 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3164 datetime.strptime(value_decoded, self.fmt_ms)
3167 "invalid GeneralizedTime (with ms) format",
3172 "invalid GeneralizedTime length",
3173 klass=self.__class__,
3175 raise InvalidValueType((self.__class__, datetime))
3177 def todatetime(self):
3178 value = self._value.decode("ascii")
3179 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3180 return datetime.strptime(value, self.fmt)
3181 return datetime.strptime(value, self.fmt_ms)
3184 class GraphicString(CommonString):
3186 tag_default = tag_encode(25)
3187 encoding = "iso-8859-1"
3188 asn1_type_name = "GraphicString"
3191 class VisibleString(CommonString):
3193 tag_default = tag_encode(26)
3195 asn1_type_name = "VisibleString"
3198 class ISO646String(VisibleString):
3200 asn1_type_name = "ISO646String"
3203 class GeneralString(CommonString):
3205 tag_default = tag_encode(27)
3206 encoding = "iso-8859-1"
3207 asn1_type_name = "GeneralString"
3210 class UniversalString(CommonString):
3212 tag_default = tag_encode(28)
3213 encoding = "utf-32-be"
3214 asn1_type_name = "UniversalString"
3217 class BMPString(CommonString):
3219 tag_default = tag_encode(30)
3220 encoding = "utf-16-be"
3221 asn1_type_name = "BMPString"
3225 """``CHOICE`` special type
3229 class GeneralName(Choice):
3231 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
3232 ('dNSName', IA5String(impl=tag_ctxp(2))),
3235 >>> gn = GeneralName()
3237 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3238 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3239 >>> gn["dNSName"] = IA5String("bar.baz")
3240 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3241 >>> gn["rfc822Name"]
3244 [2] IA5String IA5 bar.baz
3247 >>> gn.value == gn["dNSName"]
3250 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3252 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3253 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3255 __slots__ = ("specs",)
3257 asn1_type_name = "CHOICE"
3270 :param value: set the value. Either ``(choice, value)`` tuple, or
3271 :py:class:`pyderasn.Choice` object
3272 :param bytes impl: can not be set, do **not** use it
3273 :param bytes expl: override default tag with ``EXPLICIT`` one
3274 :param default: set default value. Type same as in ``value``
3275 :param bool optional: is object ``OPTIONAL`` in sequence
3277 if impl is not None:
3278 raise ValueError("no implicit tag allowed for CHOICE")
3279 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3281 schema = getattr(self, "schema", ())
3282 if len(schema) == 0:
3283 raise ValueError("schema must be specified")
3285 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3288 if value is not None:
3289 self._value = self._value_sanitize(value)
3290 if default is not None:
3291 default_value = self._value_sanitize(default)
3292 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3293 default_obj.specs = self.specs
3294 default_obj._value = default_value
3295 self.default = default_obj
3297 self._value = default_obj.copy()._value
3299 def _value_sanitize(self, value):
3300 if isinstance(value, self.__class__):
3302 if isinstance(value, tuple) and len(value) == 2:
3304 spec = self.specs.get(choice)
3306 raise ObjUnknown(choice)
3307 if not isinstance(obj, spec.__class__):
3308 raise InvalidValueType((spec,))
3309 return (choice, spec(obj))
3310 raise InvalidValueType((self.__class__, tuple))
3314 return self._value is not None and self._value[1].ready
3317 obj = self.__class__(schema=self.specs)
3318 obj._expl = self._expl
3319 obj.default = self.default
3320 obj.optional = self.optional
3321 obj.offset = self.offset
3322 obj.llen = self.llen
3323 obj.vlen = self.vlen
3325 if value is not None:
3326 obj._value = (value[0], value[1].copy())
3329 def __eq__(self, their):
3330 if isinstance(their, tuple) and len(their) == 2:
3331 return self._value == their
3332 if not isinstance(their, self.__class__):
3335 self.specs == their.specs and
3336 self._value == their._value
3346 return self.__class__(
3349 expl=self._expl if expl is None else expl,
3350 default=self.default if default is None else default,
3351 optional=self.optional if optional is None else optional,
3356 self._assert_ready()
3357 return self._value[0]
3361 self._assert_ready()
3362 return self._value[1]
3364 def __getitem__(self, key):
3365 if key not in self.specs:
3366 raise ObjUnknown(key)
3367 if self._value is None:
3369 choice, value = self._value
3374 def __setitem__(self, key, value):
3375 spec = self.specs.get(key)
3377 raise ObjUnknown(key)
3378 if not isinstance(value, spec.__class__):
3379 raise InvalidValueType((spec.__class__,))
3380 self._value = (key, spec(value))
3388 return self._value[1].decoded if self.ready else False
3391 self._assert_ready()
3392 return self._value[1].encode()
3394 def _decode(self, tlv, offset, decode_path, ctx):
3395 for choice, spec in self.specs.items():
3397 value, tail = spec.decode(
3401 decode_path=decode_path + (choice,),
3406 obj = self.__class__(
3409 default=self.default,
3410 optional=self.optional,
3411 _decoded=(offset, 0, value.tlvlen),
3413 obj._value = (choice, value)
3416 klass=self.__class__,
3417 decode_path=decode_path,
3422 value = pp_console_row(next(self.pps()))
3424 value = "%s[%r]" % (value, self.value)
3427 def pps(self, decode_path=()):
3429 asn1_type_name=self.asn1_type_name,
3430 obj_name=self.__class__.__name__,
3431 decode_path=decode_path,
3432 value=self.choice if self.ready else None,
3433 optional=self.optional,
3434 default=self == self.default,
3435 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3436 expl=None if self._expl is None else tag_decode(self._expl),
3443 yield self.value.pps(decode_path=decode_path + (self.choice,))
3446 class PrimitiveTypes(Choice):
3447 """Predefined ``CHOICE`` for all generic primitive types
3449 It could be useful for general decoding of some unspecified values:
3451 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3452 OCTET STRING 3 bytes 666f6f
3453 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3457 schema = tuple((klass.__name__, klass()) for klass in (
3482 """``ANY`` special type
3484 >>> Any(Integer(-123))
3486 >>> a = Any(OctetString(b"hello world").encode())
3487 ANY 040b68656c6c6f20776f726c64
3488 >>> hexenc(bytes(a))
3489 b'0x040x0bhello world'
3491 __slots__ = ("defined",)
3492 tag_default = tag_encode(0)
3493 asn1_type_name = "ANY"
3503 :param value: set the value. Either any kind of pyderasn's
3504 **ready** object, or bytes. Pay attention that
3505 **no** validation is performed is raw binary value
3507 :param bytes expl: override default tag with ``EXPLICIT`` one
3508 :param bool optional: is object ``OPTIONAL`` in sequence
3510 super(Any, self).__init__(None, expl, None, optional, _decoded)
3511 self._value = None if value is None else self._value_sanitize(value)
3514 def _value_sanitize(self, value):
3515 if isinstance(value, self.__class__):
3517 if isinstance(value, Obj):
3518 return value.encode()
3519 if isinstance(value, binary_type):
3521 raise InvalidValueType((self.__class__, Obj, binary_type))
3525 return self._value is not None
3528 obj = self.__class__()
3529 obj._value = self._value
3531 obj._expl = self._expl
3532 obj.optional = self.optional
3533 obj.offset = self.offset
3534 obj.llen = self.llen
3535 obj.vlen = self.vlen
3538 def __eq__(self, their):
3539 if isinstance(their, binary_type):
3540 return self._value == their
3541 if issubclass(their.__class__, Any):
3542 return self._value == their._value
3551 return self.__class__(
3553 expl=self._expl if expl is None else expl,
3554 optional=self.optional if optional is None else optional,
3557 def __bytes__(self):
3558 self._assert_ready()
3566 self._assert_ready()
3569 def _decode(self, tlv, offset, decode_path, ctx):
3571 t, tlen, lv = tag_strip(tlv)
3572 l, llen, v = len_decode(lv)
3573 except DecodeError as err:
3574 raise err.__class__(
3576 klass=self.__class__,
3577 decode_path=decode_path,
3581 raise NotEnoughData(
3582 "encoded length is longer than data",
3583 klass=self.__class__,
3584 decode_path=decode_path,
3587 tlvlen = tlen + llen + l
3588 v, tail = tlv[:tlvlen], v[l:]
3589 obj = self.__class__(
3592 optional=self.optional,
3593 _decoded=(offset, 0, tlvlen),
3599 return pp_console_row(next(self.pps()))
3601 def pps(self, decode_path=()):
3603 asn1_type_name=self.asn1_type_name,
3604 obj_name=self.__class__.__name__,
3605 decode_path=decode_path,
3606 blob=self._value if self.ready else None,
3607 optional=self.optional,
3608 default=self == self.default,
3609 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3610 expl=None if self._expl is None else tag_decode(self._expl),
3615 expl_offset=self.expl_offset if self.expled else None,
3616 expl_tlen=self.expl_tlen if self.expled else None,
3617 expl_llen=self.expl_llen if self.expled else None,
3618 expl_vlen=self.expl_vlen if self.expled else None,
3620 defined_by, defined = self.defined or (None, None)
3621 if defined_by is not None:
3623 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3627 ########################################################################
3628 # ASN.1 constructed types
3629 ########################################################################
3631 def get_def_by_path(defines_by_path, sub_decode_path):
3632 """Get define by decode path
3634 for path, define in defines_by_path:
3635 if len(path) != len(sub_decode_path):
3637 for p1, p2 in zip(path, sub_decode_path):
3638 if (p1 != any) and (p1 != p2):
3644 def abs_decode_path(decode_path, rel_path):
3645 """Create an absolute decode path from current and relative ones
3647 :param decode_path: current decode path, starting point.
3649 :param rel_path: relative path to ``decode_path``. Tuple of strings.
3650 If first tuple's element is "/", then treat it as
3651 an absolute path, ignoring ``decode_path`` as
3652 starting point. Also this tuple can contain ".."
3653 elements, stripping the leading element from
3656 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
3657 ("foo", "bar", "baz", "whatever")
3658 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
3660 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
3663 if rel_path[0] == "/":
3665 if rel_path[0] == "..":
3666 return abs_decode_path(decode_path[:-1], rel_path[1:])
3667 return decode_path + rel_path
3670 class Sequence(Obj):
3671 """``SEQUENCE`` structure type
3673 You have to make specification of sequence::
3675 class Extension(Sequence):
3677 ("extnID", ObjectIdentifier()),
3678 ("critical", Boolean(default=False)),
3679 ("extnValue", OctetString()),
3682 Then, you can work with it as with dictionary.
3684 >>> ext = Extension()
3685 >>> Extension().specs
3687 ('extnID', OBJECT IDENTIFIER),
3688 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3689 ('extnValue', OCTET STRING),
3691 >>> ext["extnID"] = "1.2.3"
3692 Traceback (most recent call last):
3693 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3694 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3696 You can know if sequence is ready to be encoded:
3701 Traceback (most recent call last):
3702 pyderasn.ObjNotReady: object is not ready: extnValue
3703 >>> ext["extnValue"] = OctetString(b"foobar")
3707 Value you want to assign, must have the same **type** as in
3708 corresponding specification, but it can have different tags,
3709 optional/default attributes -- they will be taken from specification
3712 class TBSCertificate(Sequence):
3714 ("version", Version(expl=tag_ctxc(0), default="v1")),
3717 >>> tbs = TBSCertificate()
3718 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3720 Assign ``None`` to remove value from sequence.
3722 You can know if value exists/set in the sequence and take its value:
3724 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3727 OBJECT IDENTIFIER 1.2.3
3729 But pay attention that if value has default, then it won't be (not
3730 in) in the sequence (because ``DEFAULT`` must not be encoded in
3731 DER), but you can read its value:
3733 >>> "critical" in ext, ext["critical"]
3734 (False, BOOLEAN False)
3735 >>> ext["critical"] = Boolean(True)
3736 >>> "critical" in ext, ext["critical"]
3737 (True, BOOLEAN True)
3739 All defaulted values are always optional.
3741 .. _strict_default_existence_ctx:
3745 When decoded DER contains defaulted value inside, then
3746 technically this is not valid DER encoding. But we allow and pass
3747 it **by default**. Of course reencoding of that kind of DER will
3748 result in different binary representation (validly without
3749 defaulted value inside). You can enable strict defaulted values
3750 existence validation by setting ``"strict_default_existence":
3751 True`` :ref:`context <ctx>` option -- decoding process will raise
3752 an exception if defaulted value is met.
3754 Two sequences are equal if they have equal specification (schema),
3755 implicit/explicit tagging and the same values.
3757 __slots__ = ("specs",)
3758 tag_default = tag_encode(form=TagFormConstructed, num=16)
3759 asn1_type_name = "SEQUENCE"
3771 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3773 schema = getattr(self, "schema", ())
3775 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3778 if value is not None:
3779 self._value = self._value_sanitize(value)
3780 if default is not None:
3781 default_value = self._value_sanitize(default)
3782 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3783 default_obj.specs = self.specs
3784 default_obj._value = default_value
3785 self.default = default_obj
3787 self._value = default_obj.copy()._value
3789 def _value_sanitize(self, value):
3790 if not issubclass(value.__class__, Sequence):
3791 raise InvalidValueType((Sequence,))
3796 for name, spec in self.specs.items():
3797 value = self._value.get(name)
3808 obj = self.__class__(schema=self.specs)
3810 obj._expl = self._expl
3811 obj.default = self.default
3812 obj.optional = self.optional
3813 obj.offset = self.offset
3814 obj.llen = self.llen
3815 obj.vlen = self.vlen
3816 obj._value = {k: v.copy() for k, v in self._value.items()}
3819 def __eq__(self, their):
3820 if not isinstance(their, self.__class__):
3823 self.specs == their.specs and
3824 self.tag == their.tag and
3825 self._expl == their._expl and
3826 self._value == their._value
3837 return self.__class__(
3840 impl=self.tag if impl is None else impl,
3841 expl=self._expl if expl is None else expl,
3842 default=self.default if default is None else default,
3843 optional=self.optional if optional is None else optional,
3846 def __contains__(self, key):
3847 return key in self._value
3849 def __setitem__(self, key, value):
3850 spec = self.specs.get(key)
3852 raise ObjUnknown(key)
3854 self._value.pop(key, None)
3856 if not isinstance(value, spec.__class__):
3857 raise InvalidValueType((spec.__class__,))
3858 value = spec(value=value)
3859 if spec.default is not None and value == spec.default:
3860 self._value.pop(key, None)
3862 self._value[key] = value
3864 def __getitem__(self, key):
3865 value = self._value.get(key)
3866 if value is not None:
3868 spec = self.specs.get(key)
3870 raise ObjUnknown(key)
3871 if spec.default is not None:
3875 def _encoded_values(self):
3877 for name, spec in self.specs.items():
3878 value = self._value.get(name)
3882 raise ObjNotReady(name)
3883 raws.append(value.encode())
3887 v = b"".join(self._encoded_values())
3888 return b"".join((self.tag, len_encode(len(v)), v))
3890 def _decode(self, tlv, offset, decode_path, ctx):
3892 t, tlen, lv = tag_strip(tlv)
3893 except DecodeError as err:
3894 raise err.__class__(
3896 klass=self.__class__,
3897 decode_path=decode_path,
3902 klass=self.__class__,
3903 decode_path=decode_path,
3907 l, llen, v = len_decode(lv)
3908 except DecodeError as err:
3909 raise err.__class__(
3911 klass=self.__class__,
3912 decode_path=decode_path,
3916 raise NotEnoughData(
3917 "encoded length is longer than data",
3918 klass=self.__class__,
3919 decode_path=decode_path,
3922 v, tail = v[:l], v[l:]
3923 sub_offset = offset + tlen + llen
3925 for name, spec in self.specs.items():
3926 if len(v) == 0 and spec.optional:
3928 sub_decode_path = decode_path + (name,)
3930 value, v_tail = spec.decode(
3934 decode_path=sub_decode_path,
3942 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
3943 if defined is not None:
3944 defined_by, defined_spec = defined
3945 if issubclass(value.__class__, SequenceOf):
3946 for i, _value in enumerate(value):
3947 sub_sub_decode_path = sub_decode_path + (
3949 DecodePathDefBy(defined_by),
3951 defined_value, defined_tail = defined_spec.decode(
3952 memoryview(bytes(_value)),
3954 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
3955 if value.expled else (value.tlen + value.llen)
3958 decode_path=sub_sub_decode_path,
3961 if len(defined_tail) > 0:
3964 klass=self.__class__,
3965 decode_path=sub_sub_decode_path,
3968 _value.defined = (defined_by, defined_value)
3970 defined_value, defined_tail = defined_spec.decode(
3971 memoryview(bytes(value)),
3973 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
3974 if value.expled else (value.tlen + value.llen)
3977 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
3980 if len(defined_tail) > 0:
3983 klass=self.__class__,
3984 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
3987 value.defined = (defined_by, defined_value)
3989 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3991 if spec.default is not None and value == spec.default:
3992 if ctx.get("strict_default_existence", False):
3994 "DEFAULT value met",
3995 klass=self.__class__,
3996 decode_path=sub_decode_path,
4001 values[name] = value
4003 spec_defines = getattr(spec, "defines", ())
4004 if len(spec_defines) == 0:
4005 defines_by_path = ctx.get("defines_by_path", ())
4006 if len(defines_by_path) > 0:
4007 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4008 if spec_defines is not None and len(spec_defines) > 0:
4009 for rel_path, schema in spec_defines:
4010 defined = schema.get(value, None)
4011 if defined is not None:
4012 ctx.setdefault("defines", []).append((
4013 abs_decode_path(sub_decode_path[:-1], rel_path),
4019 klass=self.__class__,
4020 decode_path=decode_path,
4023 obj = self.__class__(
4027 default=self.default,
4028 optional=self.optional,
4029 _decoded=(offset, llen, l),
4035 value = pp_console_row(next(self.pps()))
4037 for name in self.specs:
4038 _value = self._value.get(name)
4041 cols.append(repr(_value))
4042 return "%s[%s]" % (value, ", ".join(cols))
4044 def pps(self, decode_path=()):
4046 asn1_type_name=self.asn1_type_name,
4047 obj_name=self.__class__.__name__,
4048 decode_path=decode_path,
4049 optional=self.optional,
4050 default=self == self.default,
4051 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4052 expl=None if self._expl is None else tag_decode(self._expl),
4057 expl_offset=self.expl_offset if self.expled else None,
4058 expl_tlen=self.expl_tlen if self.expled else None,
4059 expl_llen=self.expl_llen if self.expled else None,
4060 expl_vlen=self.expl_vlen if self.expled else None,
4062 for name in self.specs:
4063 value = self._value.get(name)
4066 yield value.pps(decode_path=decode_path + (name,))
4069 class Set(Sequence):
4070 """``SET`` structure type
4072 Its usage is identical to :py:class:`pyderasn.Sequence`.
4075 tag_default = tag_encode(form=TagFormConstructed, num=17)
4076 asn1_type_name = "SET"
4079 raws = self._encoded_values()
4082 return b"".join((self.tag, len_encode(len(v)), v))
4084 def _decode(self, tlv, offset, decode_path, ctx):
4086 t, tlen, lv = tag_strip(tlv)
4087 except DecodeError as err:
4088 raise err.__class__(
4090 klass=self.__class__,
4091 decode_path=decode_path,
4096 klass=self.__class__,
4097 decode_path=decode_path,
4101 l, llen, v = len_decode(lv)
4102 except DecodeError as err:
4103 raise err.__class__(
4105 klass=self.__class__,
4106 decode_path=decode_path,
4110 raise NotEnoughData(
4111 "encoded length is longer than data",
4112 klass=self.__class__,
4115 v, tail = v[:l], v[l:]
4116 sub_offset = offset + tlen + llen
4118 specs_items = self.specs.items
4120 for name, spec in specs_items():
4122 value, v_tail = spec.decode(
4126 decode_path=decode_path + (name,),
4132 value.expl_tlvlen if value.expled else value.tlvlen
4135 if spec.default is None or value != spec.default: # pragma: no cover
4136 # SeqMixing.test_encoded_default_accepted covers that place
4137 values[name] = value
4141 klass=self.__class__,
4142 decode_path=decode_path,
4145 obj = self.__class__(
4149 default=self.default,
4150 optional=self.optional,
4151 _decoded=(offset, llen, l),
4157 class SequenceOf(Obj):
4158 """``SEQUENCE OF`` sequence type
4160 For that kind of type you must specify the object it will carry on
4161 (bounds are for example here, not required)::
4163 class Ints(SequenceOf):
4168 >>> ints.append(Integer(123))
4169 >>> ints.append(Integer(234))
4171 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4172 >>> [int(i) for i in ints]
4174 >>> ints.append(Integer(345))
4175 Traceback (most recent call last):
4176 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4179 >>> ints[1] = Integer(345)
4181 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4183 Also you can initialize sequence with preinitialized values:
4185 >>> ints = Ints([Integer(123), Integer(234)])
4187 __slots__ = ("spec", "_bound_min", "_bound_max")
4188 tag_default = tag_encode(form=TagFormConstructed, num=16)
4189 asn1_type_name = "SEQUENCE OF"
4202 super(SequenceOf, self).__init__(
4210 schema = getattr(self, "schema", None)
4212 raise ValueError("schema must be specified")
4214 self._bound_min, self._bound_max = getattr(
4218 ) if bounds is None else bounds
4220 if value is not None:
4221 self._value = self._value_sanitize(value)
4222 if default is not None:
4223 default_value = self._value_sanitize(default)
4224 default_obj = self.__class__(
4229 default_obj._value = default_value
4230 self.default = default_obj
4232 self._value = default_obj.copy()._value
4234 def _value_sanitize(self, value):
4235 if issubclass(value.__class__, SequenceOf):
4236 value = value._value
4237 elif hasattr(value, "__iter__"):
4240 raise InvalidValueType((self.__class__, iter))
4241 if not self._bound_min <= len(value) <= self._bound_max:
4242 raise BoundsError(self._bound_min, len(value), self._bound_max)
4244 if not isinstance(v, self.spec.__class__):
4245 raise InvalidValueType((self.spec.__class__,))
4250 return all(v.ready for v in self._value)
4253 obj = self.__class__(schema=self.spec)
4254 obj._bound_min = self._bound_min
4255 obj._bound_max = self._bound_max
4257 obj._expl = self._expl
4258 obj.default = self.default
4259 obj.optional = self.optional
4260 obj.offset = self.offset
4261 obj.llen = self.llen
4262 obj.vlen = self.vlen
4263 obj._value = [v.copy() for v in self._value]
4266 def __eq__(self, their):
4267 if isinstance(their, self.__class__):
4269 self.spec == their.spec and
4270 self.tag == their.tag and
4271 self._expl == their._expl and
4272 self._value == their._value
4274 if hasattr(their, "__iter__"):
4275 return self._value == list(their)
4287 return self.__class__(
4291 (self._bound_min, self._bound_max)
4292 if bounds is None else bounds
4294 impl=self.tag if impl is None else impl,
4295 expl=self._expl if expl is None else expl,
4296 default=self.default if default is None else default,
4297 optional=self.optional if optional is None else optional,
4300 def __contains__(self, key):
4301 return key in self._value
4303 def append(self, value):
4304 if not isinstance(value, self.spec.__class__):
4305 raise InvalidValueType((self.spec.__class__,))
4306 if len(self._value) + 1 > self._bound_max:
4309 len(self._value) + 1,
4312 self._value.append(value)
4315 self._assert_ready()
4316 return iter(self._value)
4319 self._assert_ready()
4320 return len(self._value)
4322 def __setitem__(self, key, value):
4323 if not isinstance(value, self.spec.__class__):
4324 raise InvalidValueType((self.spec.__class__,))
4325 self._value[key] = self.spec(value=value)
4327 def __getitem__(self, key):
4328 return self._value[key]
4330 def _encoded_values(self):
4331 return [v.encode() for v in self._value]
4334 v = b"".join(self._encoded_values())
4335 return b"".join((self.tag, len_encode(len(v)), v))
4337 def _decode(self, tlv, offset, decode_path, ctx):
4339 t, tlen, lv = tag_strip(tlv)
4340 except DecodeError as err:
4341 raise err.__class__(
4343 klass=self.__class__,
4344 decode_path=decode_path,
4349 klass=self.__class__,
4350 decode_path=decode_path,
4354 l, llen, v = len_decode(lv)
4355 except DecodeError as err:
4356 raise err.__class__(
4358 klass=self.__class__,
4359 decode_path=decode_path,
4363 raise NotEnoughData(
4364 "encoded length is longer than data",
4365 klass=self.__class__,
4366 decode_path=decode_path,
4369 v, tail = v[:l], v[l:]
4370 sub_offset = offset + tlen + llen
4374 value, v_tail = spec.decode(
4378 decode_path=decode_path + (str(len(_value)),),
4381 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4383 _value.append(value)
4384 obj = self.__class__(
4387 bounds=(self._bound_min, self._bound_max),
4390 default=self.default,
4391 optional=self.optional,
4392 _decoded=(offset, llen, l),
4398 pp_console_row(next(self.pps())),
4399 ", ".join(repr(v) for v in self._value),
4402 def pps(self, decode_path=()):
4404 asn1_type_name=self.asn1_type_name,
4405 obj_name=self.__class__.__name__,
4406 decode_path=decode_path,
4407 optional=self.optional,
4408 default=self == self.default,
4409 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4410 expl=None if self._expl is None else tag_decode(self._expl),
4415 expl_offset=self.expl_offset if self.expled else None,
4416 expl_tlen=self.expl_tlen if self.expled else None,
4417 expl_llen=self.expl_llen if self.expled else None,
4418 expl_vlen=self.expl_vlen if self.expled else None,
4420 for i, value in enumerate(self._value):
4421 yield value.pps(decode_path=decode_path + (str(i),))
4424 class SetOf(SequenceOf):
4425 """``SET OF`` sequence type
4427 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4430 tag_default = tag_encode(form=TagFormConstructed, num=17)
4431 asn1_type_name = "SET OF"
4434 raws = self._encoded_values()
4437 return b"".join((self.tag, len_encode(len(v)), v))
4440 def obj_by_path(pypath): # pragma: no cover
4441 """Import object specified as string Python path
4443 Modules must be separated from classes/functions with ``:``.
4445 >>> obj_by_path("foo.bar:Baz")
4446 <class 'foo.bar.Baz'>
4447 >>> obj_by_path("foo.bar:Baz.boo")
4448 <classmethod 'foo.bar.Baz.boo'>
4450 mod, objs = pypath.rsplit(":", 1)
4451 from importlib import import_module
4452 obj = import_module(mod)
4453 for obj_name in objs.split("."):
4454 obj = getattr(obj, obj_name)
4458 def generic_decoder(): # pragma: no cover
4459 # All of this below is a big hack with self references
4460 choice = PrimitiveTypes()
4461 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4462 choice.specs["SetOf"] = SetOf(schema=choice)
4464 choice.specs["SequenceOf%d" % i] = SequenceOf(
4468 choice.specs["Any"] = Any()
4470 # Class name equals to type name, to omit it from output
4471 class SEQUENCEOF(SequenceOf):
4475 def pprint_any(obj, oids=None, with_colours=False):
4476 def _pprint_pps(pps):
4478 if hasattr(pp, "_fields"):
4479 if pp.asn1_type_name == Choice.asn1_type_name:
4481 pp_kwargs = pp._asdict()
4482 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4483 pp = _pp(**pp_kwargs)
4484 yield pp_console_row(
4489 with_colours=with_colours,
4491 for row in pp_console_blob(pp):
4494 for row in _pprint_pps(pp):
4496 return "\n".join(_pprint_pps(obj.pps()))
4497 return SEQUENCEOF(), pprint_any
4500 def main(): # pragma: no cover
4502 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4503 parser.add_argument(
4507 help="Skip that number of bytes from the beginning",
4509 parser.add_argument(
4511 help="Python path to dictionary with OIDs",
4513 parser.add_argument(
4515 help="Python path to schema definition to use",
4517 parser.add_argument(
4518 "--defines-by-path",
4519 help="Python path to decoder's defines_by_path",
4521 parser.add_argument(
4523 type=argparse.FileType("rb"),
4524 help="Path to DER file you want to decode",
4526 args = parser.parse_args()
4527 args.DERFile.seek(args.skip)
4528 der = memoryview(args.DERFile.read())
4529 args.DERFile.close()
4530 oids = obj_by_path(args.oids) if args.oids else {}
4532 schema = obj_by_path(args.schema)
4533 from functools import partial
4534 pprinter = partial(pprint, big_blobs=True)
4536 schema, pprinter = generic_decoder()
4537 obj, tail = schema().decode(
4540 None if args.defines_by_path is None else
4541 {"defines_by_path": obj_by_path(args.defines_by_path)}
4547 with_colours=True if environ.get("NO_COLOR") is None else False,
4550 print("\nTrailing data: %s" % hexenc(tail))
4553 if __name__ == "__main__":