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``/``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
476 from six import add_metaclass
477 from six import binary_type
478 from six import byte2int
479 from six import indexbytes
480 from six import int2byte
481 from six import integer_types
482 from six import iterbytes
484 from six import string_types
485 from six import text_type
486 from six.moves import xrange as six_xrange
490 from termcolor import colored
492 def colored(what, *args):
535 "TagClassApplication",
539 "TagFormConstructed",
550 TagClassUniversal = 0
551 TagClassApplication = 1 << 6
552 TagClassContext = 1 << 7
553 TagClassPrivate = 1 << 6 | 1 << 7
555 TagFormConstructed = 1 << 5
558 TagClassApplication: "APPLICATION ",
559 TagClassPrivate: "PRIVATE ",
560 TagClassUniversal: "UNIV ",
564 ########################################################################
566 ########################################################################
568 class DecodeError(Exception):
569 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
571 :param str msg: reason of decode failing
572 :param klass: optional exact DecodeError inherited class (like
573 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
574 :py:exc:`InvalidLength`)
575 :param decode_path: tuple of strings. It contains human
576 readable names of the fields through which
577 decoding process has passed
578 :param int offset: binary offset where failure happened
580 super(DecodeError, self).__init__()
583 self.decode_path = decode_path
589 "" if self.klass is None else self.klass.__name__,
591 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
592 if len(self.decode_path) > 0 else ""
594 ("(at %d)" % self.offset) if self.offset > 0 else "",
600 return "%s(%s)" % (self.__class__.__name__, self)
603 class NotEnoughData(DecodeError):
607 class TagMismatch(DecodeError):
611 class InvalidLength(DecodeError):
615 class InvalidOID(DecodeError):
619 class ObjUnknown(ValueError):
620 def __init__(self, name):
621 super(ObjUnknown, self).__init__()
625 return "object is unknown: %s" % self.name
628 return "%s(%s)" % (self.__class__.__name__, self)
631 class ObjNotReady(ValueError):
632 def __init__(self, name):
633 super(ObjNotReady, self).__init__()
637 return "object is not ready: %s" % self.name
640 return "%s(%s)" % (self.__class__.__name__, self)
643 class InvalidValueType(ValueError):
644 def __init__(self, expected_types):
645 super(InvalidValueType, self).__init__()
646 self.expected_types = expected_types
649 return "invalid value type, expected: %s" % ", ".join(
650 [repr(t) for t in self.expected_types]
654 return "%s(%s)" % (self.__class__.__name__, self)
657 class BoundsError(ValueError):
658 def __init__(self, bound_min, value, bound_max):
659 super(BoundsError, self).__init__()
660 self.bound_min = bound_min
662 self.bound_max = bound_max
665 return "unsatisfied bounds: %s <= %s <= %s" % (
672 return "%s(%s)" % (self.__class__.__name__, self)
675 ########################################################################
677 ########################################################################
679 _hexdecoder = getdecoder("hex")
680 _hexencoder = getencoder("hex")
684 """Binary data to hexadecimal string convert
686 return _hexdecoder(data)[0]
690 """Hexadecimal string to binary data convert
692 return _hexencoder(data)[0].decode("ascii")
695 def int_bytes_len(num, byte_len=8):
698 return int(ceil(float(num.bit_length()) / byte_len))
701 def zero_ended_encode(num):
702 octets = bytearray(int_bytes_len(num, 7))
704 octets[i] = num & 0x7F
708 octets[i] = 0x80 | (num & 0x7F)
714 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
715 """Encode tag to binary form
717 :param int num: tag's number
718 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
719 :py:data:`pyderasn.TagClassContext`,
720 :py:data:`pyderasn.TagClassApplication`,
721 :py:data:`pyderasn.TagClassPrivate`)
722 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
723 :py:data:`pyderasn.TagFormConstructed`)
727 return int2byte(klass | form | num)
728 # [XX|X|11111][1.......][1.......] ... [0.......]
729 return int2byte(klass | form | 31) + zero_ended_encode(num)
733 """Decode tag from binary form
737 No validation is performed, assuming that it has already passed.
739 It returns tuple with three integers, as
740 :py:func:`pyderasn.tag_encode` accepts.
742 first_octet = byte2int(tag)
743 klass = first_octet & 0xC0
744 form = first_octet & 0x20
745 if first_octet & 0x1F < 0x1F:
746 return (klass, form, first_octet & 0x1F)
748 for octet in iterbytes(tag[1:]):
751 return (klass, form, num)
755 """Create CONTEXT PRIMITIVE tag
757 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
761 """Create CONTEXT CONSTRUCTED tag
763 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
767 """Take off tag from the data
769 :returns: (encoded tag, tag length, remaining data)
772 raise NotEnoughData("no data at all")
773 if byte2int(data) & 0x1F < 31:
774 return data[:1], 1, data[1:]
779 raise DecodeError("unfinished tag")
780 if indexbytes(data, i) & 0x80 == 0:
783 return data[:i], i, data[i:]
789 octets = bytearray(int_bytes_len(l) + 1)
790 octets[0] = 0x80 | (len(octets) - 1)
791 for i in six_xrange(len(octets) - 1, 0, -1):
797 def len_decode(data):
799 raise NotEnoughData("no data at all")
800 first_octet = byte2int(data)
801 if first_octet & 0x80 == 0:
802 return first_octet, 1, data[1:]
803 octets_num = first_octet & 0x7F
804 if octets_num + 1 > len(data):
805 raise NotEnoughData("encoded length is longer than data")
807 raise DecodeError("long form instead of short one")
808 if byte2int(data[1:]) == 0:
809 raise DecodeError("leading zeros")
811 for v in iterbytes(data[1:1 + octets_num]):
814 raise DecodeError("long form instead of short one")
815 return l, 1 + octets_num, data[1 + octets_num:]
818 ########################################################################
820 ########################################################################
822 class AutoAddSlots(type):
823 def __new__(mcs, name, bases, _dict):
824 _dict["__slots__"] = _dict.get("__slots__", ())
825 return type.__new__(mcs, name, bases, _dict)
828 @add_metaclass(AutoAddSlots)
830 """Common ASN.1 object class
832 All ASN.1 types are inherited from it. It has metaclass that
833 automatically adds ``__slots__`` to all inherited classes.
854 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
855 self._expl = getattr(self, "expl", None) if expl is None else expl
856 if self.tag != self.tag_default and self._expl is not None:
858 "implicit and explicit tags can not be set simultaneously"
860 if default is not None:
862 self.optional = optional
863 self.offset, self.llen, self.vlen = _decoded
867 def ready(self): # pragma: no cover
868 """Is object ready to be encoded?
870 raise NotImplementedError()
872 def _assert_ready(self):
874 raise ObjNotReady(self.__class__.__name__)
878 """Is object decoded?
880 return (self.llen + self.vlen) > 0
882 def copy(self): # pragma: no cover
883 """Make a copy of object, safe to be mutated
885 raise NotImplementedError()
893 return self.tlen + self.llen + self.vlen
895 def __str__(self): # pragma: no cover
896 return self.__bytes__() if PY2 else self.__unicode__()
898 def __ne__(self, their):
899 return not(self == their)
901 def __gt__(self, their): # pragma: no cover
902 return not(self < their)
904 def __le__(self, their): # pragma: no cover
905 return (self == their) or (self < their)
907 def __ge__(self, their): # pragma: no cover
908 return (self == their) or (self > their)
910 def _encode(self): # pragma: no cover
911 raise NotImplementedError()
913 def _decode(self, tlv, offset, decode_path, ctx): # pragma: no cover
914 raise NotImplementedError()
918 if self._expl is None:
920 return b"".join((self._expl, len_encode(len(raw)), raw))
922 def decode(self, data, offset=0, leavemm=False, decode_path=(), ctx=None):
925 :param data: either binary or memoryview
926 :param int offset: initial data's offset
927 :param bool leavemm: do we need to leave memoryview of remaining
928 data as is, or convert it to bytes otherwise
929 :param ctx: optional :ref:`context <ctx>` governing decoding process.
930 :returns: (Obj, remaining data)
934 tlv = memoryview(data)
935 if self._expl is None:
936 obj, tail = self._decode(
939 decode_path=decode_path,
944 t, tlen, lv = tag_strip(tlv)
945 except DecodeError as err:
948 klass=self.__class__,
949 decode_path=decode_path,
954 klass=self.__class__,
955 decode_path=decode_path,
959 l, llen, v = len_decode(lv)
960 except DecodeError as err:
963 klass=self.__class__,
964 decode_path=decode_path,
969 "encoded length is longer than data",
970 klass=self.__class__,
971 decode_path=decode_path,
974 obj, tail = self._decode(
976 offset=offset + tlen + llen,
977 decode_path=decode_path,
980 return obj, (tail if leavemm else tail.tobytes())
984 return self._expl is not None
992 return len(self._expl)
996 return len(len_encode(self.tlvlen))
999 def expl_offset(self):
1000 return self.offset - self.expl_tlen - self.expl_llen
1003 def expl_vlen(self):
1007 def expl_tlvlen(self):
1008 return self.expl_tlen + self.expl_llen + self.expl_vlen
1011 class DecodePathDefBy(object):
1012 """DEFINED BY representation inside decode path
1014 __slots__ = ('defined_by',)
1016 def __init__(self, defined_by):
1017 self.defined_by = defined_by
1019 def __eq__(self, their):
1020 if not isinstance(their, self.__class__):
1022 return self.defined_by == their.defined_by
1025 return "DEFINED BY " + str(self.defined_by)
1028 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1031 ########################################################################
1033 ########################################################################
1035 PP = namedtuple("PP", (
1057 asn1_type_name="unknown",
1096 def _colorize(what, colour, with_colours, attrs=("bold",)):
1097 return colored(what, colour, attrs=attrs) if with_colours else what
1112 " " if pp.expl_offset is None else
1113 ("-%d" % (pp.offset - pp.expl_offset))
1116 cols.append(_colorize(col, "red", with_colours, ()))
1117 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1118 cols.append(_colorize(col, "green", with_colours, ()))
1119 if len(pp.decode_path) > 0:
1120 cols.append(" ." * (len(pp.decode_path)))
1121 ent = pp.decode_path[-1]
1122 if isinstance(ent, DecodePathDefBy):
1123 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1124 value = str(ent.defined_by)
1126 oids is not None and
1127 ent.defined_by.asn1_type_name ==
1128 ObjectIdentifier.asn1_type_name and
1131 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1133 cols.append(_colorize("%s:" % value, "white", with_colours))
1135 cols.append(_colorize("%s:" % ent, "yellow", with_colours))
1136 if pp.expl is not None:
1137 klass, _, num = pp.expl
1138 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1139 cols.append(_colorize(col, "blue", with_colours))
1140 if pp.impl is not None:
1141 klass, _, num = pp.impl
1142 col = "[%s%d]" % (TagClassReprs[klass], num)
1143 cols.append(_colorize(col, "blue", with_colours))
1144 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1145 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1146 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1147 if pp.value is not None:
1149 cols.append(_colorize(value, "white", with_colours))
1151 oids is not None and
1152 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1155 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1157 if isinstance(pp.blob, binary_type):
1158 cols.append(hexenc(pp.blob))
1159 elif isinstance(pp.blob, tuple):
1160 cols.append(", ".join(pp.blob))
1162 cols.append(_colorize("OPTIONAL", "red", with_colours))
1164 cols.append(_colorize("DEFAULT", "red", with_colours))
1165 return " ".join(cols)
1168 def pp_console_blob(pp):
1169 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
1170 if len(pp.decode_path) > 0:
1171 cols.append(" ." * (len(pp.decode_path) + 1))
1172 if isinstance(pp.blob, binary_type):
1173 blob = hexenc(pp.blob).upper()
1174 for i in range(0, len(blob), 32):
1175 chunk = blob[i:i + 32]
1176 yield " ".join(cols + [":".join(
1177 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1179 elif isinstance(pp.blob, tuple):
1180 yield " ".join(cols + [", ".join(pp.blob)])
1183 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1184 """Pretty print object
1186 :param Obj obj: object you want to pretty print
1187 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1188 from it is met, then its humand readable form is printed
1189 :param big_blobs: if large binary objects are met (like OctetString
1190 values), do we need to print them too, on separate
1192 :param with_colours: colourize output, if ``termcolor`` library
1195 def _pprint_pps(pps):
1197 if hasattr(pp, "_fields"):
1199 yield pp_console_row(
1204 with_colours=with_colours,
1206 for row in pp_console_blob(pp):
1209 yield pp_console_row(
1214 with_colours=with_colours,
1217 for row in _pprint_pps(pp):
1219 return "\n".join(_pprint_pps(obj.pps()))
1222 ########################################################################
1223 # ASN.1 primitive types
1224 ########################################################################
1227 """``BOOLEAN`` boolean type
1229 >>> b = Boolean(True)
1231 >>> b == Boolean(True)
1237 tag_default = tag_encode(1)
1238 asn1_type_name = "BOOLEAN"
1250 :param value: set the value. Either boolean type, or
1251 :py:class:`pyderasn.Boolean` object
1252 :param bytes impl: override default tag with ``IMPLICIT`` one
1253 :param bytes expl: override default tag with ``EXPLICIT`` one
1254 :param default: set default value. Type same as in ``value``
1255 :param bool optional: is object ``OPTIONAL`` in sequence
1257 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1258 self._value = None if value is None else self._value_sanitize(value)
1259 if default is not None:
1260 default = self._value_sanitize(default)
1261 self.default = self.__class__(
1267 self._value = default
1269 def _value_sanitize(self, value):
1270 if issubclass(value.__class__, Boolean):
1272 if isinstance(value, bool):
1274 raise InvalidValueType((self.__class__, bool))
1278 return self._value is not None
1281 obj = self.__class__()
1282 obj._value = self._value
1284 obj._expl = self._expl
1285 obj.default = self.default
1286 obj.optional = self.optional
1287 obj.offset = self.offset
1288 obj.llen = self.llen
1289 obj.vlen = self.vlen
1292 def __nonzero__(self):
1293 self._assert_ready()
1297 self._assert_ready()
1300 def __eq__(self, their):
1301 if isinstance(their, bool):
1302 return self._value == their
1303 if not issubclass(their.__class__, Boolean):
1306 self._value == their._value and
1307 self.tag == their.tag and
1308 self._expl == their._expl
1319 return self.__class__(
1321 impl=self.tag if impl is None else impl,
1322 expl=self._expl if expl is None else expl,
1323 default=self.default if default is None else default,
1324 optional=self.optional if optional is None else optional,
1328 self._assert_ready()
1332 (b"\xFF" if self._value else b"\x00"),
1335 def _decode(self, tlv, offset, decode_path, ctx):
1337 t, _, lv = tag_strip(tlv)
1338 except DecodeError as err:
1339 raise err.__class__(
1341 klass=self.__class__,
1342 decode_path=decode_path,
1347 klass=self.__class__,
1348 decode_path=decode_path,
1352 l, _, v = len_decode(lv)
1353 except DecodeError as err:
1354 raise err.__class__(
1356 klass=self.__class__,
1357 decode_path=decode_path,
1361 raise InvalidLength(
1362 "Boolean's length must be equal to 1",
1363 klass=self.__class__,
1364 decode_path=decode_path,
1368 raise NotEnoughData(
1369 "encoded length is longer than data",
1370 klass=self.__class__,
1371 decode_path=decode_path,
1374 first_octet = byte2int(v)
1375 if first_octet == 0:
1377 elif first_octet == 0xFF:
1381 "unacceptable Boolean value",
1382 klass=self.__class__,
1383 decode_path=decode_path,
1386 obj = self.__class__(
1390 default=self.default,
1391 optional=self.optional,
1392 _decoded=(offset, 1, 1),
1397 return pp_console_row(next(self.pps()))
1399 def pps(self, decode_path=()):
1401 asn1_type_name=self.asn1_type_name,
1402 obj_name=self.__class__.__name__,
1403 decode_path=decode_path,
1404 value=str(self._value) if self.ready else None,
1405 optional=self.optional,
1406 default=self == self.default,
1407 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1408 expl=None if self._expl is None else tag_decode(self._expl),
1413 expl_offset=self.expl_offset if self.expled else None,
1414 expl_tlen=self.expl_tlen if self.expled else None,
1415 expl_llen=self.expl_llen if self.expled else None,
1416 expl_vlen=self.expl_vlen if self.expled else None,
1421 """``INTEGER`` integer type
1423 >>> b = Integer(-123)
1425 >>> b == Integer(-123)
1430 >>> Integer(2, bounds=(1, 3))
1432 >>> Integer(5, bounds=(1, 3))
1433 Traceback (most recent call last):
1434 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1438 class Version(Integer):
1445 >>> v = Version("v1")
1452 {'v3': 2, 'v1': 0, 'v2': 1}
1454 __slots__ = ("specs", "_bound_min", "_bound_max")
1455 tag_default = tag_encode(2)
1456 asn1_type_name = "INTEGER"
1470 :param value: set the value. Either integer type, named value
1471 (if ``schema`` is specified in the class), or
1472 :py:class:`pyderasn.Integer` object
1473 :param bounds: set ``(MIN, MAX)`` value constraint.
1474 (-inf, +inf) by default
1475 :param bytes impl: override default tag with ``IMPLICIT`` one
1476 :param bytes expl: override default tag with ``EXPLICIT`` one
1477 :param default: set default value. Type same as in ``value``
1478 :param bool optional: is object ``OPTIONAL`` in sequence
1480 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1482 specs = getattr(self, "schema", {}) if _specs is None else _specs
1483 self.specs = specs if isinstance(specs, dict) else dict(specs)
1484 self._bound_min, self._bound_max = getattr(
1487 (float("-inf"), float("+inf")),
1488 ) if bounds is None else bounds
1489 if value is not None:
1490 self._value = self._value_sanitize(value)
1491 if default is not None:
1492 default = self._value_sanitize(default)
1493 self.default = self.__class__(
1499 if self._value is None:
1500 self._value = default
1502 def _value_sanitize(self, value):
1503 if issubclass(value.__class__, Integer):
1504 value = value._value
1505 elif isinstance(value, integer_types):
1507 elif isinstance(value, str):
1508 value = self.specs.get(value)
1510 raise ObjUnknown("integer value: %s" % value)
1512 raise InvalidValueType((self.__class__, int, str))
1513 if not self._bound_min <= value <= self._bound_max:
1514 raise BoundsError(self._bound_min, value, self._bound_max)
1519 return self._value is not None
1522 obj = self.__class__(_specs=self.specs)
1523 obj._value = self._value
1524 obj._bound_min = self._bound_min
1525 obj._bound_max = self._bound_max
1527 obj._expl = self._expl
1528 obj.default = self.default
1529 obj.optional = self.optional
1530 obj.offset = self.offset
1531 obj.llen = self.llen
1532 obj.vlen = self.vlen
1536 self._assert_ready()
1537 return int(self._value)
1540 self._assert_ready()
1543 bytes(self._expl or b"") +
1544 str(self._value).encode("ascii"),
1547 def __eq__(self, their):
1548 if isinstance(their, integer_types):
1549 return self._value == their
1550 if not issubclass(their.__class__, Integer):
1553 self._value == their._value and
1554 self.tag == their.tag and
1555 self._expl == their._expl
1558 def __lt__(self, their):
1559 return self._value < their._value
1563 for name, value in self.specs.items():
1564 if value == self._value:
1576 return self.__class__(
1579 (self._bound_min, self._bound_max)
1580 if bounds is None else bounds
1582 impl=self.tag if impl is None else impl,
1583 expl=self._expl if expl is None else expl,
1584 default=self.default if default is None else default,
1585 optional=self.optional if optional is None else optional,
1590 self._assert_ready()
1594 octets = bytearray([0])
1598 octets = bytearray()
1600 octets.append((value & 0xFF) ^ 0xFF)
1602 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1605 octets = bytearray()
1607 octets.append(value & 0xFF)
1609 if octets[-1] & 0x80 > 0:
1612 octets = bytes(octets)
1614 bytes_len = ceil(value.bit_length() / 8) or 1
1617 octets = value.to_bytes(
1622 except OverflowError:
1626 return b"".join((self.tag, len_encode(len(octets)), octets))
1628 def _decode(self, tlv, offset, decode_path, ctx):
1630 t, _, lv = tag_strip(tlv)
1631 except DecodeError as err:
1632 raise err.__class__(
1634 klass=self.__class__,
1635 decode_path=decode_path,
1640 klass=self.__class__,
1641 decode_path=decode_path,
1645 l, llen, v = len_decode(lv)
1646 except DecodeError as err:
1647 raise err.__class__(
1649 klass=self.__class__,
1650 decode_path=decode_path,
1654 raise NotEnoughData(
1655 "encoded length is longer than data",
1656 klass=self.__class__,
1657 decode_path=decode_path,
1661 raise NotEnoughData(
1663 klass=self.__class__,
1664 decode_path=decode_path,
1667 v, tail = v[:l], v[l:]
1668 first_octet = byte2int(v)
1670 second_octet = byte2int(v[1:])
1672 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1673 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1676 "non normalized integer",
1677 klass=self.__class__,
1678 decode_path=decode_path,
1683 if first_octet & 0x80 > 0:
1684 octets = bytearray()
1685 for octet in bytearray(v):
1686 octets.append(octet ^ 0xFF)
1687 for octet in octets:
1688 value = (value << 8) | octet
1692 for octet in bytearray(v):
1693 value = (value << 8) | octet
1695 value = int.from_bytes(v, byteorder="big", signed=True)
1697 obj = self.__class__(
1699 bounds=(self._bound_min, self._bound_max),
1702 default=self.default,
1703 optional=self.optional,
1705 _decoded=(offset, llen, l),
1707 except BoundsError as err:
1710 klass=self.__class__,
1711 decode_path=decode_path,
1717 return pp_console_row(next(self.pps()))
1719 def pps(self, decode_path=()):
1721 asn1_type_name=self.asn1_type_name,
1722 obj_name=self.__class__.__name__,
1723 decode_path=decode_path,
1724 value=(self.named or str(self._value)) if self.ready else None,
1725 optional=self.optional,
1726 default=self == self.default,
1727 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1728 expl=None if self._expl is None else tag_decode(self._expl),
1733 expl_offset=self.expl_offset if self.expled else None,
1734 expl_tlen=self.expl_tlen if self.expled else None,
1735 expl_llen=self.expl_llen if self.expled else None,
1736 expl_vlen=self.expl_vlen if self.expled else None,
1740 class BitString(Obj):
1741 """``BIT STRING`` bit string type
1743 >>> BitString(b"hello world")
1744 BIT STRING 88 bits 68656c6c6f20776f726c64
1747 >>> b == b"hello world"
1752 >>> b = BitString("'010110000000'B")
1753 BIT STRING 12 bits 5800
1756 >>> b[0], b[1], b[2], b[3]
1757 (False, True, False, True)
1761 [False, True, False, True, True, False, False, False, False, False, False, False]
1765 class KeyUsage(BitString):
1767 ('digitalSignature', 0),
1768 ('nonRepudiation', 1),
1769 ('keyEncipherment', 2),
1772 >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1773 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1775 ['nonRepudiation', 'keyEncipherment']
1777 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1779 __slots__ = ("specs", "defined")
1780 tag_default = tag_encode(3)
1781 asn1_type_name = "BIT STRING"
1794 :param value: set the value. Either binary type, tuple of named
1795 values (if ``schema`` is specified in the class),
1796 string in ``'XXX...'B`` form, or
1797 :py:class:`pyderasn.BitString` object
1798 :param bytes impl: override default tag with ``IMPLICIT`` one
1799 :param bytes expl: override default tag with ``EXPLICIT`` one
1800 :param default: set default value. Type same as in ``value``
1801 :param bool optional: is object ``OPTIONAL`` in sequence
1803 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1804 specs = getattr(self, "schema", {}) if _specs is None else _specs
1805 self.specs = specs if isinstance(specs, dict) else dict(specs)
1806 self._value = None if value is None else self._value_sanitize(value)
1807 if default is not None:
1808 default = self._value_sanitize(default)
1809 self.default = self.__class__(
1815 self._value = default
1818 def _bits2octets(self, bits):
1819 if len(self.specs) > 0:
1820 bits = bits.rstrip("0")
1822 bits += "0" * ((8 - (bit_len % 8)) % 8)
1823 octets = bytearray(len(bits) // 8)
1824 for i in six_xrange(len(octets)):
1825 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1826 return bit_len, bytes(octets)
1828 def _value_sanitize(self, value):
1829 if issubclass(value.__class__, BitString):
1831 if isinstance(value, (string_types, binary_type)):
1833 isinstance(value, string_types) and
1834 value.startswith("'") and
1835 value.endswith("'B")
1838 if not set(value) <= set(("0", "1")):
1839 raise ValueError("B's coding contains unacceptable chars")
1840 return self._bits2octets(value)
1841 elif isinstance(value, binary_type):
1842 return (len(value) * 8, value)
1844 raise InvalidValueType((
1849 if isinstance(value, tuple):
1852 isinstance(value[0], integer_types) and
1853 isinstance(value[1], binary_type)
1858 bit = self.specs.get(name)
1860 raise ObjUnknown("BitString value: %s" % name)
1863 return self._bits2octets("")
1865 return self._bits2octets("".join(
1866 ("1" if bit in bits else "0")
1867 for bit in six_xrange(max(bits) + 1)
1869 raise InvalidValueType((self.__class__, binary_type, string_types))
1873 return self._value is not None
1876 obj = self.__class__(_specs=self.specs)
1878 if value is not None:
1879 value = (value[0], value[1])
1882 obj._expl = self._expl
1883 obj.default = self.default
1884 obj.optional = self.optional
1885 obj.offset = self.offset
1886 obj.llen = self.llen
1887 obj.vlen = self.vlen
1891 self._assert_ready()
1892 for i in six_xrange(self._value[0]):
1897 self._assert_ready()
1898 return self._value[0]
1900 def __bytes__(self):
1901 self._assert_ready()
1902 return self._value[1]
1904 def __eq__(self, their):
1905 if isinstance(their, bytes):
1906 return self._value[1] == their
1907 if not issubclass(their.__class__, BitString):
1910 self._value == their._value and
1911 self.tag == their.tag and
1912 self._expl == their._expl
1917 return [name for name, bit in self.specs.items() if self[bit]]
1927 return self.__class__(
1929 impl=self.tag if impl is None else impl,
1930 expl=self._expl if expl is None else expl,
1931 default=self.default if default is None else default,
1932 optional=self.optional if optional is None else optional,
1936 def __getitem__(self, key):
1937 if isinstance(key, int):
1938 bit_len, octets = self._value
1942 byte2int(memoryview(octets)[key // 8:]) >>
1945 if isinstance(key, string_types):
1946 value = self.specs.get(key)
1948 raise ObjUnknown("BitString value: %s" % key)
1950 raise InvalidValueType((int, str))
1953 self._assert_ready()
1954 bit_len, octets = self._value
1957 len_encode(len(octets) + 1),
1958 int2byte((8 - bit_len % 8) % 8),
1962 def _decode(self, tlv, offset, decode_path, ctx):
1964 t, _, lv = tag_strip(tlv)
1965 except DecodeError as err:
1966 raise err.__class__(
1968 klass=self.__class__,
1969 decode_path=decode_path,
1974 klass=self.__class__,
1975 decode_path=decode_path,
1979 l, llen, v = len_decode(lv)
1980 except DecodeError as err:
1981 raise err.__class__(
1983 klass=self.__class__,
1984 decode_path=decode_path,
1988 raise NotEnoughData(
1989 "encoded length is longer than data",
1990 klass=self.__class__,
1991 decode_path=decode_path,
1995 raise NotEnoughData(
1997 klass=self.__class__,
1998 decode_path=decode_path,
2001 pad_size = byte2int(v)
2002 if l == 1 and pad_size != 0:
2004 "invalid empty value",
2005 klass=self.__class__,
2006 decode_path=decode_path,
2012 klass=self.__class__,
2013 decode_path=decode_path,
2016 if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
2019 klass=self.__class__,
2020 decode_path=decode_path,
2023 v, tail = v[:l], v[l:]
2024 obj = self.__class__(
2025 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2028 default=self.default,
2029 optional=self.optional,
2031 _decoded=(offset, llen, l),
2036 return pp_console_row(next(self.pps()))
2038 def pps(self, decode_path=()):
2042 bit_len, blob = self._value
2043 value = "%d bits" % bit_len
2044 if len(self.specs) > 0:
2045 blob = tuple(self.named)
2047 asn1_type_name=self.asn1_type_name,
2048 obj_name=self.__class__.__name__,
2049 decode_path=decode_path,
2052 optional=self.optional,
2053 default=self == self.default,
2054 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2055 expl=None if self._expl is None else tag_decode(self._expl),
2060 expl_offset=self.expl_offset if self.expled else None,
2061 expl_tlen=self.expl_tlen if self.expled else None,
2062 expl_llen=self.expl_llen if self.expled else None,
2063 expl_vlen=self.expl_vlen if self.expled else None,
2065 defined_by, defined = self.defined or (None, None)
2066 if defined_by is not None:
2068 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2072 class OctetString(Obj):
2073 """``OCTET STRING`` binary string type
2075 >>> s = OctetString(b"hello world")
2076 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2077 >>> s == OctetString(b"hello world")
2082 >>> OctetString(b"hello", bounds=(4, 4))
2083 Traceback (most recent call last):
2084 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2085 >>> OctetString(b"hell", bounds=(4, 4))
2086 OCTET STRING 4 bytes 68656c6c
2088 __slots__ = ("_bound_min", "_bound_max", "defined")
2089 tag_default = tag_encode(4)
2090 asn1_type_name = "OCTET STRING"
2103 :param value: set the value. Either binary type, or
2104 :py:class:`pyderasn.OctetString` object
2105 :param bounds: set ``(MIN, MAX)`` value size constraint.
2106 (-inf, +inf) by default
2107 :param bytes impl: override default tag with ``IMPLICIT`` one
2108 :param bytes expl: override default tag with ``EXPLICIT`` one
2109 :param default: set default value. Type same as in ``value``
2110 :param bool optional: is object ``OPTIONAL`` in sequence
2112 super(OctetString, self).__init__(
2120 self._bound_min, self._bound_max = getattr(
2124 ) if bounds is None else bounds
2125 if value is not None:
2126 self._value = self._value_sanitize(value)
2127 if default is not None:
2128 default = self._value_sanitize(default)
2129 self.default = self.__class__(
2134 if self._value is None:
2135 self._value = default
2138 def _value_sanitize(self, value):
2139 if issubclass(value.__class__, OctetString):
2140 value = value._value
2141 elif isinstance(value, binary_type):
2144 raise InvalidValueType((self.__class__, bytes))
2145 if not self._bound_min <= len(value) <= self._bound_max:
2146 raise BoundsError(self._bound_min, len(value), self._bound_max)
2151 return self._value is not None
2154 obj = self.__class__()
2155 obj._value = self._value
2156 obj._bound_min = self._bound_min
2157 obj._bound_max = self._bound_max
2159 obj._expl = self._expl
2160 obj.default = self.default
2161 obj.optional = self.optional
2162 obj.offset = self.offset
2163 obj.llen = self.llen
2164 obj.vlen = self.vlen
2167 def __bytes__(self):
2168 self._assert_ready()
2171 def __eq__(self, their):
2172 if isinstance(their, binary_type):
2173 return self._value == their
2174 if not issubclass(their.__class__, OctetString):
2177 self._value == their._value and
2178 self.tag == their.tag and
2179 self._expl == their._expl
2182 def __lt__(self, their):
2183 return self._value < their._value
2194 return self.__class__(
2197 (self._bound_min, self._bound_max)
2198 if bounds is None else bounds
2200 impl=self.tag if impl is None else impl,
2201 expl=self._expl if expl is None else expl,
2202 default=self.default if default is None else default,
2203 optional=self.optional if optional is None else optional,
2207 self._assert_ready()
2210 len_encode(len(self._value)),
2214 def _decode(self, tlv, offset, decode_path, ctx):
2216 t, _, lv = tag_strip(tlv)
2217 except DecodeError as err:
2218 raise err.__class__(
2220 klass=self.__class__,
2221 decode_path=decode_path,
2226 klass=self.__class__,
2227 decode_path=decode_path,
2231 l, llen, v = len_decode(lv)
2232 except DecodeError as err:
2233 raise err.__class__(
2235 klass=self.__class__,
2236 decode_path=decode_path,
2240 raise NotEnoughData(
2241 "encoded length is longer than data",
2242 klass=self.__class__,
2243 decode_path=decode_path,
2246 v, tail = v[:l], v[l:]
2248 obj = self.__class__(
2250 bounds=(self._bound_min, self._bound_max),
2253 default=self.default,
2254 optional=self.optional,
2255 _decoded=(offset, llen, l),
2257 except BoundsError as err:
2260 klass=self.__class__,
2261 decode_path=decode_path,
2267 return pp_console_row(next(self.pps()))
2269 def pps(self, decode_path=()):
2271 asn1_type_name=self.asn1_type_name,
2272 obj_name=self.__class__.__name__,
2273 decode_path=decode_path,
2274 value=("%d bytes" % len(self._value)) if self.ready else None,
2275 blob=self._value if self.ready else None,
2276 optional=self.optional,
2277 default=self == self.default,
2278 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2279 expl=None if self._expl is None else tag_decode(self._expl),
2284 expl_offset=self.expl_offset if self.expled else None,
2285 expl_tlen=self.expl_tlen if self.expled else None,
2286 expl_llen=self.expl_llen if self.expled else None,
2287 expl_vlen=self.expl_vlen if self.expled else None,
2289 defined_by, defined = self.defined or (None, None)
2290 if defined_by is not None:
2292 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2297 """``NULL`` null object
2305 tag_default = tag_encode(5)
2306 asn1_type_name = "NULL"
2310 value=None, # unused, but Sequence passes it
2317 :param bytes impl: override default tag with ``IMPLICIT`` one
2318 :param bytes expl: override default tag with ``EXPLICIT`` one
2319 :param bool optional: is object ``OPTIONAL`` in sequence
2321 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2329 obj = self.__class__()
2331 obj._expl = self._expl
2332 obj.default = self.default
2333 obj.optional = self.optional
2334 obj.offset = self.offset
2335 obj.llen = self.llen
2336 obj.vlen = self.vlen
2339 def __eq__(self, their):
2340 if not issubclass(their.__class__, Null):
2343 self.tag == their.tag and
2344 self._expl == their._expl
2354 return self.__class__(
2355 impl=self.tag if impl is None else impl,
2356 expl=self._expl if expl is None else expl,
2357 optional=self.optional if optional is None else optional,
2361 return self.tag + len_encode(0)
2363 def _decode(self, tlv, offset, decode_path, ctx):
2365 t, _, lv = tag_strip(tlv)
2366 except DecodeError as err:
2367 raise err.__class__(
2369 klass=self.__class__,
2370 decode_path=decode_path,
2375 klass=self.__class__,
2376 decode_path=decode_path,
2380 l, _, v = len_decode(lv)
2381 except DecodeError as err:
2382 raise err.__class__(
2384 klass=self.__class__,
2385 decode_path=decode_path,
2389 raise InvalidLength(
2390 "Null must have zero length",
2391 klass=self.__class__,
2392 decode_path=decode_path,
2395 obj = self.__class__(
2398 optional=self.optional,
2399 _decoded=(offset, 1, 0),
2404 return pp_console_row(next(self.pps()))
2406 def pps(self, decode_path=()):
2408 asn1_type_name=self.asn1_type_name,
2409 obj_name=self.__class__.__name__,
2410 decode_path=decode_path,
2411 optional=self.optional,
2412 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2413 expl=None if self._expl is None else tag_decode(self._expl),
2418 expl_offset=self.expl_offset if self.expled else None,
2419 expl_tlen=self.expl_tlen if self.expled else None,
2420 expl_llen=self.expl_llen if self.expled else None,
2421 expl_vlen=self.expl_vlen if self.expled else None,
2425 class ObjectIdentifier(Obj):
2426 """``OBJECT IDENTIFIER`` OID type
2428 >>> oid = ObjectIdentifier((1, 2, 3))
2429 OBJECT IDENTIFIER 1.2.3
2430 >>> oid == ObjectIdentifier("1.2.3")
2436 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2437 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2439 >>> str(ObjectIdentifier((3, 1)))
2440 Traceback (most recent call last):
2441 pyderasn.InvalidOID: unacceptable first arc value
2443 __slots__ = ("defines",)
2444 tag_default = tag_encode(6)
2445 asn1_type_name = "OBJECT IDENTIFIER"
2458 :param value: set the value. Either tuples of integers,
2459 string of "."-concatenated integers, or
2460 :py:class:`pyderasn.ObjectIdentifier` object
2461 :param defines: sequence of tuples. Each tuple has two elements.
2462 First one is relative to current one decode
2463 path, aiming to the field defined by that OID.
2464 Read about relative path in
2465 :py:func:`pyderasn.abs_decode_path`. Second
2466 tuple element is ``{OID: pyderasn.Obj()}``
2467 dictionary, mapping between current OID value
2468 and structure applied to defined field.
2469 :ref:`Read about DEFINED BY <definedby>`
2470 :param bytes impl: override default tag with ``IMPLICIT`` one
2471 :param bytes expl: override default tag with ``EXPLICIT`` one
2472 :param default: set default value. Type same as in ``value``
2473 :param bool optional: is object ``OPTIONAL`` in sequence
2475 super(ObjectIdentifier, self).__init__(
2483 if value is not None:
2484 self._value = self._value_sanitize(value)
2485 if default is not None:
2486 default = self._value_sanitize(default)
2487 self.default = self.__class__(
2492 if self._value is None:
2493 self._value = default
2494 self.defines = defines
2496 def __add__(self, their):
2497 if isinstance(their, self.__class__):
2498 return self.__class__(self._value + their._value)
2499 if isinstance(their, tuple):
2500 return self.__class__(self._value + their)
2501 raise InvalidValueType((self.__class__, tuple))
2503 def _value_sanitize(self, value):
2504 if issubclass(value.__class__, ObjectIdentifier):
2506 if isinstance(value, string_types):
2508 value = tuple(int(arc) for arc in value.split("."))
2510 raise InvalidOID("unacceptable arcs values")
2511 if isinstance(value, tuple):
2513 raise InvalidOID("less than 2 arcs")
2514 first_arc = value[0]
2515 if first_arc in (0, 1):
2516 if not (0 <= value[1] <= 39):
2517 raise InvalidOID("second arc is too wide")
2518 elif first_arc == 2:
2521 raise InvalidOID("unacceptable first arc value")
2523 raise InvalidValueType((self.__class__, str, tuple))
2527 return self._value is not None
2530 obj = self.__class__()
2531 obj._value = self._value
2532 obj.defines = self.defines
2534 obj._expl = self._expl
2535 obj.default = self.default
2536 obj.optional = self.optional
2537 obj.offset = self.offset
2538 obj.llen = self.llen
2539 obj.vlen = self.vlen
2543 self._assert_ready()
2544 return iter(self._value)
2547 return ".".join(str(arc) for arc in self._value or ())
2550 self._assert_ready()
2553 bytes(self._expl or b"") +
2554 str(self._value).encode("ascii"),
2557 def __eq__(self, their):
2558 if isinstance(their, tuple):
2559 return self._value == their
2560 if not issubclass(their.__class__, ObjectIdentifier):
2563 self.tag == their.tag and
2564 self._expl == their._expl and
2565 self._value == their._value
2568 def __lt__(self, their):
2569 return self._value < their._value
2580 return self.__class__(
2582 defines=self.defines if defines is None else defines,
2583 impl=self.tag if impl is None else impl,
2584 expl=self._expl if expl is None else expl,
2585 default=self.default if default is None else default,
2586 optional=self.optional if optional is None else optional,
2590 self._assert_ready()
2592 first_value = value[1]
2593 first_arc = value[0]
2596 elif first_arc == 1:
2598 elif first_arc == 2:
2600 else: # pragma: no cover
2601 raise RuntimeError("invalid arc is stored")
2602 octets = [zero_ended_encode(first_value)]
2603 for arc in value[2:]:
2604 octets.append(zero_ended_encode(arc))
2605 v = b"".join(octets)
2606 return b"".join((self.tag, len_encode(len(v)), v))
2608 def _decode(self, tlv, offset, decode_path, ctx):
2610 t, _, lv = tag_strip(tlv)
2611 except DecodeError as err:
2612 raise err.__class__(
2614 klass=self.__class__,
2615 decode_path=decode_path,
2620 klass=self.__class__,
2621 decode_path=decode_path,
2625 l, llen, v = len_decode(lv)
2626 except DecodeError as err:
2627 raise err.__class__(
2629 klass=self.__class__,
2630 decode_path=decode_path,
2634 raise NotEnoughData(
2635 "encoded length is longer than data",
2636 klass=self.__class__,
2637 decode_path=decode_path,
2641 raise NotEnoughData(
2643 klass=self.__class__,
2644 decode_path=decode_path,
2647 v, tail = v[:l], v[l:]
2653 octet = indexbytes(v, i)
2654 arc = (arc << 7) | (octet & 0x7F)
2655 if octet & 0x80 == 0:
2663 klass=self.__class__,
2664 decode_path=decode_path,
2668 second_arc = arcs[0]
2669 if 0 <= second_arc <= 39:
2671 elif 40 <= second_arc <= 79:
2677 obj = self.__class__(
2678 value=tuple([first_arc, second_arc] + arcs[1:]),
2681 default=self.default,
2682 optional=self.optional,
2683 _decoded=(offset, llen, l),
2688 return pp_console_row(next(self.pps()))
2690 def pps(self, decode_path=()):
2692 asn1_type_name=self.asn1_type_name,
2693 obj_name=self.__class__.__name__,
2694 decode_path=decode_path,
2695 value=str(self) if self.ready else None,
2696 optional=self.optional,
2697 default=self == self.default,
2698 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2699 expl=None if self._expl is None else tag_decode(self._expl),
2704 expl_offset=self.expl_offset if self.expled else None,
2705 expl_tlen=self.expl_tlen if self.expled else None,
2706 expl_llen=self.expl_llen if self.expled else None,
2707 expl_vlen=self.expl_vlen if self.expled else None,
2711 class Enumerated(Integer):
2712 """``ENUMERATED`` integer type
2714 This type is identical to :py:class:`pyderasn.Integer`, but requires
2715 schema to be specified and does not accept values missing from it.
2718 tag_default = tag_encode(10)
2719 asn1_type_name = "ENUMERATED"
2730 bounds=None, # dummy argument, workability for Integer.decode
2732 super(Enumerated, self).__init__(
2741 if len(self.specs) == 0:
2742 raise ValueError("schema must be specified")
2744 def _value_sanitize(self, value):
2745 if isinstance(value, self.__class__):
2746 value = value._value
2747 elif isinstance(value, integer_types):
2748 if value not in list(self.specs.values()):
2750 "unknown integer value: %s" % value,
2751 klass=self.__class__,
2753 elif isinstance(value, string_types):
2754 value = self.specs.get(value)
2756 raise ObjUnknown("integer value: %s" % value)
2758 raise InvalidValueType((self.__class__, int, str))
2762 obj = self.__class__(_specs=self.specs)
2763 obj._value = self._value
2764 obj._bound_min = self._bound_min
2765 obj._bound_max = self._bound_max
2767 obj._expl = self._expl
2768 obj.default = self.default
2769 obj.optional = self.optional
2770 obj.offset = self.offset
2771 obj.llen = self.llen
2772 obj.vlen = self.vlen
2784 return self.__class__(
2786 impl=self.tag if impl is None else impl,
2787 expl=self._expl if expl is None else expl,
2788 default=self.default if default is None else default,
2789 optional=self.optional if optional is None else optional,
2794 class CommonString(OctetString):
2795 """Common class for all strings
2797 Everything resembles :py:class:`pyderasn.OctetString`, except
2798 ability to deal with unicode text strings.
2800 >>> hexenc("привет мир".encode("utf-8"))
2801 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2802 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2804 >>> s = UTF8String("привет мир")
2805 UTF8String UTF8String привет мир
2807 'привет мир'
2808 >>> hexenc(bytes(s))
2809 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2811 >>> PrintableString("привет мир")
2812 Traceback (most recent call last):
2813 UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2815 >>> BMPString("ада", bounds=(2, 2))
2816 Traceback (most recent call last):
2817 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2818 >>> s = BMPString("ад", bounds=(2, 2))
2821 >>> hexenc(bytes(s))
2829 * - :py:class:`pyderasn.UTF8String`
2831 * - :py:class:`pyderasn.NumericString`
2833 * - :py:class:`pyderasn.PrintableString`
2835 * - :py:class:`pyderasn.TeletexString`
2837 * - :py:class:`pyderasn.T61String`
2839 * - :py:class:`pyderasn.VideotexString`
2841 * - :py:class:`pyderasn.IA5String`
2843 * - :py:class:`pyderasn.GraphicString`
2845 * - :py:class:`pyderasn.VisibleString`
2847 * - :py:class:`pyderasn.ISO646String`
2849 * - :py:class:`pyderasn.GeneralString`
2851 * - :py:class:`pyderasn.UniversalString`
2853 * - :py:class:`pyderasn.BMPString`
2856 __slots__ = ("encoding",)
2858 def _value_sanitize(self, value):
2860 value_decoded = None
2861 if isinstance(value, self.__class__):
2862 value_raw = value._value
2863 elif isinstance(value, text_type):
2864 value_decoded = value
2865 elif isinstance(value, binary_type):
2868 raise InvalidValueType((self.__class__, text_type, binary_type))
2870 value_decoded.encode(self.encoding)
2871 if value_raw is None else value_raw
2874 value_raw.decode(self.encoding)
2875 if value_decoded is None else value_decoded
2877 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2885 def __eq__(self, their):
2886 if isinstance(their, binary_type):
2887 return self._value == their
2888 if isinstance(their, text_type):
2889 return self._value == their.encode(self.encoding)
2890 if not isinstance(their, self.__class__):
2893 self._value == their._value and
2894 self.tag == their.tag and
2895 self._expl == their._expl
2898 def __unicode__(self):
2900 return self._value.decode(self.encoding)
2901 return text_type(self._value)
2904 return pp_console_row(next(self.pps(no_unicode=PY2)))
2906 def pps(self, decode_path=(), no_unicode=False):
2909 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2911 asn1_type_name=self.asn1_type_name,
2912 obj_name=self.__class__.__name__,
2913 decode_path=decode_path,
2915 optional=self.optional,
2916 default=self == self.default,
2917 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2918 expl=None if self._expl is None else tag_decode(self._expl),
2926 class UTF8String(CommonString):
2928 tag_default = tag_encode(12)
2930 asn1_type_name = "UTF8String"
2933 class NumericString(CommonString):
2935 tag_default = tag_encode(18)
2937 asn1_type_name = "NumericString"
2940 class PrintableString(CommonString):
2942 tag_default = tag_encode(19)
2944 asn1_type_name = "PrintableString"
2947 class TeletexString(CommonString):
2949 tag_default = tag_encode(20)
2951 asn1_type_name = "TeletexString"
2954 class T61String(TeletexString):
2956 asn1_type_name = "T61String"
2959 class VideotexString(CommonString):
2961 tag_default = tag_encode(21)
2962 encoding = "iso-8859-1"
2963 asn1_type_name = "VideotexString"
2966 class IA5String(CommonString):
2968 tag_default = tag_encode(22)
2970 asn1_type_name = "IA5"
2973 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
2974 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
2975 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
2978 class UTCTime(CommonString):
2979 """``UTCTime`` datetime type
2981 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2982 UTCTime UTCTime 2017-09-30T22:07:50
2988 datetime.datetime(2017, 9, 30, 22, 7, 50)
2989 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2990 datetime.datetime(1957, 9, 30, 22, 7, 50)
2993 tag_default = tag_encode(23)
2995 asn1_type_name = "UTCTime"
2997 fmt = "%y%m%d%H%M%SZ"
3007 bounds=None, # dummy argument, workability for OctetString.decode
3010 :param value: set the value. Either datetime type, or
3011 :py:class:`pyderasn.UTCTime` object
3012 :param bytes impl: override default tag with ``IMPLICIT`` one
3013 :param bytes expl: override default tag with ``EXPLICIT`` one
3014 :param default: set default value. Type same as in ``value``
3015 :param bool optional: is object ``OPTIONAL`` in sequence
3017 super(UTCTime, self).__init__(
3025 if value is not None:
3026 self._value = self._value_sanitize(value)
3027 if default is not None:
3028 default = self._value_sanitize(default)
3029 self.default = self.__class__(
3034 if self._value is None:
3035 self._value = default
3037 def _value_sanitize(self, value):
3038 if isinstance(value, self.__class__):
3040 if isinstance(value, datetime):
3041 return value.strftime(self.fmt).encode("ascii")
3042 if isinstance(value, binary_type):
3043 value_decoded = value.decode("ascii")
3044 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3046 datetime.strptime(value_decoded, self.fmt)
3048 raise DecodeError("invalid UTCTime format")
3051 raise DecodeError("invalid UTCTime length")
3052 raise InvalidValueType((self.__class__, datetime))
3054 def __eq__(self, their):
3055 if isinstance(their, binary_type):
3056 return self._value == their
3057 if isinstance(their, datetime):
3058 return self.todatetime() == their
3059 if not isinstance(their, self.__class__):
3062 self._value == their._value and
3063 self.tag == their.tag and
3064 self._expl == their._expl
3067 def todatetime(self):
3068 """Convert to datetime
3072 Pay attention that UTCTime can not hold full year, so all years
3073 having < 50 years are treated as 20xx, 19xx otherwise, according
3074 to X.509 recomendation.
3076 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3077 year = value.year % 100
3079 year=(2000 + year) if year < 50 else (1900 + year),
3083 minute=value.minute,
3084 second=value.second,
3088 return pp_console_row(next(self.pps()))
3090 def pps(self, decode_path=()):
3092 asn1_type_name=self.asn1_type_name,
3093 obj_name=self.__class__.__name__,
3094 decode_path=decode_path,
3095 value=self.todatetime().isoformat() if self.ready else None,
3096 optional=self.optional,
3097 default=self == self.default,
3098 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3099 expl=None if self._expl is None else tag_decode(self._expl),
3107 class GeneralizedTime(UTCTime):
3108 """``GeneralizedTime`` datetime type
3110 This type is similar to :py:class:`pyderasn.UTCTime`.
3112 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3113 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3115 '20170930220750.000123Z'
3116 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3117 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3120 tag_default = tag_encode(24)
3121 asn1_type_name = "GeneralizedTime"
3123 fmt = "%Y%m%d%H%M%SZ"
3124 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3126 def _value_sanitize(self, value):
3127 if isinstance(value, self.__class__):
3129 if isinstance(value, datetime):
3130 return value.strftime(
3131 self.fmt_ms if value.microsecond > 0 else self.fmt
3133 if isinstance(value, binary_type):
3134 value_decoded = value.decode("ascii")
3135 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3137 datetime.strptime(value_decoded, self.fmt)
3140 "invalid GeneralizedTime (without ms) format",
3143 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3145 datetime.strptime(value_decoded, self.fmt_ms)
3148 "invalid GeneralizedTime (with ms) format",
3153 "invalid GeneralizedTime length",
3154 klass=self.__class__,
3156 raise InvalidValueType((self.__class__, datetime))
3158 def todatetime(self):
3159 value = self._value.decode("ascii")
3160 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3161 return datetime.strptime(value, self.fmt)
3162 return datetime.strptime(value, self.fmt_ms)
3165 class GraphicString(CommonString):
3167 tag_default = tag_encode(25)
3168 encoding = "iso-8859-1"
3169 asn1_type_name = "GraphicString"
3172 class VisibleString(CommonString):
3174 tag_default = tag_encode(26)
3176 asn1_type_name = "VisibleString"
3179 class ISO646String(VisibleString):
3181 asn1_type_name = "ISO646String"
3184 class GeneralString(CommonString):
3186 tag_default = tag_encode(27)
3187 encoding = "iso-8859-1"
3188 asn1_type_name = "GeneralString"
3191 class UniversalString(CommonString):
3193 tag_default = tag_encode(28)
3194 encoding = "utf-32-be"
3195 asn1_type_name = "UniversalString"
3198 class BMPString(CommonString):
3200 tag_default = tag_encode(30)
3201 encoding = "utf-16-be"
3202 asn1_type_name = "BMPString"
3206 """``CHOICE`` special type
3210 class GeneralName(Choice):
3212 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
3213 ('dNSName', IA5String(impl=tag_ctxp(2))),
3216 >>> gn = GeneralName()
3218 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3219 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3220 >>> gn["dNSName"] = IA5String("bar.baz")
3221 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3222 >>> gn["rfc822Name"]
3225 [2] IA5String IA5 bar.baz
3228 >>> gn.value == gn["dNSName"]
3231 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3233 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3234 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3236 __slots__ = ("specs",)
3238 asn1_type_name = "CHOICE"
3251 :param value: set the value. Either ``(choice, value)`` tuple, or
3252 :py:class:`pyderasn.Choice` object
3253 :param bytes impl: can not be set, do **not** use it
3254 :param bytes expl: override default tag with ``EXPLICIT`` one
3255 :param default: set default value. Type same as in ``value``
3256 :param bool optional: is object ``OPTIONAL`` in sequence
3258 if impl is not None:
3259 raise ValueError("no implicit tag allowed for CHOICE")
3260 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3262 schema = getattr(self, "schema", ())
3263 if len(schema) == 0:
3264 raise ValueError("schema must be specified")
3266 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3269 if value is not None:
3270 self._value = self._value_sanitize(value)
3271 if default is not None:
3272 default_value = self._value_sanitize(default)
3273 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3274 default_obj.specs = self.specs
3275 default_obj._value = default_value
3276 self.default = default_obj
3278 self._value = default_obj.copy()._value
3280 def _value_sanitize(self, value):
3281 if isinstance(value, self.__class__):
3283 if isinstance(value, tuple) and len(value) == 2:
3285 spec = self.specs.get(choice)
3287 raise ObjUnknown(choice)
3288 if not isinstance(obj, spec.__class__):
3289 raise InvalidValueType((spec,))
3290 return (choice, spec(obj))
3291 raise InvalidValueType((self.__class__, tuple))
3295 return self._value is not None and self._value[1].ready
3298 obj = self.__class__(schema=self.specs)
3299 obj._expl = self._expl
3300 obj.default = self.default
3301 obj.optional = self.optional
3302 obj.offset = self.offset
3303 obj.llen = self.llen
3304 obj.vlen = self.vlen
3306 if value is not None:
3307 obj._value = (value[0], value[1].copy())
3310 def __eq__(self, their):
3311 if isinstance(their, tuple) and len(their) == 2:
3312 return self._value == their
3313 if not isinstance(their, self.__class__):
3316 self.specs == their.specs and
3317 self._value == their._value
3327 return self.__class__(
3330 expl=self._expl if expl is None else expl,
3331 default=self.default if default is None else default,
3332 optional=self.optional if optional is None else optional,
3337 self._assert_ready()
3338 return self._value[0]
3342 self._assert_ready()
3343 return self._value[1]
3345 def __getitem__(self, key):
3346 if key not in self.specs:
3347 raise ObjUnknown(key)
3348 if self._value is None:
3350 choice, value = self._value
3355 def __setitem__(self, key, value):
3356 spec = self.specs.get(key)
3358 raise ObjUnknown(key)
3359 if not isinstance(value, spec.__class__):
3360 raise InvalidValueType((spec.__class__,))
3361 self._value = (key, spec(value))
3369 return self._value[1].decoded if self.ready else False
3372 self._assert_ready()
3373 return self._value[1].encode()
3375 def _decode(self, tlv, offset, decode_path, ctx):
3376 for choice, spec in self.specs.items():
3378 value, tail = spec.decode(
3382 decode_path=decode_path + (choice,),
3387 obj = self.__class__(
3390 default=self.default,
3391 optional=self.optional,
3392 _decoded=(offset, 0, value.tlvlen),
3394 obj._value = (choice, value)
3397 klass=self.__class__,
3398 decode_path=decode_path,
3403 value = pp_console_row(next(self.pps()))
3405 value = "%s[%r]" % (value, self.value)
3408 def pps(self, decode_path=()):
3410 asn1_type_name=self.asn1_type_name,
3411 obj_name=self.__class__.__name__,
3412 decode_path=decode_path,
3413 value=self.choice if self.ready else None,
3414 optional=self.optional,
3415 default=self == self.default,
3416 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3417 expl=None if self._expl is None else tag_decode(self._expl),
3424 yield self.value.pps(decode_path=decode_path + (self.choice,))
3427 class PrimitiveTypes(Choice):
3428 """Predefined ``CHOICE`` for all generic primitive types
3430 It could be useful for general decoding of some unspecified values:
3432 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3433 OCTET STRING 3 bytes 666f6f
3434 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3438 schema = tuple((klass.__name__, klass()) for klass in (
3463 """``ANY`` special type
3465 >>> Any(Integer(-123))
3467 >>> a = Any(OctetString(b"hello world").encode())
3468 ANY 040b68656c6c6f20776f726c64
3469 >>> hexenc(bytes(a))
3470 b'0x040x0bhello world'
3472 __slots__ = ("defined",)
3473 tag_default = tag_encode(0)
3474 asn1_type_name = "ANY"
3484 :param value: set the value. Either any kind of pyderasn's
3485 **ready** object, or bytes. Pay attention that
3486 **no** validation is performed is raw binary value
3488 :param bytes expl: override default tag with ``EXPLICIT`` one
3489 :param bool optional: is object ``OPTIONAL`` in sequence
3491 super(Any, self).__init__(None, expl, None, optional, _decoded)
3492 self._value = None if value is None else self._value_sanitize(value)
3495 def _value_sanitize(self, value):
3496 if isinstance(value, self.__class__):
3498 if isinstance(value, Obj):
3499 return value.encode()
3500 if isinstance(value, binary_type):
3502 raise InvalidValueType((self.__class__, Obj, binary_type))
3506 return self._value is not None
3509 obj = self.__class__()
3510 obj._value = self._value
3512 obj._expl = self._expl
3513 obj.optional = self.optional
3514 obj.offset = self.offset
3515 obj.llen = self.llen
3516 obj.vlen = self.vlen
3519 def __eq__(self, their):
3520 if isinstance(their, binary_type):
3521 return self._value == their
3522 if issubclass(their.__class__, Any):
3523 return self._value == their._value
3532 return self.__class__(
3534 expl=self._expl if expl is None else expl,
3535 optional=self.optional if optional is None else optional,
3538 def __bytes__(self):
3539 self._assert_ready()
3547 self._assert_ready()
3550 def _decode(self, tlv, offset, decode_path, ctx):
3552 t, tlen, lv = tag_strip(tlv)
3553 l, llen, v = len_decode(lv)
3554 except DecodeError as err:
3555 raise err.__class__(
3557 klass=self.__class__,
3558 decode_path=decode_path,
3562 raise NotEnoughData(
3563 "encoded length is longer than data",
3564 klass=self.__class__,
3565 decode_path=decode_path,
3568 tlvlen = tlen + llen + l
3569 v, tail = tlv[:tlvlen], v[l:]
3570 obj = self.__class__(
3573 optional=self.optional,
3574 _decoded=(offset, 0, tlvlen),
3580 return pp_console_row(next(self.pps()))
3582 def pps(self, decode_path=()):
3584 asn1_type_name=self.asn1_type_name,
3585 obj_name=self.__class__.__name__,
3586 decode_path=decode_path,
3587 blob=self._value if self.ready else None,
3588 optional=self.optional,
3589 default=self == self.default,
3590 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3591 expl=None if self._expl is None else tag_decode(self._expl),
3596 expl_offset=self.expl_offset if self.expled else None,
3597 expl_tlen=self.expl_tlen if self.expled else None,
3598 expl_llen=self.expl_llen if self.expled else None,
3599 expl_vlen=self.expl_vlen if self.expled else None,
3601 defined_by, defined = self.defined or (None, None)
3602 if defined_by is not None:
3604 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3608 ########################################################################
3609 # ASN.1 constructed types
3610 ########################################################################
3612 def get_def_by_path(defines_by_path, sub_decode_path):
3613 """Get define by decode path
3615 for path, define in defines_by_path:
3616 if len(path) != len(sub_decode_path):
3618 for p1, p2 in zip(path, sub_decode_path):
3619 if (p1 != any) and (p1 != p2):
3625 def abs_decode_path(decode_path, rel_path):
3626 """Create an absolute decode path from current and relative ones
3628 :param decode_path: current decode path, starting point.
3630 :param rel_path: relative path to ``decode_path``. Tuple of strings.
3631 If first tuple's element is "/", then treat it as
3632 an absolute path, ignoring ``decode_path`` as
3633 starting point. Also this tuple can contain ".."
3634 elements, stripping the leading element from
3637 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
3638 ("foo", "bar", "baz", "whatever")
3639 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
3641 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
3644 if rel_path[0] == "/":
3646 if rel_path[0] == "..":
3647 return abs_decode_path(decode_path[:-1], rel_path[1:])
3648 return decode_path + rel_path
3651 class Sequence(Obj):
3652 """``SEQUENCE`` structure type
3654 You have to make specification of sequence::
3656 class Extension(Sequence):
3658 ("extnID", ObjectIdentifier()),
3659 ("critical", Boolean(default=False)),
3660 ("extnValue", OctetString()),
3663 Then, you can work with it as with dictionary.
3665 >>> ext = Extension()
3666 >>> Extension().specs
3668 ('extnID', OBJECT IDENTIFIER),
3669 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3670 ('extnValue', OCTET STRING),
3672 >>> ext["extnID"] = "1.2.3"
3673 Traceback (most recent call last):
3674 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3675 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3677 You can know if sequence is ready to be encoded:
3682 Traceback (most recent call last):
3683 pyderasn.ObjNotReady: object is not ready: extnValue
3684 >>> ext["extnValue"] = OctetString(b"foobar")
3688 Value you want to assign, must have the same **type** as in
3689 corresponding specification, but it can have different tags,
3690 optional/default attributes -- they will be taken from specification
3693 class TBSCertificate(Sequence):
3695 ("version", Version(expl=tag_ctxc(0), default="v1")),
3698 >>> tbs = TBSCertificate()
3699 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3701 Assign ``None`` to remove value from sequence.
3703 You can know if value exists/set in the sequence and take its value:
3705 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3708 OBJECT IDENTIFIER 1.2.3
3710 But pay attention that if value has default, then it won't be (not
3711 in) in the sequence (because ``DEFAULT`` must not be encoded in
3712 DER), but you can read its value:
3714 >>> "critical" in ext, ext["critical"]
3715 (False, BOOLEAN False)
3716 >>> ext["critical"] = Boolean(True)
3717 >>> "critical" in ext, ext["critical"]
3718 (True, BOOLEAN True)
3720 All defaulted values are always optional.
3722 .. _strict_default_existence_ctx:
3726 When decoded DER contains defaulted value inside, then
3727 technically this is not valid DER encoding. But we allow and pass
3728 it **by default**. Of course reencoding of that kind of DER will
3729 result in different binary representation (validly without
3730 defaulted value inside). You can enable strict defaulted values
3731 existence validation by setting ``"strict_default_existence":
3732 True`` :ref:`context <ctx>` option -- decoding process will raise
3733 an exception if defaulted value is met.
3735 Two sequences are equal if they have equal specification (schema),
3736 implicit/explicit tagging and the same values.
3738 __slots__ = ("specs",)
3739 tag_default = tag_encode(form=TagFormConstructed, num=16)
3740 asn1_type_name = "SEQUENCE"
3752 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3754 schema = getattr(self, "schema", ())
3756 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3759 if value is not None:
3760 self._value = self._value_sanitize(value)
3761 if default is not None:
3762 default_value = self._value_sanitize(default)
3763 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3764 default_obj.specs = self.specs
3765 default_obj._value = default_value
3766 self.default = default_obj
3768 self._value = default_obj.copy()._value
3770 def _value_sanitize(self, value):
3771 if not issubclass(value.__class__, Sequence):
3772 raise InvalidValueType((Sequence,))
3777 for name, spec in self.specs.items():
3778 value = self._value.get(name)
3789 obj = self.__class__(schema=self.specs)
3791 obj._expl = self._expl
3792 obj.default = self.default
3793 obj.optional = self.optional
3794 obj.offset = self.offset
3795 obj.llen = self.llen
3796 obj.vlen = self.vlen
3797 obj._value = {k: v.copy() for k, v in self._value.items()}
3800 def __eq__(self, their):
3801 if not isinstance(their, self.__class__):
3804 self.specs == their.specs and
3805 self.tag == their.tag and
3806 self._expl == their._expl and
3807 self._value == their._value
3818 return self.__class__(
3821 impl=self.tag if impl is None else impl,
3822 expl=self._expl if expl is None else expl,
3823 default=self.default if default is None else default,
3824 optional=self.optional if optional is None else optional,
3827 def __contains__(self, key):
3828 return key in self._value
3830 def __setitem__(self, key, value):
3831 spec = self.specs.get(key)
3833 raise ObjUnknown(key)
3835 self._value.pop(key, None)
3837 if not isinstance(value, spec.__class__):
3838 raise InvalidValueType((spec.__class__,))
3839 value = spec(value=value)
3840 if spec.default is not None and value == spec.default:
3841 self._value.pop(key, None)
3843 self._value[key] = value
3845 def __getitem__(self, key):
3846 value = self._value.get(key)
3847 if value is not None:
3849 spec = self.specs.get(key)
3851 raise ObjUnknown(key)
3852 if spec.default is not None:
3856 def _encoded_values(self):
3858 for name, spec in self.specs.items():
3859 value = self._value.get(name)
3863 raise ObjNotReady(name)
3864 raws.append(value.encode())
3868 v = b"".join(self._encoded_values())
3869 return b"".join((self.tag, len_encode(len(v)), v))
3871 def _decode(self, tlv, offset, decode_path, ctx):
3873 t, tlen, lv = tag_strip(tlv)
3874 except DecodeError as err:
3875 raise err.__class__(
3877 klass=self.__class__,
3878 decode_path=decode_path,
3883 klass=self.__class__,
3884 decode_path=decode_path,
3888 l, llen, v = len_decode(lv)
3889 except DecodeError as err:
3890 raise err.__class__(
3892 klass=self.__class__,
3893 decode_path=decode_path,
3897 raise NotEnoughData(
3898 "encoded length is longer than data",
3899 klass=self.__class__,
3900 decode_path=decode_path,
3903 v, tail = v[:l], v[l:]
3904 sub_offset = offset + tlen + llen
3906 for name, spec in self.specs.items():
3907 if len(v) == 0 and spec.optional:
3909 sub_decode_path = decode_path + (name,)
3911 value, v_tail = spec.decode(
3915 decode_path=sub_decode_path,
3923 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
3924 if defined is not None:
3925 defined_by, defined_spec = defined
3926 if issubclass(value.__class__, SequenceOf):
3927 for i, _value in enumerate(value):
3928 sub_sub_decode_path = sub_decode_path + (
3930 DecodePathDefBy(defined_by),
3932 defined_value, defined_tail = defined_spec.decode(
3933 memoryview(bytes(_value)),
3935 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
3936 if value.expled else (value.tlen + value.llen)
3939 decode_path=sub_sub_decode_path,
3942 if len(defined_tail) > 0:
3945 klass=self.__class__,
3946 decode_path=sub_sub_decode_path,
3949 _value.defined = (defined_by, defined_value)
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_decode_path + (DecodePathDefBy(defined_by),),
3961 if len(defined_tail) > 0:
3964 klass=self.__class__,
3965 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
3968 value.defined = (defined_by, defined_value)
3970 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3972 if spec.default is not None and value == spec.default:
3973 if ctx.get("strict_default_existence", False):
3975 "DEFAULT value met",
3976 klass=self.__class__,
3977 decode_path=sub_decode_path,
3982 values[name] = value
3984 spec_defines = getattr(spec, "defines", ())
3985 if len(spec_defines) == 0:
3986 defines_by_path = ctx.get("defines_by_path", ())
3987 if len(defines_by_path) > 0:
3988 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
3989 if spec_defines is not None and len(spec_defines) > 0:
3990 for rel_path, schema in spec_defines:
3991 defined = schema.get(value, None)
3992 if defined is not None:
3993 ctx.setdefault("defines", []).append((
3994 abs_decode_path(sub_decode_path[:-1], rel_path),
4000 klass=self.__class__,
4001 decode_path=decode_path,
4004 obj = self.__class__(
4008 default=self.default,
4009 optional=self.optional,
4010 _decoded=(offset, llen, l),
4016 value = pp_console_row(next(self.pps()))
4018 for name in self.specs:
4019 _value = self._value.get(name)
4022 cols.append(repr(_value))
4023 return "%s[%s]" % (value, ", ".join(cols))
4025 def pps(self, decode_path=()):
4027 asn1_type_name=self.asn1_type_name,
4028 obj_name=self.__class__.__name__,
4029 decode_path=decode_path,
4030 optional=self.optional,
4031 default=self == self.default,
4032 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4033 expl=None if self._expl is None else tag_decode(self._expl),
4038 expl_offset=self.expl_offset if self.expled else None,
4039 expl_tlen=self.expl_tlen if self.expled else None,
4040 expl_llen=self.expl_llen if self.expled else None,
4041 expl_vlen=self.expl_vlen if self.expled else None,
4043 for name in self.specs:
4044 value = self._value.get(name)
4047 yield value.pps(decode_path=decode_path + (name,))
4050 class Set(Sequence):
4051 """``SET`` structure type
4053 Its usage is identical to :py:class:`pyderasn.Sequence`.
4056 tag_default = tag_encode(form=TagFormConstructed, num=17)
4057 asn1_type_name = "SET"
4060 raws = self._encoded_values()
4063 return b"".join((self.tag, len_encode(len(v)), v))
4065 def _decode(self, tlv, offset, decode_path, ctx):
4067 t, tlen, lv = tag_strip(tlv)
4068 except DecodeError as err:
4069 raise err.__class__(
4071 klass=self.__class__,
4072 decode_path=decode_path,
4077 klass=self.__class__,
4078 decode_path=decode_path,
4082 l, llen, v = len_decode(lv)
4083 except DecodeError as err:
4084 raise err.__class__(
4086 klass=self.__class__,
4087 decode_path=decode_path,
4091 raise NotEnoughData(
4092 "encoded length is longer than data",
4093 klass=self.__class__,
4096 v, tail = v[:l], v[l:]
4097 sub_offset = offset + tlen + llen
4099 specs_items = self.specs.items
4101 for name, spec in specs_items():
4103 value, v_tail = spec.decode(
4107 decode_path=decode_path + (name,),
4113 value.expl_tlvlen if value.expled else value.tlvlen
4116 if spec.default is None or value != spec.default: # pragma: no cover
4117 # SeqMixing.test_encoded_default_accepted covers that place
4118 values[name] = value
4122 klass=self.__class__,
4123 decode_path=decode_path,
4126 obj = self.__class__(
4130 default=self.default,
4131 optional=self.optional,
4132 _decoded=(offset, llen, l),
4138 class SequenceOf(Obj):
4139 """``SEQUENCE OF`` sequence type
4141 For that kind of type you must specify the object it will carry on
4142 (bounds are for example here, not required)::
4144 class Ints(SequenceOf):
4149 >>> ints.append(Integer(123))
4150 >>> ints.append(Integer(234))
4152 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4153 >>> [int(i) for i in ints]
4155 >>> ints.append(Integer(345))
4156 Traceback (most recent call last):
4157 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4160 >>> ints[1] = Integer(345)
4162 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4164 Also you can initialize sequence with preinitialized values:
4166 >>> ints = Ints([Integer(123), Integer(234)])
4168 __slots__ = ("spec", "_bound_min", "_bound_max")
4169 tag_default = tag_encode(form=TagFormConstructed, num=16)
4170 asn1_type_name = "SEQUENCE OF"
4183 super(SequenceOf, self).__init__(
4191 schema = getattr(self, "schema", None)
4193 raise ValueError("schema must be specified")
4195 self._bound_min, self._bound_max = getattr(
4199 ) if bounds is None else bounds
4201 if value is not None:
4202 self._value = self._value_sanitize(value)
4203 if default is not None:
4204 default_value = self._value_sanitize(default)
4205 default_obj = self.__class__(
4210 default_obj._value = default_value
4211 self.default = default_obj
4213 self._value = default_obj.copy()._value
4215 def _value_sanitize(self, value):
4216 if issubclass(value.__class__, SequenceOf):
4217 value = value._value
4218 elif hasattr(value, "__iter__"):
4221 raise InvalidValueType((self.__class__, iter))
4222 if not self._bound_min <= len(value) <= self._bound_max:
4223 raise BoundsError(self._bound_min, len(value), self._bound_max)
4225 if not isinstance(v, self.spec.__class__):
4226 raise InvalidValueType((self.spec.__class__,))
4231 return all(v.ready for v in self._value)
4234 obj = self.__class__(schema=self.spec)
4235 obj._bound_min = self._bound_min
4236 obj._bound_max = self._bound_max
4238 obj._expl = self._expl
4239 obj.default = self.default
4240 obj.optional = self.optional
4241 obj.offset = self.offset
4242 obj.llen = self.llen
4243 obj.vlen = self.vlen
4244 obj._value = [v.copy() for v in self._value]
4247 def __eq__(self, their):
4248 if isinstance(their, self.__class__):
4250 self.spec == their.spec and
4251 self.tag == their.tag and
4252 self._expl == their._expl and
4253 self._value == their._value
4255 if hasattr(their, "__iter__"):
4256 return self._value == list(their)
4268 return self.__class__(
4272 (self._bound_min, self._bound_max)
4273 if bounds is None else bounds
4275 impl=self.tag if impl is None else impl,
4276 expl=self._expl if expl is None else expl,
4277 default=self.default if default is None else default,
4278 optional=self.optional if optional is None else optional,
4281 def __contains__(self, key):
4282 return key in self._value
4284 def append(self, value):
4285 if not isinstance(value, self.spec.__class__):
4286 raise InvalidValueType((self.spec.__class__,))
4287 if len(self._value) + 1 > self._bound_max:
4290 len(self._value) + 1,
4293 self._value.append(value)
4296 self._assert_ready()
4297 return iter(self._value)
4300 self._assert_ready()
4301 return len(self._value)
4303 def __setitem__(self, key, value):
4304 if not isinstance(value, self.spec.__class__):
4305 raise InvalidValueType((self.spec.__class__,))
4306 self._value[key] = self.spec(value=value)
4308 def __getitem__(self, key):
4309 return self._value[key]
4311 def _encoded_values(self):
4312 return [v.encode() for v in self._value]
4315 v = b"".join(self._encoded_values())
4316 return b"".join((self.tag, len_encode(len(v)), v))
4318 def _decode(self, tlv, offset, decode_path, ctx):
4320 t, tlen, lv = tag_strip(tlv)
4321 except DecodeError as err:
4322 raise err.__class__(
4324 klass=self.__class__,
4325 decode_path=decode_path,
4330 klass=self.__class__,
4331 decode_path=decode_path,
4335 l, llen, v = len_decode(lv)
4336 except DecodeError as err:
4337 raise err.__class__(
4339 klass=self.__class__,
4340 decode_path=decode_path,
4344 raise NotEnoughData(
4345 "encoded length is longer than data",
4346 klass=self.__class__,
4347 decode_path=decode_path,
4350 v, tail = v[:l], v[l:]
4351 sub_offset = offset + tlen + llen
4355 value, v_tail = spec.decode(
4359 decode_path=decode_path + (str(len(_value)),),
4362 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4364 _value.append(value)
4365 obj = self.__class__(
4368 bounds=(self._bound_min, self._bound_max),
4371 default=self.default,
4372 optional=self.optional,
4373 _decoded=(offset, llen, l),
4379 pp_console_row(next(self.pps())),
4380 ", ".join(repr(v) for v in self._value),
4383 def pps(self, decode_path=()):
4385 asn1_type_name=self.asn1_type_name,
4386 obj_name=self.__class__.__name__,
4387 decode_path=decode_path,
4388 optional=self.optional,
4389 default=self == self.default,
4390 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4391 expl=None if self._expl is None else tag_decode(self._expl),
4396 expl_offset=self.expl_offset if self.expled else None,
4397 expl_tlen=self.expl_tlen if self.expled else None,
4398 expl_llen=self.expl_llen if self.expled else None,
4399 expl_vlen=self.expl_vlen if self.expled else None,
4401 for i, value in enumerate(self._value):
4402 yield value.pps(decode_path=decode_path + (str(i),))
4405 class SetOf(SequenceOf):
4406 """``SET OF`` sequence type
4408 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4411 tag_default = tag_encode(form=TagFormConstructed, num=17)
4412 asn1_type_name = "SET OF"
4415 raws = self._encoded_values()
4418 return b"".join((self.tag, len_encode(len(v)), v))
4421 def obj_by_path(pypath): # pragma: no cover
4422 """Import object specified as string Python path
4424 Modules must be separated from classes/functions with ``:``.
4426 >>> obj_by_path("foo.bar:Baz")
4427 <class 'foo.bar.Baz'>
4428 >>> obj_by_path("foo.bar:Baz.boo")
4429 <classmethod 'foo.bar.Baz.boo'>
4431 mod, objs = pypath.rsplit(":", 1)
4432 from importlib import import_module
4433 obj = import_module(mod)
4434 for obj_name in objs.split("."):
4435 obj = getattr(obj, obj_name)
4439 def generic_decoder(): # pragma: no cover
4440 # All of this below is a big hack with self references
4441 choice = PrimitiveTypes()
4442 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4443 choice.specs["SetOf"] = SetOf(schema=choice)
4445 choice.specs["SequenceOf%d" % i] = SequenceOf(
4449 choice.specs["Any"] = Any()
4451 # Class name equals to type name, to omit it from output
4452 class SEQUENCEOF(SequenceOf):
4456 def pprint_any(obj, oids=None, with_colours=False):
4457 def _pprint_pps(pps):
4459 if hasattr(pp, "_fields"):
4460 if pp.asn1_type_name == Choice.asn1_type_name:
4462 pp_kwargs = pp._asdict()
4463 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4464 pp = _pp(**pp_kwargs)
4465 yield pp_console_row(
4470 with_colours=with_colours,
4472 for row in pp_console_blob(pp):
4475 for row in _pprint_pps(pp):
4477 return "\n".join(_pprint_pps(obj.pps()))
4478 return SEQUENCEOF(), pprint_any
4481 def main(): # pragma: no cover
4483 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4484 parser.add_argument(
4488 help="Skip that number of bytes from the beginning",
4490 parser.add_argument(
4492 help="Python path to dictionary with OIDs",
4494 parser.add_argument(
4496 help="Python path to schema definition to use",
4498 parser.add_argument(
4499 "--defines-by-path",
4500 help="Python path to decoder's defines_by_path",
4502 parser.add_argument(
4504 action='store_true',
4505 help="Enable coloured output",
4507 parser.add_argument(
4509 type=argparse.FileType("rb"),
4510 help="Path to DER file you want to decode",
4512 args = parser.parse_args()
4513 args.DERFile.seek(args.skip)
4514 der = memoryview(args.DERFile.read())
4515 args.DERFile.close()
4516 oids = obj_by_path(args.oids) if args.oids else {}
4518 schema = obj_by_path(args.schema)
4519 from functools import partial
4520 pprinter = partial(pprint, big_blobs=True)
4522 schema, pprinter = generic_decoder()
4523 obj, tail = schema().decode(
4526 None if args.defines_by_path is None else
4527 {"defines_by_path": obj_by_path(args.defines_by_path)}
4533 with_colours=True if args.with_colours else False,
4536 print("\nTrailing data: %s" % hexenc(tail))
4539 if __name__ == "__main__":