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
475 from os import environ
477 from six import add_metaclass
478 from six import binary_type
479 from six import byte2int
480 from six import indexbytes
481 from six import int2byte
482 from six import integer_types
483 from six import iterbytes
485 from six import string_types
486 from six import text_type
487 from six.moves import xrange as six_xrange
491 from termcolor import colored
493 def colored(what, *args):
536 "TagClassApplication",
540 "TagFormConstructed",
551 TagClassUniversal = 0
552 TagClassApplication = 1 << 6
553 TagClassContext = 1 << 7
554 TagClassPrivate = 1 << 6 | 1 << 7
556 TagFormConstructed = 1 << 5
559 TagClassApplication: "APPLICATION ",
560 TagClassPrivate: "PRIVATE ",
561 TagClassUniversal: "UNIV ",
565 ########################################################################
567 ########################################################################
569 class DecodeError(Exception):
570 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
572 :param str msg: reason of decode failing
573 :param klass: optional exact DecodeError inherited class (like
574 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
575 :py:exc:`InvalidLength`)
576 :param decode_path: tuple of strings. It contains human
577 readable names of the fields through which
578 decoding process has passed
579 :param int offset: binary offset where failure happened
581 super(DecodeError, self).__init__()
584 self.decode_path = decode_path
590 "" if self.klass is None else self.klass.__name__,
592 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
593 if len(self.decode_path) > 0 else ""
595 ("(at %d)" % self.offset) if self.offset > 0 else "",
601 return "%s(%s)" % (self.__class__.__name__, self)
604 class NotEnoughData(DecodeError):
608 class TagMismatch(DecodeError):
612 class InvalidLength(DecodeError):
616 class InvalidOID(DecodeError):
620 class ObjUnknown(ValueError):
621 def __init__(self, name):
622 super(ObjUnknown, self).__init__()
626 return "object is unknown: %s" % self.name
629 return "%s(%s)" % (self.__class__.__name__, self)
632 class ObjNotReady(ValueError):
633 def __init__(self, name):
634 super(ObjNotReady, self).__init__()
638 return "object is not ready: %s" % self.name
641 return "%s(%s)" % (self.__class__.__name__, self)
644 class InvalidValueType(ValueError):
645 def __init__(self, expected_types):
646 super(InvalidValueType, self).__init__()
647 self.expected_types = expected_types
650 return "invalid value type, expected: %s" % ", ".join(
651 [repr(t) for t in self.expected_types]
655 return "%s(%s)" % (self.__class__.__name__, self)
658 class BoundsError(ValueError):
659 def __init__(self, bound_min, value, bound_max):
660 super(BoundsError, self).__init__()
661 self.bound_min = bound_min
663 self.bound_max = bound_max
666 return "unsatisfied bounds: %s <= %s <= %s" % (
673 return "%s(%s)" % (self.__class__.__name__, self)
676 ########################################################################
678 ########################################################################
680 _hexdecoder = getdecoder("hex")
681 _hexencoder = getencoder("hex")
685 """Binary data to hexadecimal string convert
687 return _hexdecoder(data)[0]
691 """Hexadecimal string to binary data convert
693 return _hexencoder(data)[0].decode("ascii")
696 def int_bytes_len(num, byte_len=8):
699 return int(ceil(float(num.bit_length()) / byte_len))
702 def zero_ended_encode(num):
703 octets = bytearray(int_bytes_len(num, 7))
705 octets[i] = num & 0x7F
709 octets[i] = 0x80 | (num & 0x7F)
715 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
716 """Encode tag to binary form
718 :param int num: tag's number
719 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
720 :py:data:`pyderasn.TagClassContext`,
721 :py:data:`pyderasn.TagClassApplication`,
722 :py:data:`pyderasn.TagClassPrivate`)
723 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
724 :py:data:`pyderasn.TagFormConstructed`)
728 return int2byte(klass | form | num)
729 # [XX|X|11111][1.......][1.......] ... [0.......]
730 return int2byte(klass | form | 31) + zero_ended_encode(num)
734 """Decode tag from binary form
738 No validation is performed, assuming that it has already passed.
740 It returns tuple with three integers, as
741 :py:func:`pyderasn.tag_encode` accepts.
743 first_octet = byte2int(tag)
744 klass = first_octet & 0xC0
745 form = first_octet & 0x20
746 if first_octet & 0x1F < 0x1F:
747 return (klass, form, first_octet & 0x1F)
749 for octet in iterbytes(tag[1:]):
752 return (klass, form, num)
756 """Create CONTEXT PRIMITIVE tag
758 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
762 """Create CONTEXT CONSTRUCTED tag
764 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
768 """Take off tag from the data
770 :returns: (encoded tag, tag length, remaining data)
773 raise NotEnoughData("no data at all")
774 if byte2int(data) & 0x1F < 31:
775 return data[:1], 1, data[1:]
780 raise DecodeError("unfinished tag")
781 if indexbytes(data, i) & 0x80 == 0:
784 return data[:i], i, data[i:]
790 octets = bytearray(int_bytes_len(l) + 1)
791 octets[0] = 0x80 | (len(octets) - 1)
792 for i in six_xrange(len(octets) - 1, 0, -1):
798 def len_decode(data):
800 raise NotEnoughData("no data at all")
801 first_octet = byte2int(data)
802 if first_octet & 0x80 == 0:
803 return first_octet, 1, data[1:]
804 octets_num = first_octet & 0x7F
805 if octets_num + 1 > len(data):
806 raise NotEnoughData("encoded length is longer than data")
808 raise DecodeError("long form instead of short one")
809 if byte2int(data[1:]) == 0:
810 raise DecodeError("leading zeros")
812 for v in iterbytes(data[1:1 + octets_num]):
815 raise DecodeError("long form instead of short one")
816 return l, 1 + octets_num, data[1 + octets_num:]
819 ########################################################################
821 ########################################################################
823 class AutoAddSlots(type):
824 def __new__(mcs, name, bases, _dict):
825 _dict["__slots__"] = _dict.get("__slots__", ())
826 return type.__new__(mcs, name, bases, _dict)
829 @add_metaclass(AutoAddSlots)
831 """Common ASN.1 object class
833 All ASN.1 types are inherited from it. It has metaclass that
834 automatically adds ``__slots__`` to all inherited classes.
855 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
856 self._expl = getattr(self, "expl", None) if expl is None else expl
857 if self.tag != self.tag_default and self._expl is not None:
859 "implicit and explicit tags can not be set simultaneously"
861 if default is not None:
863 self.optional = optional
864 self.offset, self.llen, self.vlen = _decoded
868 def ready(self): # pragma: no cover
869 """Is object ready to be encoded?
871 raise NotImplementedError()
873 def _assert_ready(self):
875 raise ObjNotReady(self.__class__.__name__)
879 """Is object decoded?
881 return (self.llen + self.vlen) > 0
883 def copy(self): # pragma: no cover
884 """Make a copy of object, safe to be mutated
886 raise NotImplementedError()
894 return self.tlen + self.llen + self.vlen
896 def __str__(self): # pragma: no cover
897 return self.__bytes__() if PY2 else self.__unicode__()
899 def __ne__(self, their):
900 return not(self == their)
902 def __gt__(self, their): # pragma: no cover
903 return not(self < their)
905 def __le__(self, their): # pragma: no cover
906 return (self == their) or (self < their)
908 def __ge__(self, their): # pragma: no cover
909 return (self == their) or (self > their)
911 def _encode(self): # pragma: no cover
912 raise NotImplementedError()
914 def _decode(self, tlv, offset, decode_path, ctx): # pragma: no cover
915 raise NotImplementedError()
919 if self._expl is None:
921 return b"".join((self._expl, len_encode(len(raw)), raw))
923 def decode(self, data, offset=0, leavemm=False, decode_path=(), ctx=None):
926 :param data: either binary or memoryview
927 :param int offset: initial data's offset
928 :param bool leavemm: do we need to leave memoryview of remaining
929 data as is, or convert it to bytes otherwise
930 :param ctx: optional :ref:`context <ctx>` governing decoding process.
931 :returns: (Obj, remaining data)
935 tlv = memoryview(data)
936 if self._expl is None:
937 obj, tail = self._decode(
940 decode_path=decode_path,
945 t, tlen, lv = tag_strip(tlv)
946 except DecodeError as err:
949 klass=self.__class__,
950 decode_path=decode_path,
955 klass=self.__class__,
956 decode_path=decode_path,
960 l, llen, v = len_decode(lv)
961 except DecodeError as err:
964 klass=self.__class__,
965 decode_path=decode_path,
970 "encoded length is longer than data",
971 klass=self.__class__,
972 decode_path=decode_path,
975 obj, tail = self._decode(
977 offset=offset + tlen + llen,
978 decode_path=decode_path,
981 return obj, (tail if leavemm else tail.tobytes())
985 return self._expl is not None
993 return len(self._expl)
997 return len(len_encode(self.tlvlen))
1000 def expl_offset(self):
1001 return self.offset - self.expl_tlen - self.expl_llen
1004 def expl_vlen(self):
1008 def expl_tlvlen(self):
1009 return self.expl_tlen + self.expl_llen + self.expl_vlen
1012 class DecodePathDefBy(object):
1013 """DEFINED BY representation inside decode path
1015 __slots__ = ('defined_by',)
1017 def __init__(self, defined_by):
1018 self.defined_by = defined_by
1020 def __eq__(self, their):
1021 if not isinstance(their, self.__class__):
1023 return self.defined_by == their.defined_by
1026 return "DEFINED BY " + str(self.defined_by)
1029 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1032 ########################################################################
1034 ########################################################################
1036 PP = namedtuple("PP", (
1058 asn1_type_name="unknown",
1097 def _colorize(what, colour, with_colours, attrs=("bold",)):
1098 return colored(what, colour, attrs=attrs) if with_colours else what
1113 " " if pp.expl_offset is None else
1114 ("-%d" % (pp.offset - pp.expl_offset))
1117 cols.append(_colorize(col, "red", with_colours, ()))
1118 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1119 cols.append(_colorize(col, "green", with_colours, ()))
1120 if len(pp.decode_path) > 0:
1121 cols.append(" ." * (len(pp.decode_path)))
1122 ent = pp.decode_path[-1]
1123 if isinstance(ent, DecodePathDefBy):
1124 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1125 value = str(ent.defined_by)
1127 oids is not None and
1128 ent.defined_by.asn1_type_name ==
1129 ObjectIdentifier.asn1_type_name and
1132 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1134 cols.append(_colorize("%s:" % value, "white", with_colours))
1136 cols.append(_colorize("%s:" % ent, "yellow", with_colours))
1137 if pp.expl is not None:
1138 klass, _, num = pp.expl
1139 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1140 cols.append(_colorize(col, "blue", with_colours))
1141 if pp.impl is not None:
1142 klass, _, num = pp.impl
1143 col = "[%s%d]" % (TagClassReprs[klass], num)
1144 cols.append(_colorize(col, "blue", with_colours))
1145 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1146 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1147 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1148 if pp.value is not None:
1150 cols.append(_colorize(value, "white", with_colours))
1152 oids is not None and
1153 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1156 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1158 if isinstance(pp.blob, binary_type):
1159 cols.append(hexenc(pp.blob))
1160 elif isinstance(pp.blob, tuple):
1161 cols.append(", ".join(pp.blob))
1163 cols.append(_colorize("OPTIONAL", "red", with_colours))
1165 cols.append(_colorize("DEFAULT", "red", with_colours))
1166 return " ".join(cols)
1169 def pp_console_blob(pp):
1170 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
1171 if len(pp.decode_path) > 0:
1172 cols.append(" ." * (len(pp.decode_path) + 1))
1173 if isinstance(pp.blob, binary_type):
1174 blob = hexenc(pp.blob).upper()
1175 for i in range(0, len(blob), 32):
1176 chunk = blob[i:i + 32]
1177 yield " ".join(cols + [":".join(
1178 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1180 elif isinstance(pp.blob, tuple):
1181 yield " ".join(cols + [", ".join(pp.blob)])
1184 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1185 """Pretty print object
1187 :param Obj obj: object you want to pretty print
1188 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1189 from it is met, then its humand readable form is printed
1190 :param big_blobs: if large binary objects are met (like OctetString
1191 values), do we need to print them too, on separate
1193 :param with_colours: colourize output, if ``termcolor`` library
1196 def _pprint_pps(pps):
1198 if hasattr(pp, "_fields"):
1200 yield pp_console_row(
1205 with_colours=with_colours,
1207 for row in pp_console_blob(pp):
1210 yield pp_console_row(
1215 with_colours=with_colours,
1218 for row in _pprint_pps(pp):
1220 return "\n".join(_pprint_pps(obj.pps()))
1223 ########################################################################
1224 # ASN.1 primitive types
1225 ########################################################################
1228 """``BOOLEAN`` boolean type
1230 >>> b = Boolean(True)
1232 >>> b == Boolean(True)
1238 tag_default = tag_encode(1)
1239 asn1_type_name = "BOOLEAN"
1251 :param value: set the value. Either boolean type, or
1252 :py:class:`pyderasn.Boolean` object
1253 :param bytes impl: override default tag with ``IMPLICIT`` one
1254 :param bytes expl: override default tag with ``EXPLICIT`` one
1255 :param default: set default value. Type same as in ``value``
1256 :param bool optional: is object ``OPTIONAL`` in sequence
1258 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1259 self._value = None if value is None else self._value_sanitize(value)
1260 if default is not None:
1261 default = self._value_sanitize(default)
1262 self.default = self.__class__(
1268 self._value = default
1270 def _value_sanitize(self, value):
1271 if issubclass(value.__class__, Boolean):
1273 if isinstance(value, bool):
1275 raise InvalidValueType((self.__class__, bool))
1279 return self._value is not None
1282 obj = self.__class__()
1283 obj._value = self._value
1285 obj._expl = self._expl
1286 obj.default = self.default
1287 obj.optional = self.optional
1288 obj.offset = self.offset
1289 obj.llen = self.llen
1290 obj.vlen = self.vlen
1293 def __nonzero__(self):
1294 self._assert_ready()
1298 self._assert_ready()
1301 def __eq__(self, their):
1302 if isinstance(their, bool):
1303 return self._value == their
1304 if not issubclass(their.__class__, Boolean):
1307 self._value == their._value and
1308 self.tag == their.tag and
1309 self._expl == their._expl
1320 return self.__class__(
1322 impl=self.tag if impl is None else impl,
1323 expl=self._expl if expl is None else expl,
1324 default=self.default if default is None else default,
1325 optional=self.optional if optional is None else optional,
1329 self._assert_ready()
1333 (b"\xFF" if self._value else b"\x00"),
1336 def _decode(self, tlv, offset, decode_path, ctx):
1338 t, _, lv = tag_strip(tlv)
1339 except DecodeError as err:
1340 raise err.__class__(
1342 klass=self.__class__,
1343 decode_path=decode_path,
1348 klass=self.__class__,
1349 decode_path=decode_path,
1353 l, _, v = len_decode(lv)
1354 except DecodeError as err:
1355 raise err.__class__(
1357 klass=self.__class__,
1358 decode_path=decode_path,
1362 raise InvalidLength(
1363 "Boolean's length must be equal to 1",
1364 klass=self.__class__,
1365 decode_path=decode_path,
1369 raise NotEnoughData(
1370 "encoded length is longer than data",
1371 klass=self.__class__,
1372 decode_path=decode_path,
1375 first_octet = byte2int(v)
1376 if first_octet == 0:
1378 elif first_octet == 0xFF:
1382 "unacceptable Boolean value",
1383 klass=self.__class__,
1384 decode_path=decode_path,
1387 obj = self.__class__(
1391 default=self.default,
1392 optional=self.optional,
1393 _decoded=(offset, 1, 1),
1398 return pp_console_row(next(self.pps()))
1400 def pps(self, decode_path=()):
1402 asn1_type_name=self.asn1_type_name,
1403 obj_name=self.__class__.__name__,
1404 decode_path=decode_path,
1405 value=str(self._value) if self.ready else None,
1406 optional=self.optional,
1407 default=self == self.default,
1408 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1409 expl=None if self._expl is None else tag_decode(self._expl),
1414 expl_offset=self.expl_offset if self.expled else None,
1415 expl_tlen=self.expl_tlen if self.expled else None,
1416 expl_llen=self.expl_llen if self.expled else None,
1417 expl_vlen=self.expl_vlen if self.expled else None,
1422 """``INTEGER`` integer type
1424 >>> b = Integer(-123)
1426 >>> b == Integer(-123)
1431 >>> Integer(2, bounds=(1, 3))
1433 >>> Integer(5, bounds=(1, 3))
1434 Traceback (most recent call last):
1435 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1439 class Version(Integer):
1446 >>> v = Version("v1")
1453 {'v3': 2, 'v1': 0, 'v2': 1}
1455 __slots__ = ("specs", "_bound_min", "_bound_max")
1456 tag_default = tag_encode(2)
1457 asn1_type_name = "INTEGER"
1471 :param value: set the value. Either integer type, named value
1472 (if ``schema`` is specified in the class), or
1473 :py:class:`pyderasn.Integer` object
1474 :param bounds: set ``(MIN, MAX)`` value constraint.
1475 (-inf, +inf) by default
1476 :param bytes impl: override default tag with ``IMPLICIT`` one
1477 :param bytes expl: override default tag with ``EXPLICIT`` one
1478 :param default: set default value. Type same as in ``value``
1479 :param bool optional: is object ``OPTIONAL`` in sequence
1481 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1483 specs = getattr(self, "schema", {}) if _specs is None else _specs
1484 self.specs = specs if isinstance(specs, dict) else dict(specs)
1485 self._bound_min, self._bound_max = getattr(
1488 (float("-inf"), float("+inf")),
1489 ) if bounds is None else bounds
1490 if value is not None:
1491 self._value = self._value_sanitize(value)
1492 if default is not None:
1493 default = self._value_sanitize(default)
1494 self.default = self.__class__(
1500 if self._value is None:
1501 self._value = default
1503 def _value_sanitize(self, value):
1504 if issubclass(value.__class__, Integer):
1505 value = value._value
1506 elif isinstance(value, integer_types):
1508 elif isinstance(value, str):
1509 value = self.specs.get(value)
1511 raise ObjUnknown("integer value: %s" % value)
1513 raise InvalidValueType((self.__class__, int, str))
1514 if not self._bound_min <= value <= self._bound_max:
1515 raise BoundsError(self._bound_min, value, self._bound_max)
1520 return self._value is not None
1523 obj = self.__class__(_specs=self.specs)
1524 obj._value = self._value
1525 obj._bound_min = self._bound_min
1526 obj._bound_max = self._bound_max
1528 obj._expl = self._expl
1529 obj.default = self.default
1530 obj.optional = self.optional
1531 obj.offset = self.offset
1532 obj.llen = self.llen
1533 obj.vlen = self.vlen
1537 self._assert_ready()
1538 return int(self._value)
1541 self._assert_ready()
1544 bytes(self._expl or b"") +
1545 str(self._value).encode("ascii"),
1548 def __eq__(self, their):
1549 if isinstance(their, integer_types):
1550 return self._value == their
1551 if not issubclass(their.__class__, Integer):
1554 self._value == their._value and
1555 self.tag == their.tag and
1556 self._expl == their._expl
1559 def __lt__(self, their):
1560 return self._value < their._value
1564 for name, value in self.specs.items():
1565 if value == self._value:
1577 return self.__class__(
1580 (self._bound_min, self._bound_max)
1581 if bounds is None else bounds
1583 impl=self.tag if impl is None else impl,
1584 expl=self._expl if expl is None else expl,
1585 default=self.default if default is None else default,
1586 optional=self.optional if optional is None else optional,
1591 self._assert_ready()
1595 octets = bytearray([0])
1599 octets = bytearray()
1601 octets.append((value & 0xFF) ^ 0xFF)
1603 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1606 octets = bytearray()
1608 octets.append(value & 0xFF)
1610 if octets[-1] & 0x80 > 0:
1613 octets = bytes(octets)
1615 bytes_len = ceil(value.bit_length() / 8) or 1
1618 octets = value.to_bytes(
1623 except OverflowError:
1627 return b"".join((self.tag, len_encode(len(octets)), octets))
1629 def _decode(self, tlv, offset, decode_path, ctx):
1631 t, _, lv = tag_strip(tlv)
1632 except DecodeError as err:
1633 raise err.__class__(
1635 klass=self.__class__,
1636 decode_path=decode_path,
1641 klass=self.__class__,
1642 decode_path=decode_path,
1646 l, llen, v = len_decode(lv)
1647 except DecodeError as err:
1648 raise err.__class__(
1650 klass=self.__class__,
1651 decode_path=decode_path,
1655 raise NotEnoughData(
1656 "encoded length is longer than data",
1657 klass=self.__class__,
1658 decode_path=decode_path,
1662 raise NotEnoughData(
1664 klass=self.__class__,
1665 decode_path=decode_path,
1668 v, tail = v[:l], v[l:]
1669 first_octet = byte2int(v)
1671 second_octet = byte2int(v[1:])
1673 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1674 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1677 "non normalized integer",
1678 klass=self.__class__,
1679 decode_path=decode_path,
1684 if first_octet & 0x80 > 0:
1685 octets = bytearray()
1686 for octet in bytearray(v):
1687 octets.append(octet ^ 0xFF)
1688 for octet in octets:
1689 value = (value << 8) | octet
1693 for octet in bytearray(v):
1694 value = (value << 8) | octet
1696 value = int.from_bytes(v, byteorder="big", signed=True)
1698 obj = self.__class__(
1700 bounds=(self._bound_min, self._bound_max),
1703 default=self.default,
1704 optional=self.optional,
1706 _decoded=(offset, llen, l),
1708 except BoundsError as err:
1711 klass=self.__class__,
1712 decode_path=decode_path,
1718 return pp_console_row(next(self.pps()))
1720 def pps(self, decode_path=()):
1722 asn1_type_name=self.asn1_type_name,
1723 obj_name=self.__class__.__name__,
1724 decode_path=decode_path,
1725 value=(self.named or str(self._value)) if self.ready else None,
1726 optional=self.optional,
1727 default=self == self.default,
1728 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1729 expl=None if self._expl is None else tag_decode(self._expl),
1734 expl_offset=self.expl_offset if self.expled else None,
1735 expl_tlen=self.expl_tlen if self.expled else None,
1736 expl_llen=self.expl_llen if self.expled else None,
1737 expl_vlen=self.expl_vlen if self.expled else None,
1741 class BitString(Obj):
1742 """``BIT STRING`` bit string type
1744 >>> BitString(b"hello world")
1745 BIT STRING 88 bits 68656c6c6f20776f726c64
1748 >>> b == b"hello world"
1753 >>> b = BitString("'010110000000'B")
1754 BIT STRING 12 bits 5800
1757 >>> b[0], b[1], b[2], b[3]
1758 (False, True, False, True)
1762 [False, True, False, True, True, False, False, False, False, False, False, False]
1766 class KeyUsage(BitString):
1768 ('digitalSignature', 0),
1769 ('nonRepudiation', 1),
1770 ('keyEncipherment', 2),
1773 >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
1774 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1776 ['nonRepudiation', 'keyEncipherment']
1778 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1780 __slots__ = ("specs", "defined")
1781 tag_default = tag_encode(3)
1782 asn1_type_name = "BIT STRING"
1795 :param value: set the value. Either binary type, tuple of named
1796 values (if ``schema`` is specified in the class),
1797 string in ``'XXX...'B`` form, or
1798 :py:class:`pyderasn.BitString` object
1799 :param bytes impl: override default tag with ``IMPLICIT`` one
1800 :param bytes expl: override default tag with ``EXPLICIT`` one
1801 :param default: set default value. Type same as in ``value``
1802 :param bool optional: is object ``OPTIONAL`` in sequence
1804 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1805 specs = getattr(self, "schema", {}) if _specs is None else _specs
1806 self.specs = specs if isinstance(specs, dict) else dict(specs)
1807 self._value = None if value is None else self._value_sanitize(value)
1808 if default is not None:
1809 default = self._value_sanitize(default)
1810 self.default = self.__class__(
1816 self._value = default
1819 def _bits2octets(self, bits):
1820 if len(self.specs) > 0:
1821 bits = bits.rstrip("0")
1823 bits += "0" * ((8 - (bit_len % 8)) % 8)
1824 octets = bytearray(len(bits) // 8)
1825 for i in six_xrange(len(octets)):
1826 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1827 return bit_len, bytes(octets)
1829 def _value_sanitize(self, value):
1830 if issubclass(value.__class__, BitString):
1832 if isinstance(value, (string_types, binary_type)):
1834 isinstance(value, string_types) and
1835 value.startswith("'") and
1836 value.endswith("'B")
1839 if not set(value) <= set(("0", "1")):
1840 raise ValueError("B's coding contains unacceptable chars")
1841 return self._bits2octets(value)
1842 elif isinstance(value, binary_type):
1843 return (len(value) * 8, value)
1845 raise InvalidValueType((
1850 if isinstance(value, tuple):
1853 isinstance(value[0], integer_types) and
1854 isinstance(value[1], binary_type)
1859 bit = self.specs.get(name)
1861 raise ObjUnknown("BitString value: %s" % name)
1864 return self._bits2octets("")
1866 return self._bits2octets("".join(
1867 ("1" if bit in bits else "0")
1868 for bit in six_xrange(max(bits) + 1)
1870 raise InvalidValueType((self.__class__, binary_type, string_types))
1874 return self._value is not None
1877 obj = self.__class__(_specs=self.specs)
1879 if value is not None:
1880 value = (value[0], value[1])
1883 obj._expl = self._expl
1884 obj.default = self.default
1885 obj.optional = self.optional
1886 obj.offset = self.offset
1887 obj.llen = self.llen
1888 obj.vlen = self.vlen
1892 self._assert_ready()
1893 for i in six_xrange(self._value[0]):
1898 self._assert_ready()
1899 return self._value[0]
1901 def __bytes__(self):
1902 self._assert_ready()
1903 return self._value[1]
1905 def __eq__(self, their):
1906 if isinstance(their, bytes):
1907 return self._value[1] == their
1908 if not issubclass(their.__class__, BitString):
1911 self._value == their._value and
1912 self.tag == their.tag and
1913 self._expl == their._expl
1918 return [name for name, bit in self.specs.items() if self[bit]]
1928 return self.__class__(
1930 impl=self.tag if impl is None else impl,
1931 expl=self._expl if expl is None else expl,
1932 default=self.default if default is None else default,
1933 optional=self.optional if optional is None else optional,
1937 def __getitem__(self, key):
1938 if isinstance(key, int):
1939 bit_len, octets = self._value
1943 byte2int(memoryview(octets)[key // 8:]) >>
1946 if isinstance(key, string_types):
1947 value = self.specs.get(key)
1949 raise ObjUnknown("BitString value: %s" % key)
1951 raise InvalidValueType((int, str))
1954 self._assert_ready()
1955 bit_len, octets = self._value
1958 len_encode(len(octets) + 1),
1959 int2byte((8 - bit_len % 8) % 8),
1963 def _decode(self, tlv, offset, decode_path, ctx):
1965 t, _, lv = tag_strip(tlv)
1966 except DecodeError as err:
1967 raise err.__class__(
1969 klass=self.__class__,
1970 decode_path=decode_path,
1975 klass=self.__class__,
1976 decode_path=decode_path,
1980 l, llen, v = len_decode(lv)
1981 except DecodeError as err:
1982 raise err.__class__(
1984 klass=self.__class__,
1985 decode_path=decode_path,
1989 raise NotEnoughData(
1990 "encoded length is longer than data",
1991 klass=self.__class__,
1992 decode_path=decode_path,
1996 raise NotEnoughData(
1998 klass=self.__class__,
1999 decode_path=decode_path,
2002 pad_size = byte2int(v)
2003 if l == 1 and pad_size != 0:
2005 "invalid empty value",
2006 klass=self.__class__,
2007 decode_path=decode_path,
2013 klass=self.__class__,
2014 decode_path=decode_path,
2017 if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
2020 klass=self.__class__,
2021 decode_path=decode_path,
2024 v, tail = v[:l], v[l:]
2025 obj = self.__class__(
2026 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2029 default=self.default,
2030 optional=self.optional,
2032 _decoded=(offset, llen, l),
2037 return pp_console_row(next(self.pps()))
2039 def pps(self, decode_path=()):
2043 bit_len, blob = self._value
2044 value = "%d bits" % bit_len
2045 if len(self.specs) > 0:
2046 blob = tuple(self.named)
2048 asn1_type_name=self.asn1_type_name,
2049 obj_name=self.__class__.__name__,
2050 decode_path=decode_path,
2053 optional=self.optional,
2054 default=self == self.default,
2055 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2056 expl=None if self._expl is None else tag_decode(self._expl),
2061 expl_offset=self.expl_offset if self.expled else None,
2062 expl_tlen=self.expl_tlen if self.expled else None,
2063 expl_llen=self.expl_llen if self.expled else None,
2064 expl_vlen=self.expl_vlen if self.expled else None,
2066 defined_by, defined = self.defined or (None, None)
2067 if defined_by is not None:
2069 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2073 class OctetString(Obj):
2074 """``OCTET STRING`` binary string type
2076 >>> s = OctetString(b"hello world")
2077 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2078 >>> s == OctetString(b"hello world")
2083 >>> OctetString(b"hello", bounds=(4, 4))
2084 Traceback (most recent call last):
2085 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2086 >>> OctetString(b"hell", bounds=(4, 4))
2087 OCTET STRING 4 bytes 68656c6c
2089 __slots__ = ("_bound_min", "_bound_max", "defined")
2090 tag_default = tag_encode(4)
2091 asn1_type_name = "OCTET STRING"
2104 :param value: set the value. Either binary type, or
2105 :py:class:`pyderasn.OctetString` object
2106 :param bounds: set ``(MIN, MAX)`` value size constraint.
2107 (-inf, +inf) by default
2108 :param bytes impl: override default tag with ``IMPLICIT`` one
2109 :param bytes expl: override default tag with ``EXPLICIT`` one
2110 :param default: set default value. Type same as in ``value``
2111 :param bool optional: is object ``OPTIONAL`` in sequence
2113 super(OctetString, self).__init__(
2121 self._bound_min, self._bound_max = getattr(
2125 ) if bounds is None else bounds
2126 if value is not None:
2127 self._value = self._value_sanitize(value)
2128 if default is not None:
2129 default = self._value_sanitize(default)
2130 self.default = self.__class__(
2135 if self._value is None:
2136 self._value = default
2139 def _value_sanitize(self, value):
2140 if issubclass(value.__class__, OctetString):
2141 value = value._value
2142 elif isinstance(value, binary_type):
2145 raise InvalidValueType((self.__class__, bytes))
2146 if not self._bound_min <= len(value) <= self._bound_max:
2147 raise BoundsError(self._bound_min, len(value), self._bound_max)
2152 return self._value is not None
2155 obj = self.__class__()
2156 obj._value = self._value
2157 obj._bound_min = self._bound_min
2158 obj._bound_max = self._bound_max
2160 obj._expl = self._expl
2161 obj.default = self.default
2162 obj.optional = self.optional
2163 obj.offset = self.offset
2164 obj.llen = self.llen
2165 obj.vlen = self.vlen
2168 def __bytes__(self):
2169 self._assert_ready()
2172 def __eq__(self, their):
2173 if isinstance(their, binary_type):
2174 return self._value == their
2175 if not issubclass(their.__class__, OctetString):
2178 self._value == their._value and
2179 self.tag == their.tag and
2180 self._expl == their._expl
2183 def __lt__(self, their):
2184 return self._value < their._value
2195 return self.__class__(
2198 (self._bound_min, self._bound_max)
2199 if bounds is None else bounds
2201 impl=self.tag if impl is None else impl,
2202 expl=self._expl if expl is None else expl,
2203 default=self.default if default is None else default,
2204 optional=self.optional if optional is None else optional,
2208 self._assert_ready()
2211 len_encode(len(self._value)),
2215 def _decode(self, tlv, offset, decode_path, ctx):
2217 t, _, lv = tag_strip(tlv)
2218 except DecodeError as err:
2219 raise err.__class__(
2221 klass=self.__class__,
2222 decode_path=decode_path,
2227 klass=self.__class__,
2228 decode_path=decode_path,
2232 l, llen, v = len_decode(lv)
2233 except DecodeError as err:
2234 raise err.__class__(
2236 klass=self.__class__,
2237 decode_path=decode_path,
2241 raise NotEnoughData(
2242 "encoded length is longer than data",
2243 klass=self.__class__,
2244 decode_path=decode_path,
2247 v, tail = v[:l], v[l:]
2249 obj = self.__class__(
2251 bounds=(self._bound_min, self._bound_max),
2254 default=self.default,
2255 optional=self.optional,
2256 _decoded=(offset, llen, l),
2258 except BoundsError as err:
2261 klass=self.__class__,
2262 decode_path=decode_path,
2268 return pp_console_row(next(self.pps()))
2270 def pps(self, decode_path=()):
2272 asn1_type_name=self.asn1_type_name,
2273 obj_name=self.__class__.__name__,
2274 decode_path=decode_path,
2275 value=("%d bytes" % len(self._value)) if self.ready else None,
2276 blob=self._value if self.ready else None,
2277 optional=self.optional,
2278 default=self == self.default,
2279 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2280 expl=None if self._expl is None else tag_decode(self._expl),
2285 expl_offset=self.expl_offset if self.expled else None,
2286 expl_tlen=self.expl_tlen if self.expled else None,
2287 expl_llen=self.expl_llen if self.expled else None,
2288 expl_vlen=self.expl_vlen if self.expled else None,
2290 defined_by, defined = self.defined or (None, None)
2291 if defined_by is not None:
2293 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2298 """``NULL`` null object
2306 tag_default = tag_encode(5)
2307 asn1_type_name = "NULL"
2311 value=None, # unused, but Sequence passes it
2318 :param bytes impl: override default tag with ``IMPLICIT`` one
2319 :param bytes expl: override default tag with ``EXPLICIT`` one
2320 :param bool optional: is object ``OPTIONAL`` in sequence
2322 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2330 obj = self.__class__()
2332 obj._expl = self._expl
2333 obj.default = self.default
2334 obj.optional = self.optional
2335 obj.offset = self.offset
2336 obj.llen = self.llen
2337 obj.vlen = self.vlen
2340 def __eq__(self, their):
2341 if not issubclass(their.__class__, Null):
2344 self.tag == their.tag and
2345 self._expl == their._expl
2355 return self.__class__(
2356 impl=self.tag if impl is None else impl,
2357 expl=self._expl if expl is None else expl,
2358 optional=self.optional if optional is None else optional,
2362 return self.tag + len_encode(0)
2364 def _decode(self, tlv, offset, decode_path, ctx):
2366 t, _, lv = tag_strip(tlv)
2367 except DecodeError as err:
2368 raise err.__class__(
2370 klass=self.__class__,
2371 decode_path=decode_path,
2376 klass=self.__class__,
2377 decode_path=decode_path,
2381 l, _, v = len_decode(lv)
2382 except DecodeError as err:
2383 raise err.__class__(
2385 klass=self.__class__,
2386 decode_path=decode_path,
2390 raise InvalidLength(
2391 "Null must have zero length",
2392 klass=self.__class__,
2393 decode_path=decode_path,
2396 obj = self.__class__(
2399 optional=self.optional,
2400 _decoded=(offset, 1, 0),
2405 return pp_console_row(next(self.pps()))
2407 def pps(self, decode_path=()):
2409 asn1_type_name=self.asn1_type_name,
2410 obj_name=self.__class__.__name__,
2411 decode_path=decode_path,
2412 optional=self.optional,
2413 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2414 expl=None if self._expl is None else tag_decode(self._expl),
2419 expl_offset=self.expl_offset if self.expled else None,
2420 expl_tlen=self.expl_tlen if self.expled else None,
2421 expl_llen=self.expl_llen if self.expled else None,
2422 expl_vlen=self.expl_vlen if self.expled else None,
2426 class ObjectIdentifier(Obj):
2427 """``OBJECT IDENTIFIER`` OID type
2429 >>> oid = ObjectIdentifier((1, 2, 3))
2430 OBJECT IDENTIFIER 1.2.3
2431 >>> oid == ObjectIdentifier("1.2.3")
2437 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2438 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2440 >>> str(ObjectIdentifier((3, 1)))
2441 Traceback (most recent call last):
2442 pyderasn.InvalidOID: unacceptable first arc value
2444 __slots__ = ("defines",)
2445 tag_default = tag_encode(6)
2446 asn1_type_name = "OBJECT IDENTIFIER"
2459 :param value: set the value. Either tuples of integers,
2460 string of "."-concatenated integers, or
2461 :py:class:`pyderasn.ObjectIdentifier` object
2462 :param defines: sequence of tuples. Each tuple has two elements.
2463 First one is relative to current one decode
2464 path, aiming to the field defined by that OID.
2465 Read about relative path in
2466 :py:func:`pyderasn.abs_decode_path`. Second
2467 tuple element is ``{OID: pyderasn.Obj()}``
2468 dictionary, mapping between current OID value
2469 and structure applied to defined field.
2470 :ref:`Read about DEFINED BY <definedby>`
2471 :param bytes impl: override default tag with ``IMPLICIT`` one
2472 :param bytes expl: override default tag with ``EXPLICIT`` one
2473 :param default: set default value. Type same as in ``value``
2474 :param bool optional: is object ``OPTIONAL`` in sequence
2476 super(ObjectIdentifier, self).__init__(
2484 if value is not None:
2485 self._value = self._value_sanitize(value)
2486 if default is not None:
2487 default = self._value_sanitize(default)
2488 self.default = self.__class__(
2493 if self._value is None:
2494 self._value = default
2495 self.defines = defines
2497 def __add__(self, their):
2498 if isinstance(their, self.__class__):
2499 return self.__class__(self._value + their._value)
2500 if isinstance(their, tuple):
2501 return self.__class__(self._value + their)
2502 raise InvalidValueType((self.__class__, tuple))
2504 def _value_sanitize(self, value):
2505 if issubclass(value.__class__, ObjectIdentifier):
2507 if isinstance(value, string_types):
2509 value = tuple(int(arc) for arc in value.split("."))
2511 raise InvalidOID("unacceptable arcs values")
2512 if isinstance(value, tuple):
2514 raise InvalidOID("less than 2 arcs")
2515 first_arc = value[0]
2516 if first_arc in (0, 1):
2517 if not (0 <= value[1] <= 39):
2518 raise InvalidOID("second arc is too wide")
2519 elif first_arc == 2:
2522 raise InvalidOID("unacceptable first arc value")
2524 raise InvalidValueType((self.__class__, str, tuple))
2528 return self._value is not None
2531 obj = self.__class__()
2532 obj._value = self._value
2533 obj.defines = self.defines
2535 obj._expl = self._expl
2536 obj.default = self.default
2537 obj.optional = self.optional
2538 obj.offset = self.offset
2539 obj.llen = self.llen
2540 obj.vlen = self.vlen
2544 self._assert_ready()
2545 return iter(self._value)
2548 return ".".join(str(arc) for arc in self._value or ())
2551 self._assert_ready()
2554 bytes(self._expl or b"") +
2555 str(self._value).encode("ascii"),
2558 def __eq__(self, their):
2559 if isinstance(their, tuple):
2560 return self._value == their
2561 if not issubclass(their.__class__, ObjectIdentifier):
2564 self.tag == their.tag and
2565 self._expl == their._expl and
2566 self._value == their._value
2569 def __lt__(self, their):
2570 return self._value < their._value
2581 return self.__class__(
2583 defines=self.defines if defines is None else defines,
2584 impl=self.tag if impl is None else impl,
2585 expl=self._expl if expl is None else expl,
2586 default=self.default if default is None else default,
2587 optional=self.optional if optional is None else optional,
2591 self._assert_ready()
2593 first_value = value[1]
2594 first_arc = value[0]
2597 elif first_arc == 1:
2599 elif first_arc == 2:
2601 else: # pragma: no cover
2602 raise RuntimeError("invalid arc is stored")
2603 octets = [zero_ended_encode(first_value)]
2604 for arc in value[2:]:
2605 octets.append(zero_ended_encode(arc))
2606 v = b"".join(octets)
2607 return b"".join((self.tag, len_encode(len(v)), v))
2609 def _decode(self, tlv, offset, decode_path, ctx):
2611 t, _, lv = tag_strip(tlv)
2612 except DecodeError as err:
2613 raise err.__class__(
2615 klass=self.__class__,
2616 decode_path=decode_path,
2621 klass=self.__class__,
2622 decode_path=decode_path,
2626 l, llen, v = len_decode(lv)
2627 except DecodeError as err:
2628 raise err.__class__(
2630 klass=self.__class__,
2631 decode_path=decode_path,
2635 raise NotEnoughData(
2636 "encoded length is longer than data",
2637 klass=self.__class__,
2638 decode_path=decode_path,
2642 raise NotEnoughData(
2644 klass=self.__class__,
2645 decode_path=decode_path,
2648 v, tail = v[:l], v[l:]
2654 octet = indexbytes(v, i)
2655 arc = (arc << 7) | (octet & 0x7F)
2656 if octet & 0x80 == 0:
2664 klass=self.__class__,
2665 decode_path=decode_path,
2669 second_arc = arcs[0]
2670 if 0 <= second_arc <= 39:
2672 elif 40 <= second_arc <= 79:
2678 obj = self.__class__(
2679 value=tuple([first_arc, second_arc] + arcs[1:]),
2682 default=self.default,
2683 optional=self.optional,
2684 _decoded=(offset, llen, l),
2689 return pp_console_row(next(self.pps()))
2691 def pps(self, decode_path=()):
2693 asn1_type_name=self.asn1_type_name,
2694 obj_name=self.__class__.__name__,
2695 decode_path=decode_path,
2696 value=str(self) if self.ready else None,
2697 optional=self.optional,
2698 default=self == self.default,
2699 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2700 expl=None if self._expl is None else tag_decode(self._expl),
2705 expl_offset=self.expl_offset if self.expled else None,
2706 expl_tlen=self.expl_tlen if self.expled else None,
2707 expl_llen=self.expl_llen if self.expled else None,
2708 expl_vlen=self.expl_vlen if self.expled else None,
2712 class Enumerated(Integer):
2713 """``ENUMERATED`` integer type
2715 This type is identical to :py:class:`pyderasn.Integer`, but requires
2716 schema to be specified and does not accept values missing from it.
2719 tag_default = tag_encode(10)
2720 asn1_type_name = "ENUMERATED"
2731 bounds=None, # dummy argument, workability for Integer.decode
2733 super(Enumerated, self).__init__(
2742 if len(self.specs) == 0:
2743 raise ValueError("schema must be specified")
2745 def _value_sanitize(self, value):
2746 if isinstance(value, self.__class__):
2747 value = value._value
2748 elif isinstance(value, integer_types):
2749 if value not in list(self.specs.values()):
2751 "unknown integer value: %s" % value,
2752 klass=self.__class__,
2754 elif isinstance(value, string_types):
2755 value = self.specs.get(value)
2757 raise ObjUnknown("integer value: %s" % value)
2759 raise InvalidValueType((self.__class__, int, str))
2763 obj = self.__class__(_specs=self.specs)
2764 obj._value = self._value
2765 obj._bound_min = self._bound_min
2766 obj._bound_max = self._bound_max
2768 obj._expl = self._expl
2769 obj.default = self.default
2770 obj.optional = self.optional
2771 obj.offset = self.offset
2772 obj.llen = self.llen
2773 obj.vlen = self.vlen
2785 return self.__class__(
2787 impl=self.tag if impl is None else impl,
2788 expl=self._expl if expl is None else expl,
2789 default=self.default if default is None else default,
2790 optional=self.optional if optional is None else optional,
2795 class CommonString(OctetString):
2796 """Common class for all strings
2798 Everything resembles :py:class:`pyderasn.OctetString`, except
2799 ability to deal with unicode text strings.
2801 >>> hexenc("привет мир".encode("utf-8"))
2802 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2803 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2805 >>> s = UTF8String("привет мир")
2806 UTF8String UTF8String привет мир
2808 'привет мир'
2809 >>> hexenc(bytes(s))
2810 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2812 >>> PrintableString("привет мир")
2813 Traceback (most recent call last):
2814 UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2816 >>> BMPString("ада", bounds=(2, 2))
2817 Traceback (most recent call last):
2818 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2819 >>> s = BMPString("ад", bounds=(2, 2))
2822 >>> hexenc(bytes(s))
2830 * - :py:class:`pyderasn.UTF8String`
2832 * - :py:class:`pyderasn.NumericString`
2834 * - :py:class:`pyderasn.PrintableString`
2836 * - :py:class:`pyderasn.TeletexString`
2838 * - :py:class:`pyderasn.T61String`
2840 * - :py:class:`pyderasn.VideotexString`
2842 * - :py:class:`pyderasn.IA5String`
2844 * - :py:class:`pyderasn.GraphicString`
2846 * - :py:class:`pyderasn.VisibleString`
2848 * - :py:class:`pyderasn.ISO646String`
2850 * - :py:class:`pyderasn.GeneralString`
2852 * - :py:class:`pyderasn.UniversalString`
2854 * - :py:class:`pyderasn.BMPString`
2857 __slots__ = ("encoding",)
2859 def _value_sanitize(self, value):
2861 value_decoded = None
2862 if isinstance(value, self.__class__):
2863 value_raw = value._value
2864 elif isinstance(value, text_type):
2865 value_decoded = value
2866 elif isinstance(value, binary_type):
2869 raise InvalidValueType((self.__class__, text_type, binary_type))
2871 value_decoded.encode(self.encoding)
2872 if value_raw is None else value_raw
2875 value_raw.decode(self.encoding)
2876 if value_decoded is None else value_decoded
2878 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2886 def __eq__(self, their):
2887 if isinstance(their, binary_type):
2888 return self._value == their
2889 if isinstance(their, text_type):
2890 return self._value == their.encode(self.encoding)
2891 if not isinstance(their, self.__class__):
2894 self._value == their._value and
2895 self.tag == their.tag and
2896 self._expl == their._expl
2899 def __unicode__(self):
2901 return self._value.decode(self.encoding)
2902 return text_type(self._value)
2905 return pp_console_row(next(self.pps(no_unicode=PY2)))
2907 def pps(self, decode_path=(), no_unicode=False):
2910 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2912 asn1_type_name=self.asn1_type_name,
2913 obj_name=self.__class__.__name__,
2914 decode_path=decode_path,
2916 optional=self.optional,
2917 default=self == self.default,
2918 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2919 expl=None if self._expl is None else tag_decode(self._expl),
2927 class UTF8String(CommonString):
2929 tag_default = tag_encode(12)
2931 asn1_type_name = "UTF8String"
2934 class NumericString(CommonString):
2936 tag_default = tag_encode(18)
2938 asn1_type_name = "NumericString"
2941 class PrintableString(CommonString):
2943 tag_default = tag_encode(19)
2945 asn1_type_name = "PrintableString"
2948 class TeletexString(CommonString):
2950 tag_default = tag_encode(20)
2952 asn1_type_name = "TeletexString"
2955 class T61String(TeletexString):
2957 asn1_type_name = "T61String"
2960 class VideotexString(CommonString):
2962 tag_default = tag_encode(21)
2963 encoding = "iso-8859-1"
2964 asn1_type_name = "VideotexString"
2967 class IA5String(CommonString):
2969 tag_default = tag_encode(22)
2971 asn1_type_name = "IA5"
2974 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
2975 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
2976 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
2979 class UTCTime(CommonString):
2980 """``UTCTime`` datetime type
2982 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
2983 UTCTime UTCTime 2017-09-30T22:07:50
2989 datetime.datetime(2017, 9, 30, 22, 7, 50)
2990 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
2991 datetime.datetime(1957, 9, 30, 22, 7, 50)
2994 tag_default = tag_encode(23)
2996 asn1_type_name = "UTCTime"
2998 fmt = "%y%m%d%H%M%SZ"
3008 bounds=None, # dummy argument, workability for OctetString.decode
3011 :param value: set the value. Either datetime type, or
3012 :py:class:`pyderasn.UTCTime` object
3013 :param bytes impl: override default tag with ``IMPLICIT`` one
3014 :param bytes expl: override default tag with ``EXPLICIT`` one
3015 :param default: set default value. Type same as in ``value``
3016 :param bool optional: is object ``OPTIONAL`` in sequence
3018 super(UTCTime, self).__init__(
3026 if value is not None:
3027 self._value = self._value_sanitize(value)
3028 if default is not None:
3029 default = self._value_sanitize(default)
3030 self.default = self.__class__(
3035 if self._value is None:
3036 self._value = default
3038 def _value_sanitize(self, value):
3039 if isinstance(value, self.__class__):
3041 if isinstance(value, datetime):
3042 return value.strftime(self.fmt).encode("ascii")
3043 if isinstance(value, binary_type):
3044 value_decoded = value.decode("ascii")
3045 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3047 datetime.strptime(value_decoded, self.fmt)
3049 raise DecodeError("invalid UTCTime format")
3052 raise DecodeError("invalid UTCTime length")
3053 raise InvalidValueType((self.__class__, datetime))
3055 def __eq__(self, their):
3056 if isinstance(their, binary_type):
3057 return self._value == their
3058 if isinstance(their, datetime):
3059 return self.todatetime() == their
3060 if not isinstance(their, self.__class__):
3063 self._value == their._value and
3064 self.tag == their.tag and
3065 self._expl == their._expl
3068 def todatetime(self):
3069 """Convert to datetime
3073 Pay attention that UTCTime can not hold full year, so all years
3074 having < 50 years are treated as 20xx, 19xx otherwise, according
3075 to X.509 recomendation.
3077 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3078 year = value.year % 100
3080 year=(2000 + year) if year < 50 else (1900 + year),
3084 minute=value.minute,
3085 second=value.second,
3089 return pp_console_row(next(self.pps()))
3091 def pps(self, decode_path=()):
3093 asn1_type_name=self.asn1_type_name,
3094 obj_name=self.__class__.__name__,
3095 decode_path=decode_path,
3096 value=self.todatetime().isoformat() if self.ready else None,
3097 optional=self.optional,
3098 default=self == self.default,
3099 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3100 expl=None if self._expl is None else tag_decode(self._expl),
3108 class GeneralizedTime(UTCTime):
3109 """``GeneralizedTime`` datetime type
3111 This type is similar to :py:class:`pyderasn.UTCTime`.
3113 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3114 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3116 '20170930220750.000123Z'
3117 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3118 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3121 tag_default = tag_encode(24)
3122 asn1_type_name = "GeneralizedTime"
3124 fmt = "%Y%m%d%H%M%SZ"
3125 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3127 def _value_sanitize(self, value):
3128 if isinstance(value, self.__class__):
3130 if isinstance(value, datetime):
3131 return value.strftime(
3132 self.fmt_ms if value.microsecond > 0 else self.fmt
3134 if isinstance(value, binary_type):
3135 value_decoded = value.decode("ascii")
3136 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3138 datetime.strptime(value_decoded, self.fmt)
3141 "invalid GeneralizedTime (without ms) format",
3144 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3146 datetime.strptime(value_decoded, self.fmt_ms)
3149 "invalid GeneralizedTime (with ms) format",
3154 "invalid GeneralizedTime length",
3155 klass=self.__class__,
3157 raise InvalidValueType((self.__class__, datetime))
3159 def todatetime(self):
3160 value = self._value.decode("ascii")
3161 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3162 return datetime.strptime(value, self.fmt)
3163 return datetime.strptime(value, self.fmt_ms)
3166 class GraphicString(CommonString):
3168 tag_default = tag_encode(25)
3169 encoding = "iso-8859-1"
3170 asn1_type_name = "GraphicString"
3173 class VisibleString(CommonString):
3175 tag_default = tag_encode(26)
3177 asn1_type_name = "VisibleString"
3180 class ISO646String(VisibleString):
3182 asn1_type_name = "ISO646String"
3185 class GeneralString(CommonString):
3187 tag_default = tag_encode(27)
3188 encoding = "iso-8859-1"
3189 asn1_type_name = "GeneralString"
3192 class UniversalString(CommonString):
3194 tag_default = tag_encode(28)
3195 encoding = "utf-32-be"
3196 asn1_type_name = "UniversalString"
3199 class BMPString(CommonString):
3201 tag_default = tag_encode(30)
3202 encoding = "utf-16-be"
3203 asn1_type_name = "BMPString"
3207 """``CHOICE`` special type
3211 class GeneralName(Choice):
3213 ('rfc822Name', IA5String(impl=tag_ctxp(1))),
3214 ('dNSName', IA5String(impl=tag_ctxp(2))),
3217 >>> gn = GeneralName()
3219 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3220 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3221 >>> gn["dNSName"] = IA5String("bar.baz")
3222 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3223 >>> gn["rfc822Name"]
3226 [2] IA5String IA5 bar.baz
3229 >>> gn.value == gn["dNSName"]
3232 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3234 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3235 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3237 __slots__ = ("specs",)
3239 asn1_type_name = "CHOICE"
3252 :param value: set the value. Either ``(choice, value)`` tuple, or
3253 :py:class:`pyderasn.Choice` object
3254 :param bytes impl: can not be set, do **not** use it
3255 :param bytes expl: override default tag with ``EXPLICIT`` one
3256 :param default: set default value. Type same as in ``value``
3257 :param bool optional: is object ``OPTIONAL`` in sequence
3259 if impl is not None:
3260 raise ValueError("no implicit tag allowed for CHOICE")
3261 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3263 schema = getattr(self, "schema", ())
3264 if len(schema) == 0:
3265 raise ValueError("schema must be specified")
3267 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3270 if value is not None:
3271 self._value = self._value_sanitize(value)
3272 if default is not None:
3273 default_value = self._value_sanitize(default)
3274 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3275 default_obj.specs = self.specs
3276 default_obj._value = default_value
3277 self.default = default_obj
3279 self._value = default_obj.copy()._value
3281 def _value_sanitize(self, value):
3282 if isinstance(value, self.__class__):
3284 if isinstance(value, tuple) and len(value) == 2:
3286 spec = self.specs.get(choice)
3288 raise ObjUnknown(choice)
3289 if not isinstance(obj, spec.__class__):
3290 raise InvalidValueType((spec,))
3291 return (choice, spec(obj))
3292 raise InvalidValueType((self.__class__, tuple))
3296 return self._value is not None and self._value[1].ready
3299 obj = self.__class__(schema=self.specs)
3300 obj._expl = self._expl
3301 obj.default = self.default
3302 obj.optional = self.optional
3303 obj.offset = self.offset
3304 obj.llen = self.llen
3305 obj.vlen = self.vlen
3307 if value is not None:
3308 obj._value = (value[0], value[1].copy())
3311 def __eq__(self, their):
3312 if isinstance(their, tuple) and len(their) == 2:
3313 return self._value == their
3314 if not isinstance(their, self.__class__):
3317 self.specs == their.specs and
3318 self._value == their._value
3328 return self.__class__(
3331 expl=self._expl if expl is None else expl,
3332 default=self.default if default is None else default,
3333 optional=self.optional if optional is None else optional,
3338 self._assert_ready()
3339 return self._value[0]
3343 self._assert_ready()
3344 return self._value[1]
3346 def __getitem__(self, key):
3347 if key not in self.specs:
3348 raise ObjUnknown(key)
3349 if self._value is None:
3351 choice, value = self._value
3356 def __setitem__(self, key, value):
3357 spec = self.specs.get(key)
3359 raise ObjUnknown(key)
3360 if not isinstance(value, spec.__class__):
3361 raise InvalidValueType((spec.__class__,))
3362 self._value = (key, spec(value))
3370 return self._value[1].decoded if self.ready else False
3373 self._assert_ready()
3374 return self._value[1].encode()
3376 def _decode(self, tlv, offset, decode_path, ctx):
3377 for choice, spec in self.specs.items():
3379 value, tail = spec.decode(
3383 decode_path=decode_path + (choice,),
3388 obj = self.__class__(
3391 default=self.default,
3392 optional=self.optional,
3393 _decoded=(offset, 0, value.tlvlen),
3395 obj._value = (choice, value)
3398 klass=self.__class__,
3399 decode_path=decode_path,
3404 value = pp_console_row(next(self.pps()))
3406 value = "%s[%r]" % (value, self.value)
3409 def pps(self, decode_path=()):
3411 asn1_type_name=self.asn1_type_name,
3412 obj_name=self.__class__.__name__,
3413 decode_path=decode_path,
3414 value=self.choice if self.ready else None,
3415 optional=self.optional,
3416 default=self == self.default,
3417 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3418 expl=None if self._expl is None else tag_decode(self._expl),
3425 yield self.value.pps(decode_path=decode_path + (self.choice,))
3428 class PrimitiveTypes(Choice):
3429 """Predefined ``CHOICE`` for all generic primitive types
3431 It could be useful for general decoding of some unspecified values:
3433 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3434 OCTET STRING 3 bytes 666f6f
3435 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3439 schema = tuple((klass.__name__, klass()) for klass in (
3464 """``ANY`` special type
3466 >>> Any(Integer(-123))
3468 >>> a = Any(OctetString(b"hello world").encode())
3469 ANY 040b68656c6c6f20776f726c64
3470 >>> hexenc(bytes(a))
3471 b'0x040x0bhello world'
3473 __slots__ = ("defined",)
3474 tag_default = tag_encode(0)
3475 asn1_type_name = "ANY"
3485 :param value: set the value. Either any kind of pyderasn's
3486 **ready** object, or bytes. Pay attention that
3487 **no** validation is performed is raw binary value
3489 :param bytes expl: override default tag with ``EXPLICIT`` one
3490 :param bool optional: is object ``OPTIONAL`` in sequence
3492 super(Any, self).__init__(None, expl, None, optional, _decoded)
3493 self._value = None if value is None else self._value_sanitize(value)
3496 def _value_sanitize(self, value):
3497 if isinstance(value, self.__class__):
3499 if isinstance(value, Obj):
3500 return value.encode()
3501 if isinstance(value, binary_type):
3503 raise InvalidValueType((self.__class__, Obj, binary_type))
3507 return self._value is not None
3510 obj = self.__class__()
3511 obj._value = self._value
3513 obj._expl = self._expl
3514 obj.optional = self.optional
3515 obj.offset = self.offset
3516 obj.llen = self.llen
3517 obj.vlen = self.vlen
3520 def __eq__(self, their):
3521 if isinstance(their, binary_type):
3522 return self._value == their
3523 if issubclass(their.__class__, Any):
3524 return self._value == their._value
3533 return self.__class__(
3535 expl=self._expl if expl is None else expl,
3536 optional=self.optional if optional is None else optional,
3539 def __bytes__(self):
3540 self._assert_ready()
3548 self._assert_ready()
3551 def _decode(self, tlv, offset, decode_path, ctx):
3553 t, tlen, lv = tag_strip(tlv)
3554 l, llen, v = len_decode(lv)
3555 except DecodeError as err:
3556 raise err.__class__(
3558 klass=self.__class__,
3559 decode_path=decode_path,
3563 raise NotEnoughData(
3564 "encoded length is longer than data",
3565 klass=self.__class__,
3566 decode_path=decode_path,
3569 tlvlen = tlen + llen + l
3570 v, tail = tlv[:tlvlen], v[l:]
3571 obj = self.__class__(
3574 optional=self.optional,
3575 _decoded=(offset, 0, tlvlen),
3581 return pp_console_row(next(self.pps()))
3583 def pps(self, decode_path=()):
3585 asn1_type_name=self.asn1_type_name,
3586 obj_name=self.__class__.__name__,
3587 decode_path=decode_path,
3588 blob=self._value if self.ready else None,
3589 optional=self.optional,
3590 default=self == self.default,
3591 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3592 expl=None if self._expl is None else tag_decode(self._expl),
3597 expl_offset=self.expl_offset if self.expled else None,
3598 expl_tlen=self.expl_tlen if self.expled else None,
3599 expl_llen=self.expl_llen if self.expled else None,
3600 expl_vlen=self.expl_vlen if self.expled else None,
3602 defined_by, defined = self.defined or (None, None)
3603 if defined_by is not None:
3605 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3609 ########################################################################
3610 # ASN.1 constructed types
3611 ########################################################################
3613 def get_def_by_path(defines_by_path, sub_decode_path):
3614 """Get define by decode path
3616 for path, define in defines_by_path:
3617 if len(path) != len(sub_decode_path):
3619 for p1, p2 in zip(path, sub_decode_path):
3620 if (p1 != any) and (p1 != p2):
3626 def abs_decode_path(decode_path, rel_path):
3627 """Create an absolute decode path from current and relative ones
3629 :param decode_path: current decode path, starting point.
3631 :param rel_path: relative path to ``decode_path``. Tuple of strings.
3632 If first tuple's element is "/", then treat it as
3633 an absolute path, ignoring ``decode_path`` as
3634 starting point. Also this tuple can contain ".."
3635 elements, stripping the leading element from
3638 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
3639 ("foo", "bar", "baz", "whatever")
3640 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
3642 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
3645 if rel_path[0] == "/":
3647 if rel_path[0] == "..":
3648 return abs_decode_path(decode_path[:-1], rel_path[1:])
3649 return decode_path + rel_path
3652 class Sequence(Obj):
3653 """``SEQUENCE`` structure type
3655 You have to make specification of sequence::
3657 class Extension(Sequence):
3659 ("extnID", ObjectIdentifier()),
3660 ("critical", Boolean(default=False)),
3661 ("extnValue", OctetString()),
3664 Then, you can work with it as with dictionary.
3666 >>> ext = Extension()
3667 >>> Extension().specs
3669 ('extnID', OBJECT IDENTIFIER),
3670 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3671 ('extnValue', OCTET STRING),
3673 >>> ext["extnID"] = "1.2.3"
3674 Traceback (most recent call last):
3675 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3676 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3678 You can know if sequence is ready to be encoded:
3683 Traceback (most recent call last):
3684 pyderasn.ObjNotReady: object is not ready: extnValue
3685 >>> ext["extnValue"] = OctetString(b"foobar")
3689 Value you want to assign, must have the same **type** as in
3690 corresponding specification, but it can have different tags,
3691 optional/default attributes -- they will be taken from specification
3694 class TBSCertificate(Sequence):
3696 ("version", Version(expl=tag_ctxc(0), default="v1")),
3699 >>> tbs = TBSCertificate()
3700 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3702 Assign ``None`` to remove value from sequence.
3704 You can know if value exists/set in the sequence and take its value:
3706 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3709 OBJECT IDENTIFIER 1.2.3
3711 But pay attention that if value has default, then it won't be (not
3712 in) in the sequence (because ``DEFAULT`` must not be encoded in
3713 DER), but you can read its value:
3715 >>> "critical" in ext, ext["critical"]
3716 (False, BOOLEAN False)
3717 >>> ext["critical"] = Boolean(True)
3718 >>> "critical" in ext, ext["critical"]
3719 (True, BOOLEAN True)
3721 All defaulted values are always optional.
3723 .. _strict_default_existence_ctx:
3727 When decoded DER contains defaulted value inside, then
3728 technically this is not valid DER encoding. But we allow and pass
3729 it **by default**. Of course reencoding of that kind of DER will
3730 result in different binary representation (validly without
3731 defaulted value inside). You can enable strict defaulted values
3732 existence validation by setting ``"strict_default_existence":
3733 True`` :ref:`context <ctx>` option -- decoding process will raise
3734 an exception if defaulted value is met.
3736 Two sequences are equal if they have equal specification (schema),
3737 implicit/explicit tagging and the same values.
3739 __slots__ = ("specs",)
3740 tag_default = tag_encode(form=TagFormConstructed, num=16)
3741 asn1_type_name = "SEQUENCE"
3753 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3755 schema = getattr(self, "schema", ())
3757 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3760 if value is not None:
3761 self._value = self._value_sanitize(value)
3762 if default is not None:
3763 default_value = self._value_sanitize(default)
3764 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3765 default_obj.specs = self.specs
3766 default_obj._value = default_value
3767 self.default = default_obj
3769 self._value = default_obj.copy()._value
3771 def _value_sanitize(self, value):
3772 if not issubclass(value.__class__, Sequence):
3773 raise InvalidValueType((Sequence,))
3778 for name, spec in self.specs.items():
3779 value = self._value.get(name)
3790 obj = self.__class__(schema=self.specs)
3792 obj._expl = self._expl
3793 obj.default = self.default
3794 obj.optional = self.optional
3795 obj.offset = self.offset
3796 obj.llen = self.llen
3797 obj.vlen = self.vlen
3798 obj._value = {k: v.copy() for k, v in self._value.items()}
3801 def __eq__(self, their):
3802 if not isinstance(their, self.__class__):
3805 self.specs == their.specs and
3806 self.tag == their.tag and
3807 self._expl == their._expl and
3808 self._value == their._value
3819 return self.__class__(
3822 impl=self.tag if impl is None else impl,
3823 expl=self._expl if expl is None else expl,
3824 default=self.default if default is None else default,
3825 optional=self.optional if optional is None else optional,
3828 def __contains__(self, key):
3829 return key in self._value
3831 def __setitem__(self, key, value):
3832 spec = self.specs.get(key)
3834 raise ObjUnknown(key)
3836 self._value.pop(key, None)
3838 if not isinstance(value, spec.__class__):
3839 raise InvalidValueType((spec.__class__,))
3840 value = spec(value=value)
3841 if spec.default is not None and value == spec.default:
3842 self._value.pop(key, None)
3844 self._value[key] = value
3846 def __getitem__(self, key):
3847 value = self._value.get(key)
3848 if value is not None:
3850 spec = self.specs.get(key)
3852 raise ObjUnknown(key)
3853 if spec.default is not None:
3857 def _encoded_values(self):
3859 for name, spec in self.specs.items():
3860 value = self._value.get(name)
3864 raise ObjNotReady(name)
3865 raws.append(value.encode())
3869 v = b"".join(self._encoded_values())
3870 return b"".join((self.tag, len_encode(len(v)), v))
3872 def _decode(self, tlv, offset, decode_path, ctx):
3874 t, tlen, lv = tag_strip(tlv)
3875 except DecodeError as err:
3876 raise err.__class__(
3878 klass=self.__class__,
3879 decode_path=decode_path,
3884 klass=self.__class__,
3885 decode_path=decode_path,
3889 l, llen, v = len_decode(lv)
3890 except DecodeError as err:
3891 raise err.__class__(
3893 klass=self.__class__,
3894 decode_path=decode_path,
3898 raise NotEnoughData(
3899 "encoded length is longer than data",
3900 klass=self.__class__,
3901 decode_path=decode_path,
3904 v, tail = v[:l], v[l:]
3905 sub_offset = offset + tlen + llen
3907 for name, spec in self.specs.items():
3908 if len(v) == 0 and spec.optional:
3910 sub_decode_path = decode_path + (name,)
3912 value, v_tail = spec.decode(
3916 decode_path=sub_decode_path,
3924 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
3925 if defined is not None:
3926 defined_by, defined_spec = defined
3927 if issubclass(value.__class__, SequenceOf):
3928 for i, _value in enumerate(value):
3929 sub_sub_decode_path = sub_decode_path + (
3931 DecodePathDefBy(defined_by),
3933 defined_value, defined_tail = defined_spec.decode(
3934 memoryview(bytes(_value)),
3936 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
3937 if value.expled else (value.tlen + value.llen)
3940 decode_path=sub_sub_decode_path,
3943 if len(defined_tail) > 0:
3946 klass=self.__class__,
3947 decode_path=sub_sub_decode_path,
3950 _value.defined = (defined_by, defined_value)
3952 defined_value, defined_tail = defined_spec.decode(
3953 memoryview(bytes(value)),
3955 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
3956 if value.expled else (value.tlen + value.llen)
3959 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
3962 if len(defined_tail) > 0:
3965 klass=self.__class__,
3966 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
3969 value.defined = (defined_by, defined_value)
3971 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
3973 if spec.default is not None and value == spec.default:
3974 if ctx.get("strict_default_existence", False):
3976 "DEFAULT value met",
3977 klass=self.__class__,
3978 decode_path=sub_decode_path,
3983 values[name] = value
3985 spec_defines = getattr(spec, "defines", ())
3986 if len(spec_defines) == 0:
3987 defines_by_path = ctx.get("defines_by_path", ())
3988 if len(defines_by_path) > 0:
3989 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
3990 if spec_defines is not None and len(spec_defines) > 0:
3991 for rel_path, schema in spec_defines:
3992 defined = schema.get(value, None)
3993 if defined is not None:
3994 ctx.setdefault("defines", []).append((
3995 abs_decode_path(sub_decode_path[:-1], rel_path),
4001 klass=self.__class__,
4002 decode_path=decode_path,
4005 obj = self.__class__(
4009 default=self.default,
4010 optional=self.optional,
4011 _decoded=(offset, llen, l),
4017 value = pp_console_row(next(self.pps()))
4019 for name in self.specs:
4020 _value = self._value.get(name)
4023 cols.append(repr(_value))
4024 return "%s[%s]" % (value, ", ".join(cols))
4026 def pps(self, decode_path=()):
4028 asn1_type_name=self.asn1_type_name,
4029 obj_name=self.__class__.__name__,
4030 decode_path=decode_path,
4031 optional=self.optional,
4032 default=self == self.default,
4033 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4034 expl=None if self._expl is None else tag_decode(self._expl),
4039 expl_offset=self.expl_offset if self.expled else None,
4040 expl_tlen=self.expl_tlen if self.expled else None,
4041 expl_llen=self.expl_llen if self.expled else None,
4042 expl_vlen=self.expl_vlen if self.expled else None,
4044 for name in self.specs:
4045 value = self._value.get(name)
4048 yield value.pps(decode_path=decode_path + (name,))
4051 class Set(Sequence):
4052 """``SET`` structure type
4054 Its usage is identical to :py:class:`pyderasn.Sequence`.
4057 tag_default = tag_encode(form=TagFormConstructed, num=17)
4058 asn1_type_name = "SET"
4061 raws = self._encoded_values()
4064 return b"".join((self.tag, len_encode(len(v)), v))
4066 def _decode(self, tlv, offset, decode_path, ctx):
4068 t, tlen, lv = tag_strip(tlv)
4069 except DecodeError as err:
4070 raise err.__class__(
4072 klass=self.__class__,
4073 decode_path=decode_path,
4078 klass=self.__class__,
4079 decode_path=decode_path,
4083 l, llen, v = len_decode(lv)
4084 except DecodeError as err:
4085 raise err.__class__(
4087 klass=self.__class__,
4088 decode_path=decode_path,
4092 raise NotEnoughData(
4093 "encoded length is longer than data",
4094 klass=self.__class__,
4097 v, tail = v[:l], v[l:]
4098 sub_offset = offset + tlen + llen
4100 specs_items = self.specs.items
4102 for name, spec in specs_items():
4104 value, v_tail = spec.decode(
4108 decode_path=decode_path + (name,),
4114 value.expl_tlvlen if value.expled else value.tlvlen
4117 if spec.default is None or value != spec.default: # pragma: no cover
4118 # SeqMixing.test_encoded_default_accepted covers that place
4119 values[name] = value
4123 klass=self.__class__,
4124 decode_path=decode_path,
4127 obj = self.__class__(
4131 default=self.default,
4132 optional=self.optional,
4133 _decoded=(offset, llen, l),
4139 class SequenceOf(Obj):
4140 """``SEQUENCE OF`` sequence type
4142 For that kind of type you must specify the object it will carry on
4143 (bounds are for example here, not required)::
4145 class Ints(SequenceOf):
4150 >>> ints.append(Integer(123))
4151 >>> ints.append(Integer(234))
4153 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4154 >>> [int(i) for i in ints]
4156 >>> ints.append(Integer(345))
4157 Traceback (most recent call last):
4158 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4161 >>> ints[1] = Integer(345)
4163 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4165 Also you can initialize sequence with preinitialized values:
4167 >>> ints = Ints([Integer(123), Integer(234)])
4169 __slots__ = ("spec", "_bound_min", "_bound_max")
4170 tag_default = tag_encode(form=TagFormConstructed, num=16)
4171 asn1_type_name = "SEQUENCE OF"
4184 super(SequenceOf, self).__init__(
4192 schema = getattr(self, "schema", None)
4194 raise ValueError("schema must be specified")
4196 self._bound_min, self._bound_max = getattr(
4200 ) if bounds is None else bounds
4202 if value is not None:
4203 self._value = self._value_sanitize(value)
4204 if default is not None:
4205 default_value = self._value_sanitize(default)
4206 default_obj = self.__class__(
4211 default_obj._value = default_value
4212 self.default = default_obj
4214 self._value = default_obj.copy()._value
4216 def _value_sanitize(self, value):
4217 if issubclass(value.__class__, SequenceOf):
4218 value = value._value
4219 elif hasattr(value, "__iter__"):
4222 raise InvalidValueType((self.__class__, iter))
4223 if not self._bound_min <= len(value) <= self._bound_max:
4224 raise BoundsError(self._bound_min, len(value), self._bound_max)
4226 if not isinstance(v, self.spec.__class__):
4227 raise InvalidValueType((self.spec.__class__,))
4232 return all(v.ready for v in self._value)
4235 obj = self.__class__(schema=self.spec)
4236 obj._bound_min = self._bound_min
4237 obj._bound_max = self._bound_max
4239 obj._expl = self._expl
4240 obj.default = self.default
4241 obj.optional = self.optional
4242 obj.offset = self.offset
4243 obj.llen = self.llen
4244 obj.vlen = self.vlen
4245 obj._value = [v.copy() for v in self._value]
4248 def __eq__(self, their):
4249 if isinstance(their, self.__class__):
4251 self.spec == their.spec and
4252 self.tag == their.tag and
4253 self._expl == their._expl and
4254 self._value == their._value
4256 if hasattr(their, "__iter__"):
4257 return self._value == list(their)
4269 return self.__class__(
4273 (self._bound_min, self._bound_max)
4274 if bounds is None else bounds
4276 impl=self.tag if impl is None else impl,
4277 expl=self._expl if expl is None else expl,
4278 default=self.default if default is None else default,
4279 optional=self.optional if optional is None else optional,
4282 def __contains__(self, key):
4283 return key in self._value
4285 def append(self, value):
4286 if not isinstance(value, self.spec.__class__):
4287 raise InvalidValueType((self.spec.__class__,))
4288 if len(self._value) + 1 > self._bound_max:
4291 len(self._value) + 1,
4294 self._value.append(value)
4297 self._assert_ready()
4298 return iter(self._value)
4301 self._assert_ready()
4302 return len(self._value)
4304 def __setitem__(self, key, value):
4305 if not isinstance(value, self.spec.__class__):
4306 raise InvalidValueType((self.spec.__class__,))
4307 self._value[key] = self.spec(value=value)
4309 def __getitem__(self, key):
4310 return self._value[key]
4312 def _encoded_values(self):
4313 return [v.encode() for v in self._value]
4316 v = b"".join(self._encoded_values())
4317 return b"".join((self.tag, len_encode(len(v)), v))
4319 def _decode(self, tlv, offset, decode_path, ctx):
4321 t, tlen, lv = tag_strip(tlv)
4322 except DecodeError as err:
4323 raise err.__class__(
4325 klass=self.__class__,
4326 decode_path=decode_path,
4331 klass=self.__class__,
4332 decode_path=decode_path,
4336 l, llen, v = len_decode(lv)
4337 except DecodeError as err:
4338 raise err.__class__(
4340 klass=self.__class__,
4341 decode_path=decode_path,
4345 raise NotEnoughData(
4346 "encoded length is longer than data",
4347 klass=self.__class__,
4348 decode_path=decode_path,
4351 v, tail = v[:l], v[l:]
4352 sub_offset = offset + tlen + llen
4356 value, v_tail = spec.decode(
4360 decode_path=decode_path + (str(len(_value)),),
4363 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4365 _value.append(value)
4366 obj = self.__class__(
4369 bounds=(self._bound_min, self._bound_max),
4372 default=self.default,
4373 optional=self.optional,
4374 _decoded=(offset, llen, l),
4380 pp_console_row(next(self.pps())),
4381 ", ".join(repr(v) for v in self._value),
4384 def pps(self, decode_path=()):
4386 asn1_type_name=self.asn1_type_name,
4387 obj_name=self.__class__.__name__,
4388 decode_path=decode_path,
4389 optional=self.optional,
4390 default=self == self.default,
4391 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4392 expl=None if self._expl is None else tag_decode(self._expl),
4397 expl_offset=self.expl_offset if self.expled else None,
4398 expl_tlen=self.expl_tlen if self.expled else None,
4399 expl_llen=self.expl_llen if self.expled else None,
4400 expl_vlen=self.expl_vlen if self.expled else None,
4402 for i, value in enumerate(self._value):
4403 yield value.pps(decode_path=decode_path + (str(i),))
4406 class SetOf(SequenceOf):
4407 """``SET OF`` sequence type
4409 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4412 tag_default = tag_encode(form=TagFormConstructed, num=17)
4413 asn1_type_name = "SET OF"
4416 raws = self._encoded_values()
4419 return b"".join((self.tag, len_encode(len(v)), v))
4422 def obj_by_path(pypath): # pragma: no cover
4423 """Import object specified as string Python path
4425 Modules must be separated from classes/functions with ``:``.
4427 >>> obj_by_path("foo.bar:Baz")
4428 <class 'foo.bar.Baz'>
4429 >>> obj_by_path("foo.bar:Baz.boo")
4430 <classmethod 'foo.bar.Baz.boo'>
4432 mod, objs = pypath.rsplit(":", 1)
4433 from importlib import import_module
4434 obj = import_module(mod)
4435 for obj_name in objs.split("."):
4436 obj = getattr(obj, obj_name)
4440 def generic_decoder(): # pragma: no cover
4441 # All of this below is a big hack with self references
4442 choice = PrimitiveTypes()
4443 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4444 choice.specs["SetOf"] = SetOf(schema=choice)
4446 choice.specs["SequenceOf%d" % i] = SequenceOf(
4450 choice.specs["Any"] = Any()
4452 # Class name equals to type name, to omit it from output
4453 class SEQUENCEOF(SequenceOf):
4457 def pprint_any(obj, oids=None, with_colours=False):
4458 def _pprint_pps(pps):
4460 if hasattr(pp, "_fields"):
4461 if pp.asn1_type_name == Choice.asn1_type_name:
4463 pp_kwargs = pp._asdict()
4464 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4465 pp = _pp(**pp_kwargs)
4466 yield pp_console_row(
4471 with_colours=with_colours,
4473 for row in pp_console_blob(pp):
4476 for row in _pprint_pps(pp):
4478 return "\n".join(_pprint_pps(obj.pps()))
4479 return SEQUENCEOF(), pprint_any
4482 def main(): # pragma: no cover
4484 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4485 parser.add_argument(
4489 help="Skip that number of bytes from the beginning",
4491 parser.add_argument(
4493 help="Python path to dictionary with OIDs",
4495 parser.add_argument(
4497 help="Python path to schema definition to use",
4499 parser.add_argument(
4500 "--defines-by-path",
4501 help="Python path to decoder's defines_by_path",
4503 parser.add_argument(
4505 type=argparse.FileType("rb"),
4506 help="Path to DER file you want to decode",
4508 args = parser.parse_args()
4509 args.DERFile.seek(args.skip)
4510 der = memoryview(args.DERFile.read())
4511 args.DERFile.close()
4512 oids = obj_by_path(args.oids) if args.oids else {}
4514 schema = obj_by_path(args.schema)
4515 from functools import partial
4516 pprinter = partial(pprint, big_blobs=True)
4518 schema, pprinter = generic_decoder()
4519 obj, tail = schema().decode(
4522 None if args.defines_by_path is None else
4523 {"defines_by_path": obj_by_path(args.defines_by_path)}
4529 with_colours=True if environ.get("NO_COLOR") is None else False,
4532 print("\nTrailing data: %s" % hexenc(tail))
4535 if __name__ == "__main__":