3 # PyDERASN -- Python ASN.1 DER codec with abstract structures
4 # Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 """Python ASN.1 DER codec with abstract structures
21 This library allows you to marshal and unmarshal various structures in
22 ASN.1 DER format, like this:
26 >>> Integer().decode(raw) == i
29 There are primitive types, holding single values
30 (:py:class:`pyderasn.BitString`,
31 :py:class:`pyderasn.Boolean`,
32 :py:class:`pyderasn.Enumerated`,
33 :py:class:`pyderasn.GeneralizedTime`,
34 :py:class:`pyderasn.Integer`,
35 :py:class:`pyderasn.Null`,
36 :py:class:`pyderasn.ObjectIdentifier`,
37 :py:class:`pyderasn.OctetString`,
38 :py:class:`pyderasn.UTCTime`,
39 :py:class:`various strings <pyderasn.CommonString>`
40 (:py:class:`pyderasn.BMPString`,
41 :py:class:`pyderasn.GeneralString`,
42 :py:class:`pyderasn.GraphicString`,
43 :py:class:`pyderasn.IA5String`,
44 :py:class:`pyderasn.ISO646String`,
45 :py:class:`pyderasn.NumericString`,
46 :py:class:`pyderasn.PrintableString`,
47 :py:class:`pyderasn.T61String`,
48 :py:class:`pyderasn.TeletexString`,
49 :py:class:`pyderasn.UniversalString`,
50 :py:class:`pyderasn.UTF8String`,
51 :py:class:`pyderasn.VideotexString`,
52 :py:class:`pyderasn.VisibleString`)),
53 constructed types, holding multiple primitive types
54 (:py:class:`pyderasn.Sequence`,
55 :py:class:`pyderasn.SequenceOf`,
56 :py:class:`pyderasn.Set`,
57 :py:class:`pyderasn.SetOf`),
58 and special types like
59 :py:class:`pyderasn.Any` and
60 :py:class:`pyderasn.Choice`.
68 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
69 the default tag used during coding process. You can override it with
70 either ``IMPLICIT`` (using ``impl`` keyword argument), or
71 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments take
72 raw binary string, containing that tag. You can **not** set implicit and
73 explicit tags simultaneously.
75 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
76 functions, allowing you to easily create ``CONTEXT``
77 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
78 number. Pay attention that explicit tags always have *constructed* tag
79 (``tag_ctxc``), but implicit tags for primitive types are primitive
84 >>> Integer(impl=tag_ctxp(1))
86 >>> Integer(expl=tag_ctxc(2))
89 Implicit tag is not explicitly shown.
91 Two objects of the same type, but with different implicit/explicit tags
94 You can get object's effective tag (either default or implicited) through
95 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
98 >>> tag_decode(tag_ctxc(123))
100 >>> klass, form, num = tag_decode(tag_ctxc(123))
101 >>> klass == TagClassContext
103 >>> form == TagFormConstructed
106 To determine if object has explicit tag, use ``expled`` boolean property
107 and ``expl_tag`` property, returning explicit tag's value.
112 Many objects in sequences could be ``OPTIONAL`` and could have
113 ``DEFAULT`` value. You can specify that object's property using
114 corresponding keyword arguments.
116 >>> Integer(optional=True, default=123)
117 INTEGER 123 OPTIONAL DEFAULT
119 Those specifications do not play any role in primitive value encoding,
120 but are taken into account when dealing with sequences holding them. For
121 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
124 class Version(Integer):
130 class TBSCertificate(Sequence):
132 ("version", Version(expl=tag_ctxc(0), default="v1")),
135 When default argument is used and value is not specified, then it equals
143 Some objects give ability to set value size constraints. This is either
144 possible integer value, or allowed length of various strings and
145 sequences. Constraints are set in the following way::
150 And values satisfaction is checked as: ``MIN <= X <= MAX``.
152 For simplicity you can also set bounds the following way::
154 bounded_x = X(bounds=(MIN, MAX))
156 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
162 All objects have ``ready`` boolean property, that tells if object is
163 ready to be encoded. If that kind of action is performed on unready
164 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
166 All objects have ``copy()`` method, that returns their copy, that can be
174 Decoding is performed using ``decode()`` method. ``offset`` optional
175 argument could be used to set initial object's offset in the binary
176 data, for convenience. It returns decoded object and remaining
177 unmarshalled data (tail). Internally all work is done on
178 ``memoryview(data)``, and you can leave returning tail as a memoryview,
179 by specifying ``leavemm=True`` argument.
181 When object is decoded, ``decoded`` property is true and you can safely
182 use following properties:
184 * ``offset`` -- position including initial offset where object's tag starts
185 * ``tlen`` -- length of object's tag
186 * ``llen`` -- length of object's length value
187 * ``vlen`` -- length of object's value
188 * ``tlvlen`` -- length of the whole object
190 Pay attention that those values do **not** include anything related to
191 explicit tag. If you want to know information about it, then use:
192 ``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
193 lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
194 (that actually equals to ordinary ``tlvlen``).
196 When error occurs, then :py:exc:`pyderasn.DecodeError` is raised.
203 You can specify so called context keyword argument during ``decode()``
204 invocation. It is dictionary containing various options governing
207 Currently available context options:
209 * :ref:`defines_by_path <defines_by_path_ctx>`
210 * :ref:`strict_default_existence <strict_default_existence_ctx>`
217 All objects have ``pps()`` method, that is a generator of
218 :py:class:`pyderasn.PP` namedtuple, holding various raw information
219 about the object. If ``pps`` is called on sequences, then all underlying
220 ``PP`` will be yielded.
222 You can use :py:func:`pyderasn.pp_console_row` function, converting
223 those ``PP`` to human readable string. Actually exactly it is used for
224 all object ``repr``. But it is easy to write custom formatters.
226 >>> from pyderasn import pprint
227 >>> encoded = Integer(-12345).encode()
228 >>> obj, tail = Integer().decode(encoded)
229 >>> print(pprint(obj))
230 0 [1,1, 2] INTEGER -12345
237 ASN.1 structures often have ANY and OCTET STRING fields, that are
238 DEFINED BY some previously met ObjectIdentifier. This library provides
239 ability to specify mapping between some OID and field that must be
240 decoded with specific specification.
245 :py:class:`pyderasn.ObjectIdentifier` field inside
246 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
247 necessary for decoding structures. For example, CMS (:rfc:`5652`)
250 class ContentInfo(Sequence):
252 ("contentType", ContentType(defines=((("content",), {
253 id_digestedData: DigestedData(),
254 id_signedData: SignedData(),
256 ("content", Any(expl=tag_ctxc(0))),
259 ``contentType`` field tells that it defines that ``content`` must be
260 decoded with ``SignedData`` specification, if ``contentType`` equals to
261 ``id-signedData``. The same applies to ``DigestedData``. If
262 ``contentType`` contains unknown OID, then no automatic decoding is
265 You can specify multiple fields, that will be autodecoded -- that is why
266 ``defines`` kwarg is a sequence. You can specify defined field
267 relatively or absolutely to current decode path. For example ``defines``
268 for AlgorithmIdentifier of X.509's
269 ``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
273 id_ecPublicKey: ECParameters(),
274 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
276 (("..", "subjectPublicKey"), {
277 id_rsaEncryption: RSAPublicKey(),
278 id_GostR3410_2001: OctetString(),
282 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
283 autodecode its parameters inside SPKI's algorithm and its public key
286 Following types can be automatically decoded (DEFINED BY):
288 * :py:class:`pyderasn.Any`
289 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
290 * :py:class:`pyderasn.OctetString`
291 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
292 ``Any``/``BitString``/``OctetString``-s
294 When any of those fields is automatically decoded, then ``.defined``
295 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
296 was defined, ``value`` contains corresponding decoded value. For example
297 above, ``content_info["content"].defined == (id_signedData,
300 .. _defines_by_path_ctx:
302 defines_by_path context option
303 ______________________________
305 Sometimes you either can not or do not want to explicitly set *defines*
306 in the scheme. You can dynamically apply those definitions when calling
307 ``.decode()`` method.
309 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
310 value must be sequence of following tuples::
312 (decode_path, defines)
314 where ``decode_path`` is a tuple holding so-called decode path to the
315 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
316 ``defines``, holding exactly the same value as accepted in its keyword
319 For example, again for CMS, you want to automatically decode
320 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
321 structures it may hold. Also, automatically decode ``controlSequence``
324 content_info, tail = ContentInfo().decode(data, defines_by_path=(
327 ((("content",), {id_signedData: SignedData()}),),
332 DecodePathDefBy(id_signedData),
337 id_cct_PKIData: PKIData(),
338 id_cct_PKIResponse: PKIResponse(),
344 DecodePathDefBy(id_signedData),
347 DecodePathDefBy(id_cct_PKIResponse),
353 id_cmc_recipientNonce: RecipientNonce(),
354 id_cmc_senderNonce: SenderNonce(),
355 id_cmc_statusInfoV2: CMCStatusInfoV2(),
356 id_cmc_transactionId: TransactionId(),
361 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
362 First function is useful for path construction when some automatic
363 decoding is already done. ``any`` means literally any value it meet --
364 useful for SEQUENCE/SET OF-s.
371 .. autoclass:: pyderasn.Boolean
376 .. autoclass:: pyderasn.Integer
381 .. autoclass:: pyderasn.BitString
386 .. autoclass:: pyderasn.OctetString
391 .. autoclass:: pyderasn.Null
396 .. autoclass:: pyderasn.ObjectIdentifier
401 .. autoclass:: pyderasn.Enumerated
405 .. autoclass:: pyderasn.CommonString
409 .. autoclass:: pyderasn.UTCTime
410 :members: __init__, todatetime
414 .. autoclass:: pyderasn.GeneralizedTime
421 .. autoclass:: pyderasn.Choice
426 .. autoclass:: PrimitiveTypes
430 .. autoclass:: pyderasn.Any
438 .. autoclass:: pyderasn.Sequence
443 .. autoclass:: pyderasn.Set
448 .. autoclass:: pyderasn.SequenceOf
453 .. autoclass:: pyderasn.SetOf
459 .. autofunction:: pyderasn.abs_decode_path
460 .. autofunction:: pyderasn.hexenc
461 .. autofunction:: pyderasn.hexdec
462 .. autofunction:: pyderasn.tag_encode
463 .. autofunction:: pyderasn.tag_decode
464 .. autofunction:: pyderasn.tag_ctxp
465 .. autofunction:: pyderasn.tag_ctxc
466 .. autoclass:: pyderasn.Obj
469 from codecs import getdecoder
470 from codecs import getencoder
471 from collections import namedtuple
472 from collections import OrderedDict
473 from datetime import datetime
474 from math import ceil
475 from os import environ
476 from string import digits
478 from six import add_metaclass
479 from six import binary_type
480 from six import byte2int
481 from six import indexbytes
482 from six import int2byte
483 from six import integer_types
484 from six import iterbytes
486 from six import string_types
487 from six import text_type
488 from six.moves import xrange as six_xrange
492 from termcolor import colored
494 def colored(what, *args):
537 "TagClassApplication",
541 "TagFormConstructed",
552 TagClassUniversal = 0
553 TagClassApplication = 1 << 6
554 TagClassContext = 1 << 7
555 TagClassPrivate = 1 << 6 | 1 << 7
557 TagFormConstructed = 1 << 5
560 TagClassApplication: "APPLICATION ",
561 TagClassPrivate: "PRIVATE ",
562 TagClassUniversal: "UNIV ",
566 ########################################################################
568 ########################################################################
570 class DecodeError(Exception):
571 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
573 :param str msg: reason of decode failing
574 :param klass: optional exact DecodeError inherited class (like
575 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
576 :py:exc:`InvalidLength`)
577 :param decode_path: tuple of strings. It contains human
578 readable names of the fields through which
579 decoding process has passed
580 :param int offset: binary offset where failure happened
582 super(DecodeError, self).__init__()
585 self.decode_path = decode_path
591 "" if self.klass is None else self.klass.__name__,
593 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
594 if len(self.decode_path) > 0 else ""
596 ("(at %d)" % self.offset) if self.offset > 0 else "",
602 return "%s(%s)" % (self.__class__.__name__, self)
605 class NotEnoughData(DecodeError):
609 class TagMismatch(DecodeError):
613 class InvalidLength(DecodeError):
617 class InvalidOID(DecodeError):
621 class ObjUnknown(ValueError):
622 def __init__(self, name):
623 super(ObjUnknown, self).__init__()
627 return "object is unknown: %s" % self.name
630 return "%s(%s)" % (self.__class__.__name__, self)
633 class ObjNotReady(ValueError):
634 def __init__(self, name):
635 super(ObjNotReady, self).__init__()
639 return "object is not ready: %s" % self.name
642 return "%s(%s)" % (self.__class__.__name__, self)
645 class InvalidValueType(ValueError):
646 def __init__(self, expected_types):
647 super(InvalidValueType, self).__init__()
648 self.expected_types = expected_types
651 return "invalid value type, expected: %s" % ", ".join(
652 [repr(t) for t in self.expected_types]
656 return "%s(%s)" % (self.__class__.__name__, self)
659 class BoundsError(ValueError):
660 def __init__(self, bound_min, value, bound_max):
661 super(BoundsError, self).__init__()
662 self.bound_min = bound_min
664 self.bound_max = bound_max
667 return "unsatisfied bounds: %s <= %s <= %s" % (
674 return "%s(%s)" % (self.__class__.__name__, self)
677 ########################################################################
679 ########################################################################
681 _hexdecoder = getdecoder("hex")
682 _hexencoder = getencoder("hex")
686 """Binary data to hexadecimal string convert
688 return _hexdecoder(data)[0]
692 """Hexadecimal string to binary data convert
694 return _hexencoder(data)[0].decode("ascii")
697 def int_bytes_len(num, byte_len=8):
700 return int(ceil(float(num.bit_length()) / byte_len))
703 def zero_ended_encode(num):
704 octets = bytearray(int_bytes_len(num, 7))
706 octets[i] = num & 0x7F
710 octets[i] = 0x80 | (num & 0x7F)
716 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
717 """Encode tag to binary form
719 :param int num: tag's number
720 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
721 :py:data:`pyderasn.TagClassContext`,
722 :py:data:`pyderasn.TagClassApplication`,
723 :py:data:`pyderasn.TagClassPrivate`)
724 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
725 :py:data:`pyderasn.TagFormConstructed`)
729 return int2byte(klass | form | num)
730 # [XX|X|11111][1.......][1.......] ... [0.......]
731 return int2byte(klass | form | 31) + zero_ended_encode(num)
735 """Decode tag from binary form
739 No validation is performed, assuming that it has already passed.
741 It returns tuple with three integers, as
742 :py:func:`pyderasn.tag_encode` accepts.
744 first_octet = byte2int(tag)
745 klass = first_octet & 0xC0
746 form = first_octet & 0x20
747 if first_octet & 0x1F < 0x1F:
748 return (klass, form, first_octet & 0x1F)
750 for octet in iterbytes(tag[1:]):
753 return (klass, form, num)
757 """Create CONTEXT PRIMITIVE tag
759 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
763 """Create CONTEXT CONSTRUCTED tag
765 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
769 """Take off tag from the data
771 :returns: (encoded tag, tag length, remaining data)
774 raise NotEnoughData("no data at all")
775 if byte2int(data) & 0x1F < 31:
776 return data[:1], 1, data[1:]
781 raise DecodeError("unfinished tag")
782 if indexbytes(data, i) & 0x80 == 0:
785 return data[:i], i, data[i:]
791 octets = bytearray(int_bytes_len(l) + 1)
792 octets[0] = 0x80 | (len(octets) - 1)
793 for i in six_xrange(len(octets) - 1, 0, -1):
799 def len_decode(data):
801 raise NotEnoughData("no data at all")
802 first_octet = byte2int(data)
803 if first_octet & 0x80 == 0:
804 return first_octet, 1, data[1:]
805 octets_num = first_octet & 0x7F
806 if octets_num + 1 > len(data):
807 raise NotEnoughData("encoded length is longer than data")
809 raise DecodeError("long form instead of short one")
810 if byte2int(data[1:]) == 0:
811 raise DecodeError("leading zeros")
813 for v in iterbytes(data[1:1 + octets_num]):
816 raise DecodeError("long form instead of short one")
817 return l, 1 + octets_num, data[1 + octets_num:]
820 ########################################################################
822 ########################################################################
824 class AutoAddSlots(type):
825 def __new__(mcs, name, bases, _dict):
826 _dict["__slots__"] = _dict.get("__slots__", ())
827 return type.__new__(mcs, name, bases, _dict)
830 @add_metaclass(AutoAddSlots)
832 """Common ASN.1 object class
834 All ASN.1 types are inherited from it. It has metaclass that
835 automatically adds ``__slots__`` to all inherited classes.
857 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
858 self._expl = getattr(self, "expl", None) if expl is None else expl
859 if self.tag != self.tag_default and self._expl is not None:
861 "implicit and explicit tags can not be set simultaneously"
863 if default is not None:
865 self.optional = optional
866 self.offset, self.llen, self.vlen = _decoded
871 def ready(self): # pragma: no cover
872 """Is object ready to be encoded?
874 raise NotImplementedError()
876 def _assert_ready(self):
878 raise ObjNotReady(self.__class__.__name__)
882 """Is object decoded?
884 return (self.llen + self.vlen) > 0
886 def copy(self): # pragma: no cover
887 """Make a copy of object, safe to be mutated
889 raise NotImplementedError()
897 return self.tlen + self.llen + self.vlen
899 def __str__(self): # pragma: no cover
900 return self.__bytes__() if PY2 else self.__unicode__()
902 def __ne__(self, their):
903 return not(self == their)
905 def __gt__(self, their): # pragma: no cover
906 return not(self < their)
908 def __le__(self, their): # pragma: no cover
909 return (self == their) or (self < their)
911 def __ge__(self, their): # pragma: no cover
912 return (self == their) or (self > their)
914 def _encode(self): # pragma: no cover
915 raise NotImplementedError()
917 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
918 raise NotImplementedError()
922 if self._expl is None:
924 return b"".join((self._expl, len_encode(len(raw)), raw))
937 :param data: either binary or memoryview
938 :param int offset: initial data's offset
939 :param bool leavemm: do we need to leave memoryview of remaining
940 data as is, or convert it to bytes otherwise
941 :param ctx: optional :ref:`context <ctx>` governing decoding process.
942 :param tag_only: decode only the tag, without length and contents
943 (used only in Choice and Set structures, trying to
944 determine if tag satisfies the scheme)
945 :returns: (Obj, remaining data)
949 tlv = memoryview(data)
950 if self._expl is None:
951 result = self._decode(
954 decode_path=decode_path,
963 t, tlen, lv = tag_strip(tlv)
964 except DecodeError as err:
967 klass=self.__class__,
968 decode_path=decode_path,
973 klass=self.__class__,
974 decode_path=decode_path,
978 l, llen, v = len_decode(lv)
979 except DecodeError as err:
982 klass=self.__class__,
983 decode_path=decode_path,
988 "encoded length is longer than data",
989 klass=self.__class__,
990 decode_path=decode_path,
993 result = self._decode(
995 offset=offset + tlen + llen,
996 decode_path=decode_path,
1003 return obj, (tail if leavemm else tail.tobytes())
1007 return self._expl is not None
1014 def expl_tlen(self):
1015 return len(self._expl)
1018 def expl_llen(self):
1019 return len(len_encode(self.tlvlen))
1022 def expl_offset(self):
1023 return self.offset - self.expl_tlen - self.expl_llen
1026 def expl_vlen(self):
1030 def expl_tlvlen(self):
1031 return self.expl_tlen + self.expl_llen + self.expl_vlen
1034 class DecodePathDefBy(object):
1035 """DEFINED BY representation inside decode path
1037 __slots__ = ("defined_by",)
1039 def __init__(self, defined_by):
1040 self.defined_by = defined_by
1042 def __ne__(self, their):
1043 return not(self == their)
1045 def __eq__(self, their):
1046 if not isinstance(their, self.__class__):
1048 return self.defined_by == their.defined_by
1051 return "DEFINED BY " + str(self.defined_by)
1054 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1057 ########################################################################
1059 ########################################################################
1061 PP = namedtuple("PP", (
1083 asn1_type_name="unknown",
1122 def _colorize(what, colour, with_colours, attrs=("bold",)):
1123 return colored(what, colour, attrs=attrs) if with_colours else what
1138 " " if pp.expl_offset is None else
1139 ("-%d" % (pp.offset - pp.expl_offset))
1142 cols.append(_colorize(col, "red", with_colours, ()))
1143 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1144 cols.append(_colorize(col, "green", with_colours, ()))
1145 if len(pp.decode_path) > 0:
1146 cols.append(" ." * (len(pp.decode_path)))
1147 ent = pp.decode_path[-1]
1148 if isinstance(ent, DecodePathDefBy):
1149 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1150 value = str(ent.defined_by)
1152 oids is not None and
1153 ent.defined_by.asn1_type_name ==
1154 ObjectIdentifier.asn1_type_name and
1157 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1159 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1161 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1162 if pp.expl is not None:
1163 klass, _, num = pp.expl
1164 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1165 cols.append(_colorize(col, "blue", with_colours))
1166 if pp.impl is not None:
1167 klass, _, num = pp.impl
1168 col = "[%s%d]" % (TagClassReprs[klass], num)
1169 cols.append(_colorize(col, "blue", with_colours))
1170 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1171 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1172 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1173 if pp.value is not None:
1175 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1177 oids is not None and
1178 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1181 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1183 if isinstance(pp.blob, binary_type):
1184 cols.append(hexenc(pp.blob))
1185 elif isinstance(pp.blob, tuple):
1186 cols.append(", ".join(pp.blob))
1188 cols.append(_colorize("OPTIONAL", "red", with_colours))
1190 cols.append(_colorize("DEFAULT", "red", with_colours))
1191 return " ".join(cols)
1194 def pp_console_blob(pp):
1195 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
1196 if len(pp.decode_path) > 0:
1197 cols.append(" ." * (len(pp.decode_path) + 1))
1198 if isinstance(pp.blob, binary_type):
1199 blob = hexenc(pp.blob).upper()
1200 for i in range(0, len(blob), 32):
1201 chunk = blob[i:i + 32]
1202 yield " ".join(cols + [":".join(
1203 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1205 elif isinstance(pp.blob, tuple):
1206 yield " ".join(cols + [", ".join(pp.blob)])
1209 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1210 """Pretty print object
1212 :param Obj obj: object you want to pretty print
1213 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1214 from it is met, then its humand readable form is printed
1215 :param big_blobs: if large binary objects are met (like OctetString
1216 values), do we need to print them too, on separate
1218 :param with_colours: colourize output, if ``termcolor`` library
1221 def _pprint_pps(pps):
1223 if hasattr(pp, "_fields"):
1225 yield pp_console_row(
1230 with_colours=with_colours,
1232 for row in pp_console_blob(pp):
1235 yield pp_console_row(
1240 with_colours=with_colours,
1243 for row in _pprint_pps(pp):
1245 return "\n".join(_pprint_pps(obj.pps()))
1248 ########################################################################
1249 # ASN.1 primitive types
1250 ########################################################################
1253 """``BOOLEAN`` boolean type
1255 >>> b = Boolean(True)
1257 >>> b == Boolean(True)
1263 tag_default = tag_encode(1)
1264 asn1_type_name = "BOOLEAN"
1276 :param value: set the value. Either boolean type, or
1277 :py:class:`pyderasn.Boolean` object
1278 :param bytes impl: override default tag with ``IMPLICIT`` one
1279 :param bytes expl: override default tag with ``EXPLICIT`` one
1280 :param default: set default value. Type same as in ``value``
1281 :param bool optional: is object ``OPTIONAL`` in sequence
1283 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1284 self._value = None if value is None else self._value_sanitize(value)
1285 if default is not None:
1286 default = self._value_sanitize(default)
1287 self.default = self.__class__(
1293 self._value = default
1295 def _value_sanitize(self, value):
1296 if issubclass(value.__class__, Boolean):
1298 if isinstance(value, bool):
1300 raise InvalidValueType((self.__class__, bool))
1304 return self._value is not None
1307 obj = self.__class__()
1308 obj._value = self._value
1310 obj._expl = self._expl
1311 obj.default = self.default
1312 obj.optional = self.optional
1313 obj.offset = self.offset
1314 obj.llen = self.llen
1315 obj.vlen = self.vlen
1318 def __nonzero__(self):
1319 self._assert_ready()
1323 self._assert_ready()
1326 def __eq__(self, their):
1327 if isinstance(their, bool):
1328 return self._value == their
1329 if not issubclass(their.__class__, Boolean):
1332 self._value == their._value and
1333 self.tag == their.tag and
1334 self._expl == their._expl
1345 return self.__class__(
1347 impl=self.tag if impl is None else impl,
1348 expl=self._expl if expl is None else expl,
1349 default=self.default if default is None else default,
1350 optional=self.optional if optional is None else optional,
1354 self._assert_ready()
1358 (b"\xFF" if self._value else b"\x00"),
1361 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1363 t, _, lv = tag_strip(tlv)
1364 except DecodeError as err:
1365 raise err.__class__(
1367 klass=self.__class__,
1368 decode_path=decode_path,
1373 klass=self.__class__,
1374 decode_path=decode_path,
1380 l, _, v = len_decode(lv)
1381 except DecodeError as err:
1382 raise err.__class__(
1384 klass=self.__class__,
1385 decode_path=decode_path,
1389 raise InvalidLength(
1390 "Boolean's length must be equal to 1",
1391 klass=self.__class__,
1392 decode_path=decode_path,
1396 raise NotEnoughData(
1397 "encoded length is longer than data",
1398 klass=self.__class__,
1399 decode_path=decode_path,
1402 first_octet = byte2int(v)
1404 if first_octet == 0:
1406 elif first_octet == 0xFF:
1408 elif ctx.get("bered", False):
1413 "unacceptable Boolean value",
1414 klass=self.__class__,
1415 decode_path=decode_path,
1418 obj = self.__class__(
1422 default=self.default,
1423 optional=self.optional,
1424 _decoded=(offset, 1, 1),
1430 return pp_console_row(next(self.pps()))
1432 def pps(self, decode_path=()):
1434 asn1_type_name=self.asn1_type_name,
1435 obj_name=self.__class__.__name__,
1436 decode_path=decode_path,
1437 value=str(self._value) if self.ready else None,
1438 optional=self.optional,
1439 default=self == self.default,
1440 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1441 expl=None if self._expl is None else tag_decode(self._expl),
1446 expl_offset=self.expl_offset if self.expled else None,
1447 expl_tlen=self.expl_tlen if self.expled else None,
1448 expl_llen=self.expl_llen if self.expled else None,
1449 expl_vlen=self.expl_vlen if self.expled else None,
1454 """``INTEGER`` integer type
1456 >>> b = Integer(-123)
1458 >>> b == Integer(-123)
1463 >>> Integer(2, bounds=(1, 3))
1465 >>> Integer(5, bounds=(1, 3))
1466 Traceback (most recent call last):
1467 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1471 class Version(Integer):
1478 >>> v = Version("v1")
1485 {'v3': 2, 'v1': 0, 'v2': 1}
1487 __slots__ = ("specs", "_bound_min", "_bound_max")
1488 tag_default = tag_encode(2)
1489 asn1_type_name = "INTEGER"
1503 :param value: set the value. Either integer type, named value
1504 (if ``schema`` is specified in the class), or
1505 :py:class:`pyderasn.Integer` object
1506 :param bounds: set ``(MIN, MAX)`` value constraint.
1507 (-inf, +inf) by default
1508 :param bytes impl: override default tag with ``IMPLICIT`` one
1509 :param bytes expl: override default tag with ``EXPLICIT`` one
1510 :param default: set default value. Type same as in ``value``
1511 :param bool optional: is object ``OPTIONAL`` in sequence
1513 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1515 specs = getattr(self, "schema", {}) if _specs is None else _specs
1516 self.specs = specs if isinstance(specs, dict) else dict(specs)
1517 self._bound_min, self._bound_max = getattr(
1520 (float("-inf"), float("+inf")),
1521 ) if bounds is None else bounds
1522 if value is not None:
1523 self._value = self._value_sanitize(value)
1524 if default is not None:
1525 default = self._value_sanitize(default)
1526 self.default = self.__class__(
1532 if self._value is None:
1533 self._value = default
1535 def _value_sanitize(self, value):
1536 if issubclass(value.__class__, Integer):
1537 value = value._value
1538 elif isinstance(value, integer_types):
1540 elif isinstance(value, str):
1541 value = self.specs.get(value)
1543 raise ObjUnknown("integer value: %s" % value)
1545 raise InvalidValueType((self.__class__, int, str))
1546 if not self._bound_min <= value <= self._bound_max:
1547 raise BoundsError(self._bound_min, value, self._bound_max)
1552 return self._value is not None
1555 obj = self.__class__(_specs=self.specs)
1556 obj._value = self._value
1557 obj._bound_min = self._bound_min
1558 obj._bound_max = self._bound_max
1560 obj._expl = self._expl
1561 obj.default = self.default
1562 obj.optional = self.optional
1563 obj.offset = self.offset
1564 obj.llen = self.llen
1565 obj.vlen = self.vlen
1569 self._assert_ready()
1570 return int(self._value)
1573 self._assert_ready()
1576 bytes(self._expl or b"") +
1577 str(self._value).encode("ascii"),
1580 def __eq__(self, their):
1581 if isinstance(their, integer_types):
1582 return self._value == their
1583 if not issubclass(their.__class__, Integer):
1586 self._value == their._value and
1587 self.tag == their.tag and
1588 self._expl == their._expl
1591 def __lt__(self, their):
1592 return self._value < their._value
1596 for name, value in self.specs.items():
1597 if value == self._value:
1609 return self.__class__(
1612 (self._bound_min, self._bound_max)
1613 if bounds is None else bounds
1615 impl=self.tag if impl is None else impl,
1616 expl=self._expl if expl is None else expl,
1617 default=self.default if default is None else default,
1618 optional=self.optional if optional is None else optional,
1623 self._assert_ready()
1627 octets = bytearray([0])
1631 octets = bytearray()
1633 octets.append((value & 0xFF) ^ 0xFF)
1635 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1638 octets = bytearray()
1640 octets.append(value & 0xFF)
1642 if octets[-1] & 0x80 > 0:
1645 octets = bytes(octets)
1647 bytes_len = ceil(value.bit_length() / 8) or 1
1650 octets = value.to_bytes(
1655 except OverflowError:
1659 return b"".join((self.tag, len_encode(len(octets)), octets))
1661 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1663 t, _, lv = tag_strip(tlv)
1664 except DecodeError as err:
1665 raise err.__class__(
1667 klass=self.__class__,
1668 decode_path=decode_path,
1673 klass=self.__class__,
1674 decode_path=decode_path,
1680 l, llen, v = len_decode(lv)
1681 except DecodeError as err:
1682 raise err.__class__(
1684 klass=self.__class__,
1685 decode_path=decode_path,
1689 raise NotEnoughData(
1690 "encoded length is longer than data",
1691 klass=self.__class__,
1692 decode_path=decode_path,
1696 raise NotEnoughData(
1698 klass=self.__class__,
1699 decode_path=decode_path,
1702 v, tail = v[:l], v[l:]
1703 first_octet = byte2int(v)
1705 second_octet = byte2int(v[1:])
1707 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1708 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1711 "non normalized integer",
1712 klass=self.__class__,
1713 decode_path=decode_path,
1718 if first_octet & 0x80 > 0:
1719 octets = bytearray()
1720 for octet in bytearray(v):
1721 octets.append(octet ^ 0xFF)
1722 for octet in octets:
1723 value = (value << 8) | octet
1727 for octet in bytearray(v):
1728 value = (value << 8) | octet
1730 value = int.from_bytes(v, byteorder="big", signed=True)
1732 obj = self.__class__(
1734 bounds=(self._bound_min, self._bound_max),
1737 default=self.default,
1738 optional=self.optional,
1740 _decoded=(offset, llen, l),
1742 except BoundsError as err:
1745 klass=self.__class__,
1746 decode_path=decode_path,
1752 return pp_console_row(next(self.pps()))
1754 def pps(self, decode_path=()):
1756 asn1_type_name=self.asn1_type_name,
1757 obj_name=self.__class__.__name__,
1758 decode_path=decode_path,
1759 value=(self.named or str(self._value)) if self.ready else None,
1760 optional=self.optional,
1761 default=self == self.default,
1762 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1763 expl=None if self._expl is None else tag_decode(self._expl),
1768 expl_offset=self.expl_offset if self.expled else None,
1769 expl_tlen=self.expl_tlen if self.expled else None,
1770 expl_llen=self.expl_llen if self.expled else None,
1771 expl_vlen=self.expl_vlen if self.expled else None,
1775 class BitString(Obj):
1776 """``BIT STRING`` bit string type
1778 >>> BitString(b"hello world")
1779 BIT STRING 88 bits 68656c6c6f20776f726c64
1782 >>> b == b"hello world"
1787 >>> b = BitString("'010110000000'B")
1788 BIT STRING 12 bits 5800
1791 >>> b[0], b[1], b[2], b[3]
1792 (False, True, False, True)
1796 [False, True, False, True, True, False, False, False, False, False, False, False]
1800 class KeyUsage(BitString):
1802 ("digitalSignature", 0),
1803 ("nonRepudiation", 1),
1804 ("keyEncipherment", 2),
1807 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1808 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1810 ['nonRepudiation', 'keyEncipherment']
1812 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1814 __slots__ = ("specs", "defined")
1815 tag_default = tag_encode(3)
1816 asn1_type_name = "BIT STRING"
1829 :param value: set the value. Either binary type, tuple of named
1830 values (if ``schema`` is specified in the class),
1831 string in ``'XXX...'B`` form, or
1832 :py:class:`pyderasn.BitString` object
1833 :param bytes impl: override default tag with ``IMPLICIT`` one
1834 :param bytes expl: override default tag with ``EXPLICIT`` one
1835 :param default: set default value. Type same as in ``value``
1836 :param bool optional: is object ``OPTIONAL`` in sequence
1838 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1839 specs = getattr(self, "schema", {}) if _specs is None else _specs
1840 self.specs = specs if isinstance(specs, dict) else dict(specs)
1841 self._value = None if value is None else self._value_sanitize(value)
1842 if default is not None:
1843 default = self._value_sanitize(default)
1844 self.default = self.__class__(
1850 self._value = default
1853 def _bits2octets(self, bits):
1854 if len(self.specs) > 0:
1855 bits = bits.rstrip("0")
1857 bits += "0" * ((8 - (bit_len % 8)) % 8)
1858 octets = bytearray(len(bits) // 8)
1859 for i in six_xrange(len(octets)):
1860 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1861 return bit_len, bytes(octets)
1863 def _value_sanitize(self, value):
1864 if issubclass(value.__class__, BitString):
1866 if isinstance(value, (string_types, binary_type)):
1868 isinstance(value, string_types) and
1869 value.startswith("'") and
1870 value.endswith("'B")
1873 if not set(value) <= set(("0", "1")):
1874 raise ValueError("B's coding contains unacceptable chars")
1875 return self._bits2octets(value)
1876 elif isinstance(value, binary_type):
1877 return (len(value) * 8, value)
1879 raise InvalidValueType((
1884 if isinstance(value, tuple):
1887 isinstance(value[0], integer_types) and
1888 isinstance(value[1], binary_type)
1893 bit = self.specs.get(name)
1895 raise ObjUnknown("BitString value: %s" % name)
1898 return self._bits2octets("")
1900 return self._bits2octets("".join(
1901 ("1" if bit in bits else "0")
1902 for bit in six_xrange(max(bits) + 1)
1904 raise InvalidValueType((self.__class__, binary_type, string_types))
1908 return self._value is not None
1911 obj = self.__class__(_specs=self.specs)
1913 if value is not None:
1914 value = (value[0], value[1])
1917 obj._expl = self._expl
1918 obj.default = self.default
1919 obj.optional = self.optional
1920 obj.offset = self.offset
1921 obj.llen = self.llen
1922 obj.vlen = self.vlen
1926 self._assert_ready()
1927 for i in six_xrange(self._value[0]):
1932 self._assert_ready()
1933 return self._value[0]
1935 def __bytes__(self):
1936 self._assert_ready()
1937 return self._value[1]
1939 def __eq__(self, their):
1940 if isinstance(their, bytes):
1941 return self._value[1] == their
1942 if not issubclass(their.__class__, BitString):
1945 self._value == their._value and
1946 self.tag == their.tag and
1947 self._expl == their._expl
1952 return [name for name, bit in self.specs.items() if self[bit]]
1962 return self.__class__(
1964 impl=self.tag if impl is None else impl,
1965 expl=self._expl if expl is None else expl,
1966 default=self.default if default is None else default,
1967 optional=self.optional if optional is None else optional,
1971 def __getitem__(self, key):
1972 if isinstance(key, int):
1973 bit_len, octets = self._value
1977 byte2int(memoryview(octets)[key // 8:]) >>
1980 if isinstance(key, string_types):
1981 value = self.specs.get(key)
1983 raise ObjUnknown("BitString value: %s" % key)
1985 raise InvalidValueType((int, str))
1988 self._assert_ready()
1989 bit_len, octets = self._value
1992 len_encode(len(octets) + 1),
1993 int2byte((8 - bit_len % 8) % 8),
1997 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1999 t, _, lv = tag_strip(tlv)
2000 except DecodeError as err:
2001 raise err.__class__(
2003 klass=self.__class__,
2004 decode_path=decode_path,
2009 klass=self.__class__,
2010 decode_path=decode_path,
2016 l, llen, v = len_decode(lv)
2017 except DecodeError as err:
2018 raise err.__class__(
2020 klass=self.__class__,
2021 decode_path=decode_path,
2025 raise NotEnoughData(
2026 "encoded length is longer than data",
2027 klass=self.__class__,
2028 decode_path=decode_path,
2032 raise NotEnoughData(
2034 klass=self.__class__,
2035 decode_path=decode_path,
2038 pad_size = byte2int(v)
2039 if l == 1 and pad_size != 0:
2041 "invalid empty value",
2042 klass=self.__class__,
2043 decode_path=decode_path,
2049 klass=self.__class__,
2050 decode_path=decode_path,
2053 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2056 klass=self.__class__,
2057 decode_path=decode_path,
2060 v, tail = v[:l], v[l:]
2061 obj = self.__class__(
2062 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2065 default=self.default,
2066 optional=self.optional,
2068 _decoded=(offset, llen, l),
2073 return pp_console_row(next(self.pps()))
2075 def pps(self, decode_path=()):
2079 bit_len, blob = self._value
2080 value = "%d bits" % bit_len
2081 if len(self.specs) > 0:
2082 blob = tuple(self.named)
2084 asn1_type_name=self.asn1_type_name,
2085 obj_name=self.__class__.__name__,
2086 decode_path=decode_path,
2089 optional=self.optional,
2090 default=self == self.default,
2091 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2092 expl=None if self._expl is None else tag_decode(self._expl),
2097 expl_offset=self.expl_offset if self.expled else None,
2098 expl_tlen=self.expl_tlen if self.expled else None,
2099 expl_llen=self.expl_llen if self.expled else None,
2100 expl_vlen=self.expl_vlen if self.expled else None,
2102 defined_by, defined = self.defined or (None, None)
2103 if defined_by is not None:
2105 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2109 class OctetString(Obj):
2110 """``OCTET STRING`` binary string type
2112 >>> s = OctetString(b"hello world")
2113 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2114 >>> s == OctetString(b"hello world")
2119 >>> OctetString(b"hello", bounds=(4, 4))
2120 Traceback (most recent call last):
2121 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2122 >>> OctetString(b"hell", bounds=(4, 4))
2123 OCTET STRING 4 bytes 68656c6c
2125 __slots__ = ("_bound_min", "_bound_max", "defined")
2126 tag_default = tag_encode(4)
2127 asn1_type_name = "OCTET STRING"
2140 :param value: set the value. Either binary type, or
2141 :py:class:`pyderasn.OctetString` object
2142 :param bounds: set ``(MIN, MAX)`` value size constraint.
2143 (-inf, +inf) by default
2144 :param bytes impl: override default tag with ``IMPLICIT`` one
2145 :param bytes expl: override default tag with ``EXPLICIT`` one
2146 :param default: set default value. Type same as in ``value``
2147 :param bool optional: is object ``OPTIONAL`` in sequence
2149 super(OctetString, self).__init__(
2157 self._bound_min, self._bound_max = getattr(
2161 ) if bounds is None else bounds
2162 if value is not None:
2163 self._value = self._value_sanitize(value)
2164 if default is not None:
2165 default = self._value_sanitize(default)
2166 self.default = self.__class__(
2171 if self._value is None:
2172 self._value = default
2175 def _value_sanitize(self, value):
2176 if issubclass(value.__class__, OctetString):
2177 value = value._value
2178 elif isinstance(value, binary_type):
2181 raise InvalidValueType((self.__class__, bytes))
2182 if not self._bound_min <= len(value) <= self._bound_max:
2183 raise BoundsError(self._bound_min, len(value), self._bound_max)
2188 return self._value is not None
2191 obj = self.__class__()
2192 obj._value = self._value
2193 obj._bound_min = self._bound_min
2194 obj._bound_max = self._bound_max
2196 obj._expl = self._expl
2197 obj.default = self.default
2198 obj.optional = self.optional
2199 obj.offset = self.offset
2200 obj.llen = self.llen
2201 obj.vlen = self.vlen
2204 def __bytes__(self):
2205 self._assert_ready()
2208 def __eq__(self, their):
2209 if isinstance(their, binary_type):
2210 return self._value == their
2211 if not issubclass(their.__class__, OctetString):
2214 self._value == their._value and
2215 self.tag == their.tag and
2216 self._expl == their._expl
2219 def __lt__(self, their):
2220 return self._value < their._value
2231 return self.__class__(
2234 (self._bound_min, self._bound_max)
2235 if bounds is None else bounds
2237 impl=self.tag if impl is None else impl,
2238 expl=self._expl if expl is None else expl,
2239 default=self.default if default is None else default,
2240 optional=self.optional if optional is None else optional,
2244 self._assert_ready()
2247 len_encode(len(self._value)),
2251 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2253 t, _, lv = tag_strip(tlv)
2254 except DecodeError as err:
2255 raise err.__class__(
2257 klass=self.__class__,
2258 decode_path=decode_path,
2263 klass=self.__class__,
2264 decode_path=decode_path,
2270 l, llen, v = len_decode(lv)
2271 except DecodeError as err:
2272 raise err.__class__(
2274 klass=self.__class__,
2275 decode_path=decode_path,
2279 raise NotEnoughData(
2280 "encoded length is longer than data",
2281 klass=self.__class__,
2282 decode_path=decode_path,
2285 v, tail = v[:l], v[l:]
2287 obj = self.__class__(
2289 bounds=(self._bound_min, self._bound_max),
2292 default=self.default,
2293 optional=self.optional,
2294 _decoded=(offset, llen, l),
2296 except DecodeError as err:
2299 klass=self.__class__,
2300 decode_path=decode_path,
2303 except BoundsError as err:
2306 klass=self.__class__,
2307 decode_path=decode_path,
2313 return pp_console_row(next(self.pps()))
2315 def pps(self, decode_path=()):
2317 asn1_type_name=self.asn1_type_name,
2318 obj_name=self.__class__.__name__,
2319 decode_path=decode_path,
2320 value=("%d bytes" % len(self._value)) if self.ready else None,
2321 blob=self._value if self.ready else None,
2322 optional=self.optional,
2323 default=self == self.default,
2324 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2325 expl=None if self._expl is None else tag_decode(self._expl),
2330 expl_offset=self.expl_offset if self.expled else None,
2331 expl_tlen=self.expl_tlen if self.expled else None,
2332 expl_llen=self.expl_llen if self.expled else None,
2333 expl_vlen=self.expl_vlen if self.expled else None,
2335 defined_by, defined = self.defined or (None, None)
2336 if defined_by is not None:
2338 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2343 """``NULL`` null object
2351 tag_default = tag_encode(5)
2352 asn1_type_name = "NULL"
2356 value=None, # unused, but Sequence passes it
2363 :param bytes impl: override default tag with ``IMPLICIT`` one
2364 :param bytes expl: override default tag with ``EXPLICIT`` one
2365 :param bool optional: is object ``OPTIONAL`` in sequence
2367 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2375 obj = self.__class__()
2377 obj._expl = self._expl
2378 obj.default = self.default
2379 obj.optional = self.optional
2380 obj.offset = self.offset
2381 obj.llen = self.llen
2382 obj.vlen = self.vlen
2385 def __eq__(self, their):
2386 if not issubclass(their.__class__, Null):
2389 self.tag == their.tag and
2390 self._expl == their._expl
2400 return self.__class__(
2401 impl=self.tag if impl is None else impl,
2402 expl=self._expl if expl is None else expl,
2403 optional=self.optional if optional is None else optional,
2407 return self.tag + len_encode(0)
2409 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2411 t, _, lv = tag_strip(tlv)
2412 except DecodeError as err:
2413 raise err.__class__(
2415 klass=self.__class__,
2416 decode_path=decode_path,
2421 klass=self.__class__,
2422 decode_path=decode_path,
2428 l, _, v = len_decode(lv)
2429 except DecodeError as err:
2430 raise err.__class__(
2432 klass=self.__class__,
2433 decode_path=decode_path,
2437 raise InvalidLength(
2438 "Null must have zero length",
2439 klass=self.__class__,
2440 decode_path=decode_path,
2443 obj = self.__class__(
2446 optional=self.optional,
2447 _decoded=(offset, 1, 0),
2452 return pp_console_row(next(self.pps()))
2454 def pps(self, decode_path=()):
2456 asn1_type_name=self.asn1_type_name,
2457 obj_name=self.__class__.__name__,
2458 decode_path=decode_path,
2459 optional=self.optional,
2460 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2461 expl=None if self._expl is None else tag_decode(self._expl),
2466 expl_offset=self.expl_offset if self.expled else None,
2467 expl_tlen=self.expl_tlen if self.expled else None,
2468 expl_llen=self.expl_llen if self.expled else None,
2469 expl_vlen=self.expl_vlen if self.expled else None,
2473 class ObjectIdentifier(Obj):
2474 """``OBJECT IDENTIFIER`` OID type
2476 >>> oid = ObjectIdentifier((1, 2, 3))
2477 OBJECT IDENTIFIER 1.2.3
2478 >>> oid == ObjectIdentifier("1.2.3")
2484 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2485 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2487 >>> str(ObjectIdentifier((3, 1)))
2488 Traceback (most recent call last):
2489 pyderasn.InvalidOID: unacceptable first arc value
2491 __slots__ = ("defines",)
2492 tag_default = tag_encode(6)
2493 asn1_type_name = "OBJECT IDENTIFIER"
2506 :param value: set the value. Either tuples of integers,
2507 string of "."-concatenated integers, or
2508 :py:class:`pyderasn.ObjectIdentifier` object
2509 :param defines: sequence of tuples. Each tuple has two elements.
2510 First one is relative to current one decode
2511 path, aiming to the field defined by that OID.
2512 Read about relative path in
2513 :py:func:`pyderasn.abs_decode_path`. Second
2514 tuple element is ``{OID: pyderasn.Obj()}``
2515 dictionary, mapping between current OID value
2516 and structure applied to defined field.
2517 :ref:`Read about DEFINED BY <definedby>`
2518 :param bytes impl: override default tag with ``IMPLICIT`` one
2519 :param bytes expl: override default tag with ``EXPLICIT`` one
2520 :param default: set default value. Type same as in ``value``
2521 :param bool optional: is object ``OPTIONAL`` in sequence
2523 super(ObjectIdentifier, self).__init__(
2531 if value is not None:
2532 self._value = self._value_sanitize(value)
2533 if default is not None:
2534 default = self._value_sanitize(default)
2535 self.default = self.__class__(
2540 if self._value is None:
2541 self._value = default
2542 self.defines = defines
2544 def __add__(self, their):
2545 if isinstance(their, self.__class__):
2546 return self.__class__(self._value + their._value)
2547 if isinstance(their, tuple):
2548 return self.__class__(self._value + their)
2549 raise InvalidValueType((self.__class__, tuple))
2551 def _value_sanitize(self, value):
2552 if issubclass(value.__class__, ObjectIdentifier):
2554 if isinstance(value, string_types):
2556 value = tuple(int(arc) for arc in value.split("."))
2558 raise InvalidOID("unacceptable arcs values")
2559 if isinstance(value, tuple):
2561 raise InvalidOID("less than 2 arcs")
2562 first_arc = value[0]
2563 if first_arc in (0, 1):
2564 if not (0 <= value[1] <= 39):
2565 raise InvalidOID("second arc is too wide")
2566 elif first_arc == 2:
2569 raise InvalidOID("unacceptable first arc value")
2571 raise InvalidValueType((self.__class__, str, tuple))
2575 return self._value is not None
2578 obj = self.__class__()
2579 obj._value = self._value
2580 obj.defines = self.defines
2582 obj._expl = self._expl
2583 obj.default = self.default
2584 obj.optional = self.optional
2585 obj.offset = self.offset
2586 obj.llen = self.llen
2587 obj.vlen = self.vlen
2591 self._assert_ready()
2592 return iter(self._value)
2595 return ".".join(str(arc) for arc in self._value or ())
2598 self._assert_ready()
2601 bytes(self._expl or b"") +
2602 str(self._value).encode("ascii"),
2605 def __eq__(self, their):
2606 if isinstance(their, tuple):
2607 return self._value == their
2608 if not issubclass(their.__class__, ObjectIdentifier):
2611 self.tag == their.tag and
2612 self._expl == their._expl and
2613 self._value == their._value
2616 def __lt__(self, their):
2617 return self._value < their._value
2628 return self.__class__(
2630 defines=self.defines if defines is None else defines,
2631 impl=self.tag if impl is None else impl,
2632 expl=self._expl if expl is None else expl,
2633 default=self.default if default is None else default,
2634 optional=self.optional if optional is None else optional,
2638 self._assert_ready()
2640 first_value = value[1]
2641 first_arc = value[0]
2644 elif first_arc == 1:
2646 elif first_arc == 2:
2648 else: # pragma: no cover
2649 raise RuntimeError("invalid arc is stored")
2650 octets = [zero_ended_encode(first_value)]
2651 for arc in value[2:]:
2652 octets.append(zero_ended_encode(arc))
2653 v = b"".join(octets)
2654 return b"".join((self.tag, len_encode(len(v)), v))
2656 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2658 t, _, lv = tag_strip(tlv)
2659 except DecodeError as err:
2660 raise err.__class__(
2662 klass=self.__class__,
2663 decode_path=decode_path,
2668 klass=self.__class__,
2669 decode_path=decode_path,
2675 l, llen, v = len_decode(lv)
2676 except DecodeError as err:
2677 raise err.__class__(
2679 klass=self.__class__,
2680 decode_path=decode_path,
2684 raise NotEnoughData(
2685 "encoded length is longer than data",
2686 klass=self.__class__,
2687 decode_path=decode_path,
2691 raise NotEnoughData(
2693 klass=self.__class__,
2694 decode_path=decode_path,
2697 v, tail = v[:l], v[l:]
2703 octet = indexbytes(v, i)
2704 arc = (arc << 7) | (octet & 0x7F)
2705 if octet & 0x80 == 0:
2713 klass=self.__class__,
2714 decode_path=decode_path,
2718 second_arc = arcs[0]
2719 if 0 <= second_arc <= 39:
2721 elif 40 <= second_arc <= 79:
2727 obj = self.__class__(
2728 value=tuple([first_arc, second_arc] + arcs[1:]),
2731 default=self.default,
2732 optional=self.optional,
2733 _decoded=(offset, llen, l),
2738 return pp_console_row(next(self.pps()))
2740 def pps(self, decode_path=()):
2742 asn1_type_name=self.asn1_type_name,
2743 obj_name=self.__class__.__name__,
2744 decode_path=decode_path,
2745 value=str(self) if self.ready else None,
2746 optional=self.optional,
2747 default=self == self.default,
2748 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2749 expl=None if self._expl is None else tag_decode(self._expl),
2754 expl_offset=self.expl_offset if self.expled else None,
2755 expl_tlen=self.expl_tlen if self.expled else None,
2756 expl_llen=self.expl_llen if self.expled else None,
2757 expl_vlen=self.expl_vlen if self.expled else None,
2761 class Enumerated(Integer):
2762 """``ENUMERATED`` integer type
2764 This type is identical to :py:class:`pyderasn.Integer`, but requires
2765 schema to be specified and does not accept values missing from it.
2768 tag_default = tag_encode(10)
2769 asn1_type_name = "ENUMERATED"
2780 bounds=None, # dummy argument, workability for Integer.decode
2782 super(Enumerated, self).__init__(
2791 if len(self.specs) == 0:
2792 raise ValueError("schema must be specified")
2794 def _value_sanitize(self, value):
2795 if isinstance(value, self.__class__):
2796 value = value._value
2797 elif isinstance(value, integer_types):
2798 if value not in list(self.specs.values()):
2800 "unknown integer value: %s" % value,
2801 klass=self.__class__,
2803 elif isinstance(value, string_types):
2804 value = self.specs.get(value)
2806 raise ObjUnknown("integer value: %s" % value)
2808 raise InvalidValueType((self.__class__, int, str))
2812 obj = self.__class__(_specs=self.specs)
2813 obj._value = self._value
2814 obj._bound_min = self._bound_min
2815 obj._bound_max = self._bound_max
2817 obj._expl = self._expl
2818 obj.default = self.default
2819 obj.optional = self.optional
2820 obj.offset = self.offset
2821 obj.llen = self.llen
2822 obj.vlen = self.vlen
2834 return self.__class__(
2836 impl=self.tag if impl is None else impl,
2837 expl=self._expl if expl is None else expl,
2838 default=self.default if default is None else default,
2839 optional=self.optional if optional is None else optional,
2844 class CommonString(OctetString):
2845 """Common class for all strings
2847 Everything resembles :py:class:`pyderasn.OctetString`, except
2848 ability to deal with unicode text strings.
2850 >>> hexenc("привет мир".encode("utf-8"))
2851 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2852 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
2854 >>> s = UTF8String("привет мир")
2855 UTF8String UTF8String привет мир
2857 'привет мир'
2858 >>> hexenc(bytes(s))
2859 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
2861 >>> PrintableString("привет мир")
2862 Traceback (most recent call last):
2863 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
2865 >>> BMPString("ада", bounds=(2, 2))
2866 Traceback (most recent call last):
2867 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
2868 >>> s = BMPString("ад", bounds=(2, 2))
2871 >>> hexenc(bytes(s))
2879 * - :py:class:`pyderasn.UTF8String`
2881 * - :py:class:`pyderasn.NumericString`
2883 * - :py:class:`pyderasn.PrintableString`
2885 * - :py:class:`pyderasn.TeletexString`
2887 * - :py:class:`pyderasn.T61String`
2889 * - :py:class:`pyderasn.VideotexString`
2891 * - :py:class:`pyderasn.IA5String`
2893 * - :py:class:`pyderasn.GraphicString`
2895 * - :py:class:`pyderasn.VisibleString`
2897 * - :py:class:`pyderasn.ISO646String`
2899 * - :py:class:`pyderasn.GeneralString`
2901 * - :py:class:`pyderasn.UniversalString`
2903 * - :py:class:`pyderasn.BMPString`
2906 __slots__ = ("encoding",)
2908 def _value_sanitize(self, value):
2910 value_decoded = None
2911 if isinstance(value, self.__class__):
2912 value_raw = value._value
2913 elif isinstance(value, text_type):
2914 value_decoded = value
2915 elif isinstance(value, binary_type):
2918 raise InvalidValueType((self.__class__, text_type, binary_type))
2921 value_decoded.encode(self.encoding)
2922 if value_raw is None else value_raw
2925 value_raw.decode(self.encoding)
2926 if value_decoded is None else value_decoded
2928 except (UnicodeEncodeError, UnicodeDecodeError) as err:
2929 raise DecodeError(str(err))
2930 if not self._bound_min <= len(value_decoded) <= self._bound_max:
2938 def __eq__(self, their):
2939 if isinstance(their, binary_type):
2940 return self._value == their
2941 if isinstance(their, text_type):
2942 return self._value == their.encode(self.encoding)
2943 if not isinstance(their, self.__class__):
2946 self._value == their._value and
2947 self.tag == their.tag and
2948 self._expl == their._expl
2951 def __unicode__(self):
2953 return self._value.decode(self.encoding)
2954 return text_type(self._value)
2957 return pp_console_row(next(self.pps(no_unicode=PY2)))
2959 def pps(self, decode_path=(), no_unicode=False):
2962 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
2964 asn1_type_name=self.asn1_type_name,
2965 obj_name=self.__class__.__name__,
2966 decode_path=decode_path,
2968 optional=self.optional,
2969 default=self == self.default,
2970 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2971 expl=None if self._expl is None else tag_decode(self._expl),
2979 class UTF8String(CommonString):
2981 tag_default = tag_encode(12)
2983 asn1_type_name = "UTF8String"
2986 class NumericString(CommonString):
2988 tag_default = tag_encode(18)
2990 asn1_type_name = "NumericString"
2991 allowable_chars = set(digits.encode("ascii"))
2993 def _value_sanitize(self, value):
2994 value = super(NumericString, self)._value_sanitize(value)
2995 if not set(value) <= self.allowable_chars:
2996 raise DecodeError("non-numeric value")
3000 class PrintableString(CommonString):
3002 tag_default = tag_encode(19)
3004 asn1_type_name = "PrintableString"
3007 class TeletexString(CommonString):
3009 tag_default = tag_encode(20)
3011 asn1_type_name = "TeletexString"
3014 class T61String(TeletexString):
3016 asn1_type_name = "T61String"
3019 class VideotexString(CommonString):
3021 tag_default = tag_encode(21)
3022 encoding = "iso-8859-1"
3023 asn1_type_name = "VideotexString"
3026 class IA5String(CommonString):
3028 tag_default = tag_encode(22)
3030 asn1_type_name = "IA5"
3033 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3034 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3035 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3038 class UTCTime(CommonString):
3039 """``UTCTime`` datetime type
3041 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3042 UTCTime UTCTime 2017-09-30T22:07:50
3048 datetime.datetime(2017, 9, 30, 22, 7, 50)
3049 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3050 datetime.datetime(1957, 9, 30, 22, 7, 50)
3053 tag_default = tag_encode(23)
3055 asn1_type_name = "UTCTime"
3057 fmt = "%y%m%d%H%M%SZ"
3067 bounds=None, # dummy argument, workability for OctetString.decode
3070 :param value: set the value. Either datetime type, or
3071 :py:class:`pyderasn.UTCTime` object
3072 :param bytes impl: override default tag with ``IMPLICIT`` one
3073 :param bytes expl: override default tag with ``EXPLICIT`` one
3074 :param default: set default value. Type same as in ``value``
3075 :param bool optional: is object ``OPTIONAL`` in sequence
3077 super(UTCTime, self).__init__(
3085 if value is not None:
3086 self._value = self._value_sanitize(value)
3087 if default is not None:
3088 default = self._value_sanitize(default)
3089 self.default = self.__class__(
3094 if self._value is None:
3095 self._value = default
3097 def _value_sanitize(self, value):
3098 if isinstance(value, self.__class__):
3100 if isinstance(value, datetime):
3101 return value.strftime(self.fmt).encode("ascii")
3102 if isinstance(value, binary_type):
3103 value_decoded = value.decode("ascii")
3104 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3106 datetime.strptime(value_decoded, self.fmt)
3108 raise DecodeError("invalid UTCTime format")
3111 raise DecodeError("invalid UTCTime length")
3112 raise InvalidValueType((self.__class__, datetime))
3114 def __eq__(self, their):
3115 if isinstance(their, binary_type):
3116 return self._value == their
3117 if isinstance(their, datetime):
3118 return self.todatetime() == their
3119 if not isinstance(their, self.__class__):
3122 self._value == their._value and
3123 self.tag == their.tag and
3124 self._expl == their._expl
3127 def todatetime(self):
3128 """Convert to datetime
3132 Pay attention that UTCTime can not hold full year, so all years
3133 having < 50 years are treated as 20xx, 19xx otherwise, according
3134 to X.509 recomendation.
3136 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3137 year = value.year % 100
3139 year=(2000 + year) if year < 50 else (1900 + year),
3143 minute=value.minute,
3144 second=value.second,
3148 return pp_console_row(next(self.pps()))
3150 def pps(self, decode_path=()):
3152 asn1_type_name=self.asn1_type_name,
3153 obj_name=self.__class__.__name__,
3154 decode_path=decode_path,
3155 value=self.todatetime().isoformat() if self.ready else None,
3156 optional=self.optional,
3157 default=self == self.default,
3158 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3159 expl=None if self._expl is None else tag_decode(self._expl),
3167 class GeneralizedTime(UTCTime):
3168 """``GeneralizedTime`` datetime type
3170 This type is similar to :py:class:`pyderasn.UTCTime`.
3172 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3173 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3175 '20170930220750.000123Z'
3176 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3177 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3180 tag_default = tag_encode(24)
3181 asn1_type_name = "GeneralizedTime"
3183 fmt = "%Y%m%d%H%M%SZ"
3184 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3186 def _value_sanitize(self, value):
3187 if isinstance(value, self.__class__):
3189 if isinstance(value, datetime):
3190 return value.strftime(
3191 self.fmt_ms if value.microsecond > 0 else self.fmt
3193 if isinstance(value, binary_type):
3194 value_decoded = value.decode("ascii")
3195 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3197 datetime.strptime(value_decoded, self.fmt)
3200 "invalid GeneralizedTime (without ms) format",
3203 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3205 datetime.strptime(value_decoded, self.fmt_ms)
3208 "invalid GeneralizedTime (with ms) format",
3213 "invalid GeneralizedTime length",
3214 klass=self.__class__,
3216 raise InvalidValueType((self.__class__, datetime))
3218 def todatetime(self):
3219 value = self._value.decode("ascii")
3220 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3221 return datetime.strptime(value, self.fmt)
3222 return datetime.strptime(value, self.fmt_ms)
3225 class GraphicString(CommonString):
3227 tag_default = tag_encode(25)
3228 encoding = "iso-8859-1"
3229 asn1_type_name = "GraphicString"
3232 class VisibleString(CommonString):
3234 tag_default = tag_encode(26)
3236 asn1_type_name = "VisibleString"
3239 class ISO646String(VisibleString):
3241 asn1_type_name = "ISO646String"
3244 class GeneralString(CommonString):
3246 tag_default = tag_encode(27)
3247 encoding = "iso-8859-1"
3248 asn1_type_name = "GeneralString"
3251 class UniversalString(CommonString):
3253 tag_default = tag_encode(28)
3254 encoding = "utf-32-be"
3255 asn1_type_name = "UniversalString"
3258 class BMPString(CommonString):
3260 tag_default = tag_encode(30)
3261 encoding = "utf-16-be"
3262 asn1_type_name = "BMPString"
3266 """``CHOICE`` special type
3270 class GeneralName(Choice):
3272 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3273 ("dNSName", IA5String(impl=tag_ctxp(2))),
3276 >>> gn = GeneralName()
3278 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3279 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3280 >>> gn["dNSName"] = IA5String("bar.baz")
3281 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3282 >>> gn["rfc822Name"]
3285 [2] IA5String IA5 bar.baz
3288 >>> gn.value == gn["dNSName"]
3291 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3293 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3294 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3296 __slots__ = ("specs",)
3298 asn1_type_name = "CHOICE"
3311 :param value: set the value. Either ``(choice, value)`` tuple, or
3312 :py:class:`pyderasn.Choice` object
3313 :param bytes impl: can not be set, do **not** use it
3314 :param bytes expl: override default tag with ``EXPLICIT`` one
3315 :param default: set default value. Type same as in ``value``
3316 :param bool optional: is object ``OPTIONAL`` in sequence
3318 if impl is not None:
3319 raise ValueError("no implicit tag allowed for CHOICE")
3320 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3322 schema = getattr(self, "schema", ())
3323 if len(schema) == 0:
3324 raise ValueError("schema must be specified")
3326 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3329 if value is not None:
3330 self._value = self._value_sanitize(value)
3331 if default is not None:
3332 default_value = self._value_sanitize(default)
3333 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3334 default_obj.specs = self.specs
3335 default_obj._value = default_value
3336 self.default = default_obj
3338 self._value = default_obj.copy()._value
3340 def _value_sanitize(self, value):
3341 if isinstance(value, self.__class__):
3343 if isinstance(value, tuple) and len(value) == 2:
3345 spec = self.specs.get(choice)
3347 raise ObjUnknown(choice)
3348 if not isinstance(obj, spec.__class__):
3349 raise InvalidValueType((spec,))
3350 return (choice, spec(obj))
3351 raise InvalidValueType((self.__class__, tuple))
3355 return self._value is not None and self._value[1].ready
3358 obj = self.__class__(schema=self.specs)
3359 obj._expl = self._expl
3360 obj.default = self.default
3361 obj.optional = self.optional
3362 obj.offset = self.offset
3363 obj.llen = self.llen
3364 obj.vlen = self.vlen
3366 if value is not None:
3367 obj._value = (value[0], value[1].copy())
3370 def __eq__(self, their):
3371 if isinstance(their, tuple) and len(their) == 2:
3372 return self._value == their
3373 if not isinstance(their, self.__class__):
3376 self.specs == their.specs and
3377 self._value == their._value
3387 return self.__class__(
3390 expl=self._expl if expl is None else expl,
3391 default=self.default if default is None else default,
3392 optional=self.optional if optional is None else optional,
3397 self._assert_ready()
3398 return self._value[0]
3402 self._assert_ready()
3403 return self._value[1]
3405 def __getitem__(self, key):
3406 if key not in self.specs:
3407 raise ObjUnknown(key)
3408 if self._value is None:
3410 choice, value = self._value
3415 def __setitem__(self, key, value):
3416 spec = self.specs.get(key)
3418 raise ObjUnknown(key)
3419 if not isinstance(value, spec.__class__):
3420 raise InvalidValueType((spec.__class__,))
3421 self._value = (key, spec(value))
3429 return self._value[1].decoded if self.ready else False
3432 self._assert_ready()
3433 return self._value[1].encode()
3435 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3436 for choice, spec in self.specs.items():
3437 sub_decode_path = decode_path + (choice,)
3443 decode_path=sub_decode_path,
3452 klass=self.__class__,
3453 decode_path=decode_path,
3458 value, tail = spec.decode(
3462 decode_path=sub_decode_path,
3465 obj = self.__class__(
3468 default=self.default,
3469 optional=self.optional,
3470 _decoded=(offset, 0, value.tlvlen),
3472 obj._value = (choice, value)
3476 value = pp_console_row(next(self.pps()))
3478 value = "%s[%r]" % (value, self.value)
3481 def pps(self, decode_path=()):
3483 asn1_type_name=self.asn1_type_name,
3484 obj_name=self.__class__.__name__,
3485 decode_path=decode_path,
3486 value=self.choice if self.ready else None,
3487 optional=self.optional,
3488 default=self == self.default,
3489 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3490 expl=None if self._expl is None else tag_decode(self._expl),
3497 yield self.value.pps(decode_path=decode_path + (self.choice,))
3500 class PrimitiveTypes(Choice):
3501 """Predefined ``CHOICE`` for all generic primitive types
3503 It could be useful for general decoding of some unspecified values:
3505 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3506 OCTET STRING 3 bytes 666f6f
3507 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3511 schema = tuple((klass.__name__, klass()) for klass in (
3536 """``ANY`` special type
3538 >>> Any(Integer(-123))
3540 >>> a = Any(OctetString(b"hello world").encode())
3541 ANY 040b68656c6c6f20776f726c64
3542 >>> hexenc(bytes(a))
3543 b'0x040x0bhello world'
3545 __slots__ = ("defined",)
3546 tag_default = tag_encode(0)
3547 asn1_type_name = "ANY"
3557 :param value: set the value. Either any kind of pyderasn's
3558 **ready** object, or bytes. Pay attention that
3559 **no** validation is performed is raw binary value
3561 :param bytes expl: override default tag with ``EXPLICIT`` one
3562 :param bool optional: is object ``OPTIONAL`` in sequence
3564 super(Any, self).__init__(None, expl, None, optional, _decoded)
3565 self._value = None if value is None else self._value_sanitize(value)
3568 def _value_sanitize(self, value):
3569 if isinstance(value, self.__class__):
3571 if isinstance(value, Obj):
3572 return value.encode()
3573 if isinstance(value, binary_type):
3575 raise InvalidValueType((self.__class__, Obj, binary_type))
3579 return self._value is not None
3582 obj = self.__class__()
3583 obj._value = self._value
3585 obj._expl = self._expl
3586 obj.optional = self.optional
3587 obj.offset = self.offset
3588 obj.llen = self.llen
3589 obj.vlen = self.vlen
3592 def __eq__(self, their):
3593 if isinstance(their, binary_type):
3594 return self._value == their
3595 if issubclass(their.__class__, Any):
3596 return self._value == their._value
3605 return self.__class__(
3607 expl=self._expl if expl is None else expl,
3608 optional=self.optional if optional is None else optional,
3611 def __bytes__(self):
3612 self._assert_ready()
3620 self._assert_ready()
3623 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3625 t, tlen, lv = tag_strip(tlv)
3626 l, llen, v = len_decode(lv)
3627 except DecodeError as err:
3628 raise err.__class__(
3630 klass=self.__class__,
3631 decode_path=decode_path,
3635 raise NotEnoughData(
3636 "encoded length is longer than data",
3637 klass=self.__class__,
3638 decode_path=decode_path,
3641 tlvlen = tlen + llen + l
3642 v, tail = tlv[:tlvlen], v[l:]
3643 obj = self.__class__(
3646 optional=self.optional,
3647 _decoded=(offset, 0, tlvlen),
3653 return pp_console_row(next(self.pps()))
3655 def pps(self, decode_path=()):
3657 asn1_type_name=self.asn1_type_name,
3658 obj_name=self.__class__.__name__,
3659 decode_path=decode_path,
3660 blob=self._value if self.ready else None,
3661 optional=self.optional,
3662 default=self == self.default,
3663 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3664 expl=None if self._expl is None else tag_decode(self._expl),
3669 expl_offset=self.expl_offset if self.expled else None,
3670 expl_tlen=self.expl_tlen if self.expled else None,
3671 expl_llen=self.expl_llen if self.expled else None,
3672 expl_vlen=self.expl_vlen if self.expled else None,
3674 defined_by, defined = self.defined or (None, None)
3675 if defined_by is not None:
3677 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3681 ########################################################################
3682 # ASN.1 constructed types
3683 ########################################################################
3685 def get_def_by_path(defines_by_path, sub_decode_path):
3686 """Get define by decode path
3688 for path, define in defines_by_path:
3689 if len(path) != len(sub_decode_path):
3691 for p1, p2 in zip(path, sub_decode_path):
3692 if (p1 != any) and (p1 != p2):
3698 def abs_decode_path(decode_path, rel_path):
3699 """Create an absolute decode path from current and relative ones
3701 :param decode_path: current decode path, starting point.
3703 :param rel_path: relative path to ``decode_path``. Tuple of strings.
3704 If first tuple's element is "/", then treat it as
3705 an absolute path, ignoring ``decode_path`` as
3706 starting point. Also this tuple can contain ".."
3707 elements, stripping the leading element from
3710 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
3711 ("foo", "bar", "baz", "whatever")
3712 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
3714 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
3717 if rel_path[0] == "/":
3719 if rel_path[0] == "..":
3720 return abs_decode_path(decode_path[:-1], rel_path[1:])
3721 return decode_path + rel_path
3724 class Sequence(Obj):
3725 """``SEQUENCE`` structure type
3727 You have to make specification of sequence::
3729 class Extension(Sequence):
3731 ("extnID", ObjectIdentifier()),
3732 ("critical", Boolean(default=False)),
3733 ("extnValue", OctetString()),
3736 Then, you can work with it as with dictionary.
3738 >>> ext = Extension()
3739 >>> Extension().specs
3741 ('extnID', OBJECT IDENTIFIER),
3742 ('critical', BOOLEAN False OPTIONAL DEFAULT),
3743 ('extnValue', OCTET STRING),
3745 >>> ext["extnID"] = "1.2.3"
3746 Traceback (most recent call last):
3747 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
3748 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
3750 You can determine if sequence is ready to be encoded:
3755 Traceback (most recent call last):
3756 pyderasn.ObjNotReady: object is not ready: extnValue
3757 >>> ext["extnValue"] = OctetString(b"foobar")
3761 Value you want to assign, must have the same **type** as in
3762 corresponding specification, but it can have different tags,
3763 optional/default attributes -- they will be taken from specification
3766 class TBSCertificate(Sequence):
3768 ("version", Version(expl=tag_ctxc(0), default="v1")),
3771 >>> tbs = TBSCertificate()
3772 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
3774 Assign ``None`` to remove value from sequence.
3776 You can set values in Sequence during its initialization:
3778 >>> AlgorithmIdentifier((
3779 ("algorithm", ObjectIdentifier("1.2.3")),
3780 ("parameters", Any(Null()))
3782 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
3784 You can determine if value exists/set in the sequence and take its value:
3786 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
3789 OBJECT IDENTIFIER 1.2.3
3791 But pay attention that if value has default, then it won't be (not
3792 in) in the sequence (because ``DEFAULT`` must not be encoded in
3793 DER), but you can read its value:
3795 >>> "critical" in ext, ext["critical"]
3796 (False, BOOLEAN False)
3797 >>> ext["critical"] = Boolean(True)
3798 >>> "critical" in ext, ext["critical"]
3799 (True, BOOLEAN True)
3801 All defaulted values are always optional.
3803 .. _strict_default_existence_ctx:
3807 When decoded DER contains defaulted value inside, then
3808 technically this is not valid DER encoding. But we allow and pass
3809 it **by default**. Of course reencoding of that kind of DER will
3810 result in different binary representation (validly without
3811 defaulted value inside). You can enable strict defaulted values
3812 existence validation by setting ``"strict_default_existence":
3813 True`` :ref:`context <ctx>` option -- decoding process will raise
3814 an exception if defaulted value is met.
3816 Two sequences are equal if they have equal specification (schema),
3817 implicit/explicit tagging and the same values.
3819 __slots__ = ("specs",)
3820 tag_default = tag_encode(form=TagFormConstructed, num=16)
3821 asn1_type_name = "SEQUENCE"
3833 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
3835 schema = getattr(self, "schema", ())
3837 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3840 if value is not None:
3841 if issubclass(value.__class__, Sequence):
3842 self._value = value._value
3843 elif hasattr(value, "__iter__"):
3844 for seq_key, seq_value in value:
3845 self[seq_key] = seq_value
3847 raise InvalidValueType((Sequence,))
3848 if default is not None:
3849 if not issubclass(default.__class__, Sequence):
3850 raise InvalidValueType((Sequence,))
3851 default_value = default._value
3852 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3853 default_obj.specs = self.specs
3854 default_obj._value = default_value
3855 self.default = default_obj
3857 self._value = default_obj.copy()._value
3861 for name, spec in self.specs.items():
3862 value = self._value.get(name)
3873 obj = self.__class__(schema=self.specs)
3875 obj._expl = self._expl
3876 obj.default = self.default
3877 obj.optional = self.optional
3878 obj.offset = self.offset
3879 obj.llen = self.llen
3880 obj.vlen = self.vlen
3881 obj._value = {k: v.copy() for k, v in self._value.items()}
3884 def __eq__(self, their):
3885 if not isinstance(their, self.__class__):
3888 self.specs == their.specs and
3889 self.tag == their.tag and
3890 self._expl == their._expl and
3891 self._value == their._value
3902 return self.__class__(
3905 impl=self.tag if impl is None else impl,
3906 expl=self._expl if expl is None else expl,
3907 default=self.default if default is None else default,
3908 optional=self.optional if optional is None else optional,
3911 def __contains__(self, key):
3912 return key in self._value
3914 def __setitem__(self, key, value):
3915 spec = self.specs.get(key)
3917 raise ObjUnknown(key)
3919 self._value.pop(key, None)
3921 if not isinstance(value, spec.__class__):
3922 raise InvalidValueType((spec.__class__,))
3923 value = spec(value=value)
3924 if spec.default is not None and value == spec.default:
3925 self._value.pop(key, None)
3927 self._value[key] = value
3929 def __getitem__(self, key):
3930 value = self._value.get(key)
3931 if value is not None:
3933 spec = self.specs.get(key)
3935 raise ObjUnknown(key)
3936 if spec.default is not None:
3940 def _encoded_values(self):
3942 for name, spec in self.specs.items():
3943 value = self._value.get(name)
3947 raise ObjNotReady(name)
3948 raws.append(value.encode())
3952 v = b"".join(self._encoded_values())
3953 return b"".join((self.tag, len_encode(len(v)), v))
3955 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3957 t, tlen, lv = tag_strip(tlv)
3958 except DecodeError as err:
3959 raise err.__class__(
3961 klass=self.__class__,
3962 decode_path=decode_path,
3967 klass=self.__class__,
3968 decode_path=decode_path,
3974 l, llen, v = len_decode(lv)
3975 except DecodeError as err:
3976 raise err.__class__(
3978 klass=self.__class__,
3979 decode_path=decode_path,
3983 raise NotEnoughData(
3984 "encoded length is longer than data",
3985 klass=self.__class__,
3986 decode_path=decode_path,
3989 v, tail = v[:l], v[l:]
3990 sub_offset = offset + tlen + llen
3992 for name, spec in self.specs.items():
3993 if len(v) == 0 and spec.optional:
3995 sub_decode_path = decode_path + (name,)
3997 value, v_tail = spec.decode(
4001 decode_path=sub_decode_path,
4009 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4010 if defined is not None:
4011 defined_by, defined_spec = defined
4012 if issubclass(value.__class__, SequenceOf):
4013 for i, _value in enumerate(value):
4014 sub_sub_decode_path = sub_decode_path + (
4016 DecodePathDefBy(defined_by),
4018 defined_value, defined_tail = defined_spec.decode(
4019 memoryview(bytes(_value)),
4021 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4022 if value.expled else (value.tlen + value.llen)
4025 decode_path=sub_sub_decode_path,
4028 if len(defined_tail) > 0:
4031 klass=self.__class__,
4032 decode_path=sub_sub_decode_path,
4035 _value.defined = (defined_by, defined_value)
4037 defined_value, defined_tail = defined_spec.decode(
4038 memoryview(bytes(value)),
4040 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4041 if value.expled else (value.tlen + value.llen)
4044 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4047 if len(defined_tail) > 0:
4050 klass=self.__class__,
4051 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4054 value.defined = (defined_by, defined_value)
4056 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4058 if spec.default is not None and value == spec.default:
4059 if ctx.get("strict_default_existence", False):
4061 "DEFAULT value met",
4062 klass=self.__class__,
4063 decode_path=sub_decode_path,
4068 values[name] = value
4070 spec_defines = getattr(spec, "defines", ())
4071 if len(spec_defines) == 0:
4072 defines_by_path = ctx.get("defines_by_path", ())
4073 if len(defines_by_path) > 0:
4074 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4075 if spec_defines is not None and len(spec_defines) > 0:
4076 for rel_path, schema in spec_defines:
4077 defined = schema.get(value, None)
4078 if defined is not None:
4079 ctx.setdefault("defines", []).append((
4080 abs_decode_path(sub_decode_path[:-1], rel_path),
4086 klass=self.__class__,
4087 decode_path=decode_path,
4090 obj = self.__class__(
4094 default=self.default,
4095 optional=self.optional,
4096 _decoded=(offset, llen, l),
4102 value = pp_console_row(next(self.pps()))
4104 for name in self.specs:
4105 _value = self._value.get(name)
4108 cols.append(repr(_value))
4109 return "%s[%s]" % (value, ", ".join(cols))
4111 def pps(self, decode_path=()):
4113 asn1_type_name=self.asn1_type_name,
4114 obj_name=self.__class__.__name__,
4115 decode_path=decode_path,
4116 optional=self.optional,
4117 default=self == self.default,
4118 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4119 expl=None if self._expl is None else tag_decode(self._expl),
4124 expl_offset=self.expl_offset if self.expled else None,
4125 expl_tlen=self.expl_tlen if self.expled else None,
4126 expl_llen=self.expl_llen if self.expled else None,
4127 expl_vlen=self.expl_vlen if self.expled else None,
4129 for name in self.specs:
4130 value = self._value.get(name)
4133 yield value.pps(decode_path=decode_path + (name,))
4136 class Set(Sequence):
4137 """``SET`` structure type
4139 Its usage is identical to :py:class:`pyderasn.Sequence`.
4142 tag_default = tag_encode(form=TagFormConstructed, num=17)
4143 asn1_type_name = "SET"
4146 raws = self._encoded_values()
4149 return b"".join((self.tag, len_encode(len(v)), v))
4151 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4153 t, tlen, lv = tag_strip(tlv)
4154 except DecodeError as err:
4155 raise err.__class__(
4157 klass=self.__class__,
4158 decode_path=decode_path,
4163 klass=self.__class__,
4164 decode_path=decode_path,
4170 l, llen, v = len_decode(lv)
4171 except DecodeError as err:
4172 raise err.__class__(
4174 klass=self.__class__,
4175 decode_path=decode_path,
4179 raise NotEnoughData(
4180 "encoded length is longer than data",
4181 klass=self.__class__,
4184 v, tail = v[:l], v[l:]
4185 sub_offset = offset + tlen + llen
4187 specs_items = self.specs.items
4189 for name, spec in specs_items():
4190 sub_decode_path = decode_path + (name,)
4196 decode_path=sub_decode_path,
4205 klass=self.__class__,
4206 decode_path=decode_path,
4209 value, v_tail = spec.decode(
4213 decode_path=sub_decode_path,
4217 value.expl_tlvlen if value.expled else value.tlvlen
4220 if spec.default is None or value != spec.default: # pragma: no cover
4221 # SeqMixing.test_encoded_default_accepted covers that place
4222 values[name] = value
4223 obj = self.__class__(
4227 default=self.default,
4228 optional=self.optional,
4229 _decoded=(offset, llen, l),
4235 class SequenceOf(Obj):
4236 """``SEQUENCE OF`` sequence type
4238 For that kind of type you must specify the object it will carry on
4239 (bounds are for example here, not required)::
4241 class Ints(SequenceOf):
4246 >>> ints.append(Integer(123))
4247 >>> ints.append(Integer(234))
4249 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4250 >>> [int(i) for i in ints]
4252 >>> ints.append(Integer(345))
4253 Traceback (most recent call last):
4254 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4257 >>> ints[1] = Integer(345)
4259 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4261 Also you can initialize sequence with preinitialized values:
4263 >>> ints = Ints([Integer(123), Integer(234)])
4265 __slots__ = ("spec", "_bound_min", "_bound_max")
4266 tag_default = tag_encode(form=TagFormConstructed, num=16)
4267 asn1_type_name = "SEQUENCE OF"
4280 super(SequenceOf, self).__init__(
4288 schema = getattr(self, "schema", None)
4290 raise ValueError("schema must be specified")
4292 self._bound_min, self._bound_max = getattr(
4296 ) if bounds is None else bounds
4298 if value is not None:
4299 self._value = self._value_sanitize(value)
4300 if default is not None:
4301 default_value = self._value_sanitize(default)
4302 default_obj = self.__class__(
4307 default_obj._value = default_value
4308 self.default = default_obj
4310 self._value = default_obj.copy()._value
4312 def _value_sanitize(self, value):
4313 if issubclass(value.__class__, SequenceOf):
4314 value = value._value
4315 elif hasattr(value, "__iter__"):
4318 raise InvalidValueType((self.__class__, iter))
4319 if not self._bound_min <= len(value) <= self._bound_max:
4320 raise BoundsError(self._bound_min, len(value), self._bound_max)
4322 if not isinstance(v, self.spec.__class__):
4323 raise InvalidValueType((self.spec.__class__,))
4328 return all(v.ready for v in self._value)
4331 obj = self.__class__(schema=self.spec)
4332 obj._bound_min = self._bound_min
4333 obj._bound_max = self._bound_max
4335 obj._expl = self._expl
4336 obj.default = self.default
4337 obj.optional = self.optional
4338 obj.offset = self.offset
4339 obj.llen = self.llen
4340 obj.vlen = self.vlen
4341 obj._value = [v.copy() for v in self._value]
4344 def __eq__(self, their):
4345 if isinstance(their, self.__class__):
4347 self.spec == their.spec and
4348 self.tag == their.tag and
4349 self._expl == their._expl and
4350 self._value == their._value
4352 if hasattr(their, "__iter__"):
4353 return self._value == list(their)
4365 return self.__class__(
4369 (self._bound_min, self._bound_max)
4370 if bounds is None else bounds
4372 impl=self.tag if impl is None else impl,
4373 expl=self._expl if expl is None else expl,
4374 default=self.default if default is None else default,
4375 optional=self.optional if optional is None else optional,
4378 def __contains__(self, key):
4379 return key in self._value
4381 def append(self, value):
4382 if not isinstance(value, self.spec.__class__):
4383 raise InvalidValueType((self.spec.__class__,))
4384 if len(self._value) + 1 > self._bound_max:
4387 len(self._value) + 1,
4390 self._value.append(value)
4393 self._assert_ready()
4394 return iter(self._value)
4397 self._assert_ready()
4398 return len(self._value)
4400 def __setitem__(self, key, value):
4401 if not isinstance(value, self.spec.__class__):
4402 raise InvalidValueType((self.spec.__class__,))
4403 self._value[key] = self.spec(value=value)
4405 def __getitem__(self, key):
4406 return self._value[key]
4408 def _encoded_values(self):
4409 return [v.encode() for v in self._value]
4412 v = b"".join(self._encoded_values())
4413 return b"".join((self.tag, len_encode(len(v)), v))
4415 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4417 t, tlen, lv = tag_strip(tlv)
4418 except DecodeError as err:
4419 raise err.__class__(
4421 klass=self.__class__,
4422 decode_path=decode_path,
4427 klass=self.__class__,
4428 decode_path=decode_path,
4434 l, llen, v = len_decode(lv)
4435 except DecodeError as err:
4436 raise err.__class__(
4438 klass=self.__class__,
4439 decode_path=decode_path,
4443 raise NotEnoughData(
4444 "encoded length is longer than data",
4445 klass=self.__class__,
4446 decode_path=decode_path,
4449 v, tail = v[:l], v[l:]
4450 sub_offset = offset + tlen + llen
4454 value, v_tail = spec.decode(
4458 decode_path=decode_path + (str(len(_value)),),
4461 sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
4463 _value.append(value)
4464 obj = self.__class__(
4467 bounds=(self._bound_min, self._bound_max),
4470 default=self.default,
4471 optional=self.optional,
4472 _decoded=(offset, llen, l),
4478 pp_console_row(next(self.pps())),
4479 ", ".join(repr(v) for v in self._value),
4482 def pps(self, decode_path=()):
4484 asn1_type_name=self.asn1_type_name,
4485 obj_name=self.__class__.__name__,
4486 decode_path=decode_path,
4487 optional=self.optional,
4488 default=self == self.default,
4489 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4490 expl=None if self._expl is None else tag_decode(self._expl),
4495 expl_offset=self.expl_offset if self.expled else None,
4496 expl_tlen=self.expl_tlen if self.expled else None,
4497 expl_llen=self.expl_llen if self.expled else None,
4498 expl_vlen=self.expl_vlen if self.expled else None,
4500 for i, value in enumerate(self._value):
4501 yield value.pps(decode_path=decode_path + (str(i),))
4504 class SetOf(SequenceOf):
4505 """``SET OF`` sequence type
4507 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4510 tag_default = tag_encode(form=TagFormConstructed, num=17)
4511 asn1_type_name = "SET OF"
4514 raws = self._encoded_values()
4517 return b"".join((self.tag, len_encode(len(v)), v))
4520 def obj_by_path(pypath): # pragma: no cover
4521 """Import object specified as string Python path
4523 Modules must be separated from classes/functions with ``:``.
4525 >>> obj_by_path("foo.bar:Baz")
4526 <class 'foo.bar.Baz'>
4527 >>> obj_by_path("foo.bar:Baz.boo")
4528 <classmethod 'foo.bar.Baz.boo'>
4530 mod, objs = pypath.rsplit(":", 1)
4531 from importlib import import_module
4532 obj = import_module(mod)
4533 for obj_name in objs.split("."):
4534 obj = getattr(obj, obj_name)
4538 def generic_decoder(): # pragma: no cover
4539 # All of this below is a big hack with self references
4540 choice = PrimitiveTypes()
4541 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4542 choice.specs["SetOf"] = SetOf(schema=choice)
4544 choice.specs["SequenceOf%d" % i] = SequenceOf(
4548 choice.specs["Any"] = Any()
4550 # Class name equals to type name, to omit it from output
4551 class SEQUENCEOF(SequenceOf):
4555 def pprint_any(obj, oids=None, with_colours=False):
4556 def _pprint_pps(pps):
4558 if hasattr(pp, "_fields"):
4559 if pp.asn1_type_name == Choice.asn1_type_name:
4561 pp_kwargs = pp._asdict()
4562 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4563 pp = _pp(**pp_kwargs)
4564 yield pp_console_row(
4569 with_colours=with_colours,
4571 for row in pp_console_blob(pp):
4574 for row in _pprint_pps(pp):
4576 return "\n".join(_pprint_pps(obj.pps()))
4577 return SEQUENCEOF(), pprint_any
4580 def main(): # pragma: no cover
4582 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4583 parser.add_argument(
4587 help="Skip that number of bytes from the beginning",
4589 parser.add_argument(
4591 help="Python path to dictionary with OIDs",
4593 parser.add_argument(
4595 help="Python path to schema definition to use",
4597 parser.add_argument(
4598 "--defines-by-path",
4599 help="Python path to decoder's defines_by_path",
4601 parser.add_argument(
4603 type=argparse.FileType("rb"),
4604 help="Path to DER file you want to decode",
4606 args = parser.parse_args()
4607 args.DERFile.seek(args.skip)
4608 der = memoryview(args.DERFile.read())
4609 args.DERFile.close()
4610 oids = obj_by_path(args.oids) if args.oids else {}
4612 schema = obj_by_path(args.schema)
4613 from functools import partial
4614 pprinter = partial(pprint, big_blobs=True)
4616 schema, pprinter = generic_decoder()
4617 obj, tail = schema().decode(
4620 None if args.defines_by_path is None else
4621 {"defines_by_path": obj_by_path(args.defines_by_path)}
4627 with_colours=True if environ.get("NO_COLOR") is None else False,
4630 print("\nTrailing data: %s" % hexenc(tail))
4633 if __name__ == "__main__":