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 ",
568 ########################################################################
570 ########################################################################
572 class DecodeError(Exception):
573 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
575 :param str msg: reason of decode failing
576 :param klass: optional exact DecodeError inherited class (like
577 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
578 :py:exc:`InvalidLength`)
579 :param decode_path: tuple of strings. It contains human
580 readable names of the fields through which
581 decoding process has passed
582 :param int offset: binary offset where failure happened
584 super(DecodeError, self).__init__()
587 self.decode_path = decode_path
593 "" if self.klass is None else self.klass.__name__,
595 ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
596 if len(self.decode_path) > 0 else ""
598 ("(at %d)" % self.offset) if self.offset > 0 else "",
604 return "%s(%s)" % (self.__class__.__name__, self)
607 class NotEnoughData(DecodeError):
611 class LenIndefForm(DecodeError):
615 class TagMismatch(DecodeError):
619 class InvalidLength(DecodeError):
623 class InvalidOID(DecodeError):
627 class ObjUnknown(ValueError):
628 def __init__(self, name):
629 super(ObjUnknown, self).__init__()
633 return "object is unknown: %s" % self.name
636 return "%s(%s)" % (self.__class__.__name__, self)
639 class ObjNotReady(ValueError):
640 def __init__(self, name):
641 super(ObjNotReady, self).__init__()
645 return "object is not ready: %s" % self.name
648 return "%s(%s)" % (self.__class__.__name__, self)
651 class InvalidValueType(ValueError):
652 def __init__(self, expected_types):
653 super(InvalidValueType, self).__init__()
654 self.expected_types = expected_types
657 return "invalid value type, expected: %s" % ", ".join(
658 [repr(t) for t in self.expected_types]
662 return "%s(%s)" % (self.__class__.__name__, self)
665 class BoundsError(ValueError):
666 def __init__(self, bound_min, value, bound_max):
667 super(BoundsError, self).__init__()
668 self.bound_min = bound_min
670 self.bound_max = bound_max
673 return "unsatisfied bounds: %s <= %s <= %s" % (
680 return "%s(%s)" % (self.__class__.__name__, self)
683 ########################################################################
685 ########################################################################
687 _hexdecoder = getdecoder("hex")
688 _hexencoder = getencoder("hex")
692 """Binary data to hexadecimal string convert
694 return _hexdecoder(data)[0]
698 """Hexadecimal string to binary data convert
700 return _hexencoder(data)[0].decode("ascii")
703 def int_bytes_len(num, byte_len=8):
706 return int(ceil(float(num.bit_length()) / byte_len))
709 def zero_ended_encode(num):
710 octets = bytearray(int_bytes_len(num, 7))
712 octets[i] = num & 0x7F
716 octets[i] = 0x80 | (num & 0x7F)
722 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
723 """Encode tag to binary form
725 :param int num: tag's number
726 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
727 :py:data:`pyderasn.TagClassContext`,
728 :py:data:`pyderasn.TagClassApplication`,
729 :py:data:`pyderasn.TagClassPrivate`)
730 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
731 :py:data:`pyderasn.TagFormConstructed`)
735 return int2byte(klass | form | num)
736 # [XX|X|11111][1.......][1.......] ... [0.......]
737 return int2byte(klass | form | 31) + zero_ended_encode(num)
741 """Decode tag from binary form
745 No validation is performed, assuming that it has already passed.
747 It returns tuple with three integers, as
748 :py:func:`pyderasn.tag_encode` accepts.
750 first_octet = byte2int(tag)
751 klass = first_octet & 0xC0
752 form = first_octet & 0x20
753 if first_octet & 0x1F < 0x1F:
754 return (klass, form, first_octet & 0x1F)
756 for octet in iterbytes(tag[1:]):
759 return (klass, form, num)
763 """Create CONTEXT PRIMITIVE tag
765 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
769 """Create CONTEXT CONSTRUCTED tag
771 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
775 """Take off tag from the data
777 :returns: (encoded tag, tag length, remaining data)
780 raise NotEnoughData("no data at all")
781 if byte2int(data) & 0x1F < 31:
782 return data[:1], 1, data[1:]
787 raise DecodeError("unfinished tag")
788 if indexbytes(data, i) & 0x80 == 0:
791 return data[:i], i, data[i:]
797 octets = bytearray(int_bytes_len(l) + 1)
798 octets[0] = 0x80 | (len(octets) - 1)
799 for i in six_xrange(len(octets) - 1, 0, -1):
805 def len_decode(data):
808 :returns: (decoded length, length's length, remaining data)
809 :raises LenIndefForm: if indefinite form encoding is met
812 raise NotEnoughData("no data at all")
813 first_octet = byte2int(data)
814 if first_octet & 0x80 == 0:
815 return first_octet, 1, data[1:]
816 octets_num = first_octet & 0x7F
817 if octets_num + 1 > len(data):
818 raise NotEnoughData("encoded length is longer than data")
821 if byte2int(data[1:]) == 0:
822 raise DecodeError("leading zeros")
824 for v in iterbytes(data[1:1 + octets_num]):
827 raise DecodeError("long form instead of short one")
828 return l, 1 + octets_num, data[1 + octets_num:]
831 ########################################################################
833 ########################################################################
835 class AutoAddSlots(type):
836 def __new__(mcs, name, bases, _dict):
837 _dict["__slots__"] = _dict.get("__slots__", ())
838 return type.__new__(mcs, name, bases, _dict)
841 @add_metaclass(AutoAddSlots)
843 """Common ASN.1 object class
845 All ASN.1 types are inherited from it. It has metaclass that
846 automatically adds ``__slots__`` to all inherited classes.
870 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
871 self._expl = getattr(self, "expl", None) if expl is None else expl
872 if self.tag != self.tag_default and self._expl is not None:
874 "implicit and explicit tags can not be set simultaneously"
876 if default is not None:
878 self.optional = optional
879 self.offset, self.llen, self.vlen = _decoded
881 self.lenindef = False
882 self.expl_lenindef = False
886 def ready(self): # pragma: no cover
887 """Is object ready to be encoded?
889 raise NotImplementedError()
891 def _assert_ready(self):
893 raise ObjNotReady(self.__class__.__name__)
897 """Is object decoded?
899 return (self.llen + self.vlen) > 0
901 def copy(self): # pragma: no cover
902 """Make a copy of object, safe to be mutated
904 raise NotImplementedError()
912 return self.tlen + self.llen + self.vlen
914 def __str__(self): # pragma: no cover
915 return self.__bytes__() if PY2 else self.__unicode__()
917 def __ne__(self, their):
918 return not(self == their)
920 def __gt__(self, their): # pragma: no cover
921 return not(self < their)
923 def __le__(self, their): # pragma: no cover
924 return (self == their) or (self < their)
926 def __ge__(self, their): # pragma: no cover
927 return (self == their) or (self > their)
929 def _encode(self): # pragma: no cover
930 raise NotImplementedError()
932 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
933 raise NotImplementedError()
937 if self._expl is None:
939 return b"".join((self._expl, len_encode(len(raw)), raw))
952 :param data: either binary or memoryview
953 :param int offset: initial data's offset
954 :param bool leavemm: do we need to leave memoryview of remaining
955 data as is, or convert it to bytes otherwise
956 :param ctx: optional :ref:`context <ctx>` governing decoding process.
957 :param tag_only: decode only the tag, without length and contents
958 (used only in Choice and Set structures, trying to
959 determine if tag satisfies the scheme)
960 :returns: (Obj, remaining data)
964 tlv = memoryview(data)
965 if self._expl is None:
966 result = self._decode(
969 decode_path=decode_path,
978 t, tlen, lv = tag_strip(tlv)
979 except DecodeError as err:
982 klass=self.__class__,
983 decode_path=decode_path,
988 klass=self.__class__,
989 decode_path=decode_path,
993 l, llen, v = len_decode(lv)
994 except LenIndefForm as err:
995 if not ctx.get("bered", False):
998 klass=self.__class__,
999 decode_path=decode_path,
1003 offset += tlen + llen
1004 result = self._decode(
1007 decode_path=decode_path,
1014 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1015 if eoc_expected.tobytes() != EOC:
1018 decode_path=decode_path,
1022 obj.expl_lenindef = True
1023 except DecodeError as err:
1024 raise err.__class__(
1026 klass=self.__class__,
1027 decode_path=decode_path,
1032 raise NotEnoughData(
1033 "encoded length is longer than data",
1034 klass=self.__class__,
1035 decode_path=decode_path,
1038 result = self._decode(
1040 offset=offset + tlen + llen,
1041 decode_path=decode_path,
1048 return obj, (tail if leavemm else tail.tobytes())
1052 return self._expl is not None
1059 def expl_tlen(self):
1060 return len(self._expl)
1063 def expl_llen(self):
1064 if self.expl_lenindef:
1066 return len(len_encode(self.tlvlen))
1069 def expl_offset(self):
1070 return self.offset - self.expl_tlen - self.expl_llen
1073 def expl_vlen(self):
1077 def expl_tlvlen(self):
1078 return self.expl_tlen + self.expl_llen + self.expl_vlen
1081 class DecodePathDefBy(object):
1082 """DEFINED BY representation inside decode path
1084 __slots__ = ("defined_by",)
1086 def __init__(self, defined_by):
1087 self.defined_by = defined_by
1089 def __ne__(self, their):
1090 return not(self == their)
1092 def __eq__(self, their):
1093 if not isinstance(their, self.__class__):
1095 return self.defined_by == their.defined_by
1098 return "DEFINED BY " + str(self.defined_by)
1101 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1104 ########################################################################
1106 ########################################################################
1108 PP = namedtuple("PP", (
1130 asn1_type_name="unknown",
1169 def _colorize(what, colour, with_colours, attrs=("bold",)):
1170 return colored(what, colour, attrs=attrs) if with_colours else what
1185 " " if pp.expl_offset is None else
1186 ("-%d" % (pp.offset - pp.expl_offset))
1189 cols.append(_colorize(col, "red", with_colours, ()))
1190 col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
1191 cols.append(_colorize(col, "green", with_colours, ()))
1192 if len(pp.decode_path) > 0:
1193 cols.append(" ." * (len(pp.decode_path)))
1194 ent = pp.decode_path[-1]
1195 if isinstance(ent, DecodePathDefBy):
1196 cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
1197 value = str(ent.defined_by)
1199 oids is not None and
1200 ent.defined_by.asn1_type_name ==
1201 ObjectIdentifier.asn1_type_name and
1204 cols.append(_colorize("%s:" % oids[value], "green", with_colours))
1206 cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
1208 cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1209 if pp.expl is not None:
1210 klass, _, num = pp.expl
1211 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1212 cols.append(_colorize(col, "blue", with_colours))
1213 if pp.impl is not None:
1214 klass, _, num = pp.impl
1215 col = "[%s%d]" % (TagClassReprs[klass], num)
1216 cols.append(_colorize(col, "blue", with_colours))
1217 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1218 cols.append(_colorize(pp.obj_name, "magenta", with_colours))
1219 cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
1220 if pp.value is not None:
1222 cols.append(_colorize(value, "white", with_colours, ("reverse",)))
1224 oids is not None and
1225 pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
1228 cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
1230 if isinstance(pp.blob, binary_type):
1231 cols.append(hexenc(pp.blob))
1232 elif isinstance(pp.blob, tuple):
1233 cols.append(", ".join(pp.blob))
1235 cols.append(_colorize("OPTIONAL", "red", with_colours))
1237 cols.append(_colorize("DEFAULT", "red", with_colours))
1238 return " ".join(cols)
1241 def pp_console_blob(pp):
1242 cols = [" " * len("XXXXXYY [X,X,XXXX]")]
1243 if len(pp.decode_path) > 0:
1244 cols.append(" ." * (len(pp.decode_path) + 1))
1245 if isinstance(pp.blob, binary_type):
1246 blob = hexenc(pp.blob).upper()
1247 for i in range(0, len(blob), 32):
1248 chunk = blob[i:i + 32]
1249 yield " ".join(cols + [":".join(
1250 chunk[j:j + 2] for j in range(0, len(chunk), 2)
1252 elif isinstance(pp.blob, tuple):
1253 yield " ".join(cols + [", ".join(pp.blob)])
1256 def pprint(obj, oids=None, big_blobs=False, with_colours=False):
1257 """Pretty print object
1259 :param Obj obj: object you want to pretty print
1260 :param oids: ``OID <-> humand readable string`` dictionary. When OID
1261 from it is met, then its humand readable form is printed
1262 :param big_blobs: if large binary objects are met (like OctetString
1263 values), do we need to print them too, on separate
1265 :param with_colours: colourize output, if ``termcolor`` library
1268 def _pprint_pps(pps):
1270 if hasattr(pp, "_fields"):
1272 yield pp_console_row(
1277 with_colours=with_colours,
1279 for row in pp_console_blob(pp):
1282 yield pp_console_row(
1287 with_colours=with_colours,
1290 for row in _pprint_pps(pp):
1292 return "\n".join(_pprint_pps(obj.pps()))
1295 ########################################################################
1296 # ASN.1 primitive types
1297 ########################################################################
1300 """``BOOLEAN`` boolean type
1302 >>> b = Boolean(True)
1304 >>> b == Boolean(True)
1310 tag_default = tag_encode(1)
1311 asn1_type_name = "BOOLEAN"
1323 :param value: set the value. Either boolean type, or
1324 :py:class:`pyderasn.Boolean` object
1325 :param bytes impl: override default tag with ``IMPLICIT`` one
1326 :param bytes expl: override default tag with ``EXPLICIT`` one
1327 :param default: set default value. Type same as in ``value``
1328 :param bool optional: is object ``OPTIONAL`` in sequence
1330 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1331 self._value = None if value is None else self._value_sanitize(value)
1332 if default is not None:
1333 default = self._value_sanitize(default)
1334 self.default = self.__class__(
1340 self._value = default
1342 def _value_sanitize(self, value):
1343 if issubclass(value.__class__, Boolean):
1345 if isinstance(value, bool):
1347 raise InvalidValueType((self.__class__, bool))
1351 return self._value is not None
1354 obj = self.__class__()
1355 obj._value = self._value
1357 obj._expl = self._expl
1358 obj.default = self.default
1359 obj.optional = self.optional
1360 obj.offset = self.offset
1361 obj.llen = self.llen
1362 obj.vlen = self.vlen
1365 def __nonzero__(self):
1366 self._assert_ready()
1370 self._assert_ready()
1373 def __eq__(self, their):
1374 if isinstance(their, bool):
1375 return self._value == their
1376 if not issubclass(their.__class__, Boolean):
1379 self._value == their._value and
1380 self.tag == their.tag and
1381 self._expl == their._expl
1392 return self.__class__(
1394 impl=self.tag if impl is None else impl,
1395 expl=self._expl if expl is None else expl,
1396 default=self.default if default is None else default,
1397 optional=self.optional if optional is None else optional,
1401 self._assert_ready()
1405 (b"\xFF" if self._value else b"\x00"),
1408 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1410 t, _, lv = tag_strip(tlv)
1411 except DecodeError as err:
1412 raise err.__class__(
1414 klass=self.__class__,
1415 decode_path=decode_path,
1420 klass=self.__class__,
1421 decode_path=decode_path,
1427 l, _, v = len_decode(lv)
1428 except DecodeError as err:
1429 raise err.__class__(
1431 klass=self.__class__,
1432 decode_path=decode_path,
1436 raise InvalidLength(
1437 "Boolean's length must be equal to 1",
1438 klass=self.__class__,
1439 decode_path=decode_path,
1443 raise NotEnoughData(
1444 "encoded length is longer than data",
1445 klass=self.__class__,
1446 decode_path=decode_path,
1449 first_octet = byte2int(v)
1451 if first_octet == 0:
1453 elif first_octet == 0xFF:
1455 elif ctx.get("bered", False):
1460 "unacceptable Boolean value",
1461 klass=self.__class__,
1462 decode_path=decode_path,
1465 obj = self.__class__(
1469 default=self.default,
1470 optional=self.optional,
1471 _decoded=(offset, 1, 1),
1477 return pp_console_row(next(self.pps()))
1479 def pps(self, decode_path=()):
1481 asn1_type_name=self.asn1_type_name,
1482 obj_name=self.__class__.__name__,
1483 decode_path=decode_path,
1484 value=str(self._value) if self.ready else None,
1485 optional=self.optional,
1486 default=self == self.default,
1487 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1488 expl=None if self._expl is None else tag_decode(self._expl),
1493 expl_offset=self.expl_offset if self.expled else None,
1494 expl_tlen=self.expl_tlen if self.expled else None,
1495 expl_llen=self.expl_llen if self.expled else None,
1496 expl_vlen=self.expl_vlen if self.expled else None,
1501 """``INTEGER`` integer type
1503 >>> b = Integer(-123)
1505 >>> b == Integer(-123)
1510 >>> Integer(2, bounds=(1, 3))
1512 >>> Integer(5, bounds=(1, 3))
1513 Traceback (most recent call last):
1514 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1518 class Version(Integer):
1525 >>> v = Version("v1")
1532 {'v3': 2, 'v1': 0, 'v2': 1}
1534 __slots__ = ("specs", "_bound_min", "_bound_max")
1535 tag_default = tag_encode(2)
1536 asn1_type_name = "INTEGER"
1550 :param value: set the value. Either integer type, named value
1551 (if ``schema`` is specified in the class), or
1552 :py:class:`pyderasn.Integer` object
1553 :param bounds: set ``(MIN, MAX)`` value constraint.
1554 (-inf, +inf) by default
1555 :param bytes impl: override default tag with ``IMPLICIT`` one
1556 :param bytes expl: override default tag with ``EXPLICIT`` one
1557 :param default: set default value. Type same as in ``value``
1558 :param bool optional: is object ``OPTIONAL`` in sequence
1560 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1562 specs = getattr(self, "schema", {}) if _specs is None else _specs
1563 self.specs = specs if isinstance(specs, dict) else dict(specs)
1564 self._bound_min, self._bound_max = getattr(
1567 (float("-inf"), float("+inf")),
1568 ) if bounds is None else bounds
1569 if value is not None:
1570 self._value = self._value_sanitize(value)
1571 if default is not None:
1572 default = self._value_sanitize(default)
1573 self.default = self.__class__(
1579 if self._value is None:
1580 self._value = default
1582 def _value_sanitize(self, value):
1583 if issubclass(value.__class__, Integer):
1584 value = value._value
1585 elif isinstance(value, integer_types):
1587 elif isinstance(value, str):
1588 value = self.specs.get(value)
1590 raise ObjUnknown("integer value: %s" % value)
1592 raise InvalidValueType((self.__class__, int, str))
1593 if not self._bound_min <= value <= self._bound_max:
1594 raise BoundsError(self._bound_min, value, self._bound_max)
1599 return self._value is not None
1602 obj = self.__class__(_specs=self.specs)
1603 obj._value = self._value
1604 obj._bound_min = self._bound_min
1605 obj._bound_max = self._bound_max
1607 obj._expl = self._expl
1608 obj.default = self.default
1609 obj.optional = self.optional
1610 obj.offset = self.offset
1611 obj.llen = self.llen
1612 obj.vlen = self.vlen
1616 self._assert_ready()
1617 return int(self._value)
1620 self._assert_ready()
1623 bytes(self._expl or b"") +
1624 str(self._value).encode("ascii"),
1627 def __eq__(self, their):
1628 if isinstance(their, integer_types):
1629 return self._value == their
1630 if not issubclass(their.__class__, Integer):
1633 self._value == their._value and
1634 self.tag == their.tag and
1635 self._expl == their._expl
1638 def __lt__(self, their):
1639 return self._value < their._value
1643 for name, value in self.specs.items():
1644 if value == self._value:
1656 return self.__class__(
1659 (self._bound_min, self._bound_max)
1660 if bounds is None else bounds
1662 impl=self.tag if impl is None else impl,
1663 expl=self._expl if expl is None else expl,
1664 default=self.default if default is None else default,
1665 optional=self.optional if optional is None else optional,
1670 self._assert_ready()
1674 octets = bytearray([0])
1678 octets = bytearray()
1680 octets.append((value & 0xFF) ^ 0xFF)
1682 if len(octets) == 0 or octets[-1] & 0x80 == 0:
1685 octets = bytearray()
1687 octets.append(value & 0xFF)
1689 if octets[-1] & 0x80 > 0:
1692 octets = bytes(octets)
1694 bytes_len = ceil(value.bit_length() / 8) or 1
1697 octets = value.to_bytes(
1702 except OverflowError:
1706 return b"".join((self.tag, len_encode(len(octets)), octets))
1708 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1710 t, _, lv = tag_strip(tlv)
1711 except DecodeError as err:
1712 raise err.__class__(
1714 klass=self.__class__,
1715 decode_path=decode_path,
1720 klass=self.__class__,
1721 decode_path=decode_path,
1727 l, llen, v = len_decode(lv)
1728 except DecodeError as err:
1729 raise err.__class__(
1731 klass=self.__class__,
1732 decode_path=decode_path,
1736 raise NotEnoughData(
1737 "encoded length is longer than data",
1738 klass=self.__class__,
1739 decode_path=decode_path,
1743 raise NotEnoughData(
1745 klass=self.__class__,
1746 decode_path=decode_path,
1749 v, tail = v[:l], v[l:]
1750 first_octet = byte2int(v)
1752 second_octet = byte2int(v[1:])
1754 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
1755 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
1758 "non normalized integer",
1759 klass=self.__class__,
1760 decode_path=decode_path,
1765 if first_octet & 0x80 > 0:
1766 octets = bytearray()
1767 for octet in bytearray(v):
1768 octets.append(octet ^ 0xFF)
1769 for octet in octets:
1770 value = (value << 8) | octet
1774 for octet in bytearray(v):
1775 value = (value << 8) | octet
1777 value = int.from_bytes(v, byteorder="big", signed=True)
1779 obj = self.__class__(
1781 bounds=(self._bound_min, self._bound_max),
1784 default=self.default,
1785 optional=self.optional,
1787 _decoded=(offset, llen, l),
1789 except BoundsError as err:
1792 klass=self.__class__,
1793 decode_path=decode_path,
1799 return pp_console_row(next(self.pps()))
1801 def pps(self, decode_path=()):
1803 asn1_type_name=self.asn1_type_name,
1804 obj_name=self.__class__.__name__,
1805 decode_path=decode_path,
1806 value=(self.named or str(self._value)) if self.ready else None,
1807 optional=self.optional,
1808 default=self == self.default,
1809 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1810 expl=None if self._expl is None else tag_decode(self._expl),
1815 expl_offset=self.expl_offset if self.expled else None,
1816 expl_tlen=self.expl_tlen if self.expled else None,
1817 expl_llen=self.expl_llen if self.expled else None,
1818 expl_vlen=self.expl_vlen if self.expled else None,
1822 class BitString(Obj):
1823 """``BIT STRING`` bit string type
1825 >>> BitString(b"hello world")
1826 BIT STRING 88 bits 68656c6c6f20776f726c64
1829 >>> b == b"hello world"
1834 >>> b = BitString("'010110000000'B")
1835 BIT STRING 12 bits 5800
1838 >>> b[0], b[1], b[2], b[3]
1839 (False, True, False, True)
1843 [False, True, False, True, True, False, False, False, False, False, False, False]
1847 class KeyUsage(BitString):
1849 ("digitalSignature", 0),
1850 ("nonRepudiation", 1),
1851 ("keyEncipherment", 2),
1854 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
1855 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
1857 ['nonRepudiation', 'keyEncipherment']
1859 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
1861 __slots__ = ("tag_constructed", "specs", "defined")
1862 tag_default = tag_encode(3)
1863 asn1_type_name = "BIT STRING"
1876 :param value: set the value. Either binary type, tuple of named
1877 values (if ``schema`` is specified in the class),
1878 string in ``'XXX...'B`` form, or
1879 :py:class:`pyderasn.BitString` object
1880 :param bytes impl: override default tag with ``IMPLICIT`` one
1881 :param bytes expl: override default tag with ``EXPLICIT`` one
1882 :param default: set default value. Type same as in ``value``
1883 :param bool optional: is object ``OPTIONAL`` in sequence
1885 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
1886 specs = getattr(self, "schema", {}) if _specs is None else _specs
1887 self.specs = specs if isinstance(specs, dict) else dict(specs)
1888 self._value = None if value is None else self._value_sanitize(value)
1889 if default is not None:
1890 default = self._value_sanitize(default)
1891 self.default = self.__class__(
1897 self._value = default
1899 tag_klass, _, tag_num = tag_decode(self.tag)
1900 self.tag_constructed = tag_encode(
1902 form=TagFormConstructed,
1906 def _bits2octets(self, bits):
1907 if len(self.specs) > 0:
1908 bits = bits.rstrip("0")
1910 bits += "0" * ((8 - (bit_len % 8)) % 8)
1911 octets = bytearray(len(bits) // 8)
1912 for i in six_xrange(len(octets)):
1913 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
1914 return bit_len, bytes(octets)
1916 def _value_sanitize(self, value):
1917 if issubclass(value.__class__, BitString):
1919 if isinstance(value, (string_types, binary_type)):
1921 isinstance(value, string_types) and
1922 value.startswith("'") and
1923 value.endswith("'B")
1926 if not set(value) <= set(("0", "1")):
1927 raise ValueError("B's coding contains unacceptable chars")
1928 return self._bits2octets(value)
1929 elif isinstance(value, binary_type):
1930 return (len(value) * 8, value)
1932 raise InvalidValueType((
1937 if isinstance(value, tuple):
1940 isinstance(value[0], integer_types) and
1941 isinstance(value[1], binary_type)
1946 bit = self.specs.get(name)
1948 raise ObjUnknown("BitString value: %s" % name)
1951 return self._bits2octets("")
1953 return self._bits2octets("".join(
1954 ("1" if bit in bits else "0")
1955 for bit in six_xrange(max(bits) + 1)
1957 raise InvalidValueType((self.__class__, binary_type, string_types))
1961 return self._value is not None
1964 obj = self.__class__(_specs=self.specs)
1966 if value is not None:
1967 value = (value[0], value[1])
1970 obj._expl = self._expl
1971 obj.default = self.default
1972 obj.optional = self.optional
1973 obj.offset = self.offset
1974 obj.llen = self.llen
1975 obj.vlen = self.vlen
1979 self._assert_ready()
1980 for i in six_xrange(self._value[0]):
1985 self._assert_ready()
1986 return self._value[0]
1988 def __bytes__(self):
1989 self._assert_ready()
1990 return self._value[1]
1992 def __eq__(self, their):
1993 if isinstance(their, bytes):
1994 return self._value[1] == their
1995 if not issubclass(their.__class__, BitString):
1998 self._value == their._value and
1999 self.tag == their.tag and
2000 self._expl == their._expl
2005 return [name for name, bit in self.specs.items() if self[bit]]
2015 return self.__class__(
2017 impl=self.tag if impl is None else impl,
2018 expl=self._expl if expl is None else expl,
2019 default=self.default if default is None else default,
2020 optional=self.optional if optional is None else optional,
2024 def __getitem__(self, key):
2025 if isinstance(key, int):
2026 bit_len, octets = self._value
2030 byte2int(memoryview(octets)[key // 8:]) >>
2033 if isinstance(key, string_types):
2034 value = self.specs.get(key)
2036 raise ObjUnknown("BitString value: %s" % key)
2038 raise InvalidValueType((int, str))
2041 self._assert_ready()
2042 bit_len, octets = self._value
2045 len_encode(len(octets) + 1),
2046 int2byte((8 - bit_len % 8) % 8),
2050 def _decode_chunk(self, lv, offset, decode_path, ctx):
2052 l, llen, v = len_decode(lv)
2053 except DecodeError as err:
2054 raise err.__class__(
2056 klass=self.__class__,
2057 decode_path=decode_path,
2061 raise NotEnoughData(
2062 "encoded length is longer than data",
2063 klass=self.__class__,
2064 decode_path=decode_path,
2068 raise NotEnoughData(
2070 klass=self.__class__,
2071 decode_path=decode_path,
2074 pad_size = byte2int(v)
2075 if l == 1 and pad_size != 0:
2077 "invalid empty value",
2078 klass=self.__class__,
2079 decode_path=decode_path,
2085 klass=self.__class__,
2086 decode_path=decode_path,
2089 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2092 klass=self.__class__,
2093 decode_path=decode_path,
2096 v, tail = v[:l], v[l:]
2097 obj = self.__class__(
2098 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2101 default=self.default,
2102 optional=self.optional,
2104 _decoded=(offset, llen, l),
2108 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2110 t, tlen, lv = tag_strip(tlv)
2111 except DecodeError as err:
2112 raise err.__class__(
2114 klass=self.__class__,
2115 decode_path=decode_path,
2121 return self._decode_chunk(lv, offset, decode_path, ctx)
2122 if t == self.tag_constructed:
2123 if not ctx.get("bered", False):
2125 msg="unallowed BER constructed encoding",
2126 decode_path=decode_path,
2133 l, llen, v = len_decode(lv)
2134 except LenIndefForm:
2135 llen, l, v = 1, 0, lv[1:]
2137 except DecodeError as err:
2138 raise err.__class__(
2140 klass=self.__class__,
2141 decode_path=decode_path,
2144 if l > 0 and l > len(v):
2145 raise NotEnoughData(
2146 "encoded length is longer than data",
2147 klass=self.__class__,
2148 decode_path=decode_path,
2151 if not lenindef and l == 0:
2152 raise NotEnoughData(
2154 klass=self.__class__,
2155 decode_path=decode_path,
2159 sub_offset = offset + tlen + llen
2163 if v[:EOC_LEN].tobytes() == EOC:
2170 msg="chunk out of bounds",
2171 decode_path=len(chunks) - 1,
2172 offset=chunks[-1].offset,
2174 sub_decode_path = decode_path + (str(len(chunks)),)
2176 chunk, v_tail = BitString().decode(
2179 decode_path=sub_decode_path,
2185 msg="expected BitString encoded chunk",
2186 decode_path=sub_decode_path,
2189 chunks.append(chunk)
2190 sub_offset += chunk.tlvlen
2191 vlen += chunk.tlvlen
2193 if len(chunks) == 0:
2196 decode_path=decode_path,
2201 for chunk_i, chunk in enumerate(chunks[:-1]):
2202 if chunk.bit_len % 8 != 0:
2204 msg="BitString chunk is not multiple of 8 bit",
2205 decode_path=decode_path + (str(chunk_i),),
2206 offset=chunk.offset,
2208 values.append(bytes(chunk))
2209 bit_len += chunk.bit_len
2210 chunk_last = chunks[-1]
2211 values.append(bytes(chunk_last))
2212 bit_len += chunk_last.bit_len
2213 obj = self.__class__(
2214 value=(bit_len, b"".join(values)),
2217 default=self.default,
2218 optional=self.optional,
2220 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2222 obj.lenindef = lenindef
2224 return obj, (v[EOC_LEN:] if lenindef else v)
2226 klass=self.__class__,
2227 decode_path=decode_path,
2232 return pp_console_row(next(self.pps()))
2234 def pps(self, decode_path=()):
2238 bit_len, blob = self._value
2239 value = "%d bits" % bit_len
2240 if len(self.specs) > 0:
2241 blob = tuple(self.named)
2243 asn1_type_name=self.asn1_type_name,
2244 obj_name=self.__class__.__name__,
2245 decode_path=decode_path,
2248 optional=self.optional,
2249 default=self == self.default,
2250 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2251 expl=None if self._expl is None else tag_decode(self._expl),
2256 expl_offset=self.expl_offset if self.expled else None,
2257 expl_tlen=self.expl_tlen if self.expled else None,
2258 expl_llen=self.expl_llen if self.expled else None,
2259 expl_vlen=self.expl_vlen if self.expled else None,
2261 defined_by, defined = self.defined or (None, None)
2262 if defined_by is not None:
2264 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2268 class OctetString(Obj):
2269 """``OCTET STRING`` binary string type
2271 >>> s = OctetString(b"hello world")
2272 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2273 >>> s == OctetString(b"hello world")
2278 >>> OctetString(b"hello", bounds=(4, 4))
2279 Traceback (most recent call last):
2280 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2281 >>> OctetString(b"hell", bounds=(4, 4))
2282 OCTET STRING 4 bytes 68656c6c
2284 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2285 tag_default = tag_encode(4)
2286 asn1_type_name = "OCTET STRING"
2299 :param value: set the value. Either binary type, or
2300 :py:class:`pyderasn.OctetString` object
2301 :param bounds: set ``(MIN, MAX)`` value size constraint.
2302 (-inf, +inf) by default
2303 :param bytes impl: override default tag with ``IMPLICIT`` one
2304 :param bytes expl: override default tag with ``EXPLICIT`` one
2305 :param default: set default value. Type same as in ``value``
2306 :param bool optional: is object ``OPTIONAL`` in sequence
2308 super(OctetString, self).__init__(
2316 self._bound_min, self._bound_max = getattr(
2320 ) if bounds is None else bounds
2321 if value is not None:
2322 self._value = self._value_sanitize(value)
2323 if default is not None:
2324 default = self._value_sanitize(default)
2325 self.default = self.__class__(
2330 if self._value is None:
2331 self._value = default
2333 tag_klass, _, tag_num = tag_decode(self.tag)
2334 self.tag_constructed = tag_encode(
2336 form=TagFormConstructed,
2340 def _value_sanitize(self, value):
2341 if issubclass(value.__class__, OctetString):
2342 value = value._value
2343 elif isinstance(value, binary_type):
2346 raise InvalidValueType((self.__class__, bytes))
2347 if not self._bound_min <= len(value) <= self._bound_max:
2348 raise BoundsError(self._bound_min, len(value), self._bound_max)
2353 return self._value is not None
2356 obj = self.__class__()
2357 obj._value = self._value
2358 obj._bound_min = self._bound_min
2359 obj._bound_max = self._bound_max
2361 obj._expl = self._expl
2362 obj.default = self.default
2363 obj.optional = self.optional
2364 obj.offset = self.offset
2365 obj.llen = self.llen
2366 obj.vlen = self.vlen
2369 def __bytes__(self):
2370 self._assert_ready()
2373 def __eq__(self, their):
2374 if isinstance(their, binary_type):
2375 return self._value == their
2376 if not issubclass(their.__class__, OctetString):
2379 self._value == their._value and
2380 self.tag == their.tag and
2381 self._expl == their._expl
2384 def __lt__(self, their):
2385 return self._value < their._value
2396 return self.__class__(
2399 (self._bound_min, self._bound_max)
2400 if bounds is None else bounds
2402 impl=self.tag if impl is None else impl,
2403 expl=self._expl if expl is None else expl,
2404 default=self.default if default is None else default,
2405 optional=self.optional if optional is None else optional,
2409 self._assert_ready()
2412 len_encode(len(self._value)),
2416 def _decode_chunk(self, lv, offset, decode_path, ctx):
2418 l, llen, v = len_decode(lv)
2419 except DecodeError as err:
2420 raise err.__class__(
2422 klass=self.__class__,
2423 decode_path=decode_path,
2427 raise NotEnoughData(
2428 "encoded length is longer than data",
2429 klass=self.__class__,
2430 decode_path=decode_path,
2433 v, tail = v[:l], v[l:]
2435 obj = self.__class__(
2437 bounds=(self._bound_min, self._bound_max),
2440 default=self.default,
2441 optional=self.optional,
2442 _decoded=(offset, llen, l),
2444 except DecodeError as err:
2447 klass=self.__class__,
2448 decode_path=decode_path,
2451 except BoundsError as err:
2454 klass=self.__class__,
2455 decode_path=decode_path,
2460 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2462 t, tlen, lv = tag_strip(tlv)
2463 except DecodeError as err:
2464 raise err.__class__(
2466 klass=self.__class__,
2467 decode_path=decode_path,
2473 return self._decode_chunk(lv, offset, decode_path, ctx)
2474 if t == self.tag_constructed:
2475 if not ctx.get("bered", False):
2477 msg="unallowed BER constructed encoding",
2478 decode_path=decode_path,
2485 l, llen, v = len_decode(lv)
2486 except LenIndefForm:
2487 llen, l, v = 1, 0, lv[1:]
2489 except DecodeError as err:
2490 raise err.__class__(
2492 klass=self.__class__,
2493 decode_path=decode_path,
2496 if l > 0 and l > len(v):
2497 raise NotEnoughData(
2498 "encoded length is longer than data",
2499 klass=self.__class__,
2500 decode_path=decode_path,
2503 if not lenindef and l == 0:
2504 raise NotEnoughData(
2506 klass=self.__class__,
2507 decode_path=decode_path,
2511 sub_offset = offset + tlen + llen
2515 if v[:EOC_LEN].tobytes() == EOC:
2522 msg="chunk out of bounds",
2523 decode_path=len(chunks) - 1,
2524 offset=chunks[-1].offset,
2526 sub_decode_path = decode_path + (str(len(chunks)),)
2528 chunk, v_tail = OctetString().decode(
2531 decode_path=sub_decode_path,
2537 msg="expected OctetString encoded chunk",
2538 decode_path=sub_decode_path,
2541 chunks.append(chunk)
2542 sub_offset += chunk.tlvlen
2543 vlen += chunk.tlvlen
2545 if len(chunks) == 0:
2548 decode_path=decode_path,
2552 obj = self.__class__(
2553 value=b"".join(bytes(chunk) for chunk in chunks),
2554 bounds=(self._bound_min, self._bound_max),
2557 default=self.default,
2558 optional=self.optional,
2559 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2561 except DecodeError as err:
2564 klass=self.__class__,
2565 decode_path=decode_path,
2568 except BoundsError as err:
2571 klass=self.__class__,
2572 decode_path=decode_path,
2575 obj.lenindef = lenindef
2577 return obj, (v[EOC_LEN:] if lenindef else v)
2579 klass=self.__class__,
2580 decode_path=decode_path,
2585 return pp_console_row(next(self.pps()))
2587 def pps(self, decode_path=()):
2589 asn1_type_name=self.asn1_type_name,
2590 obj_name=self.__class__.__name__,
2591 decode_path=decode_path,
2592 value=("%d bytes" % len(self._value)) if self.ready else None,
2593 blob=self._value if self.ready else None,
2594 optional=self.optional,
2595 default=self == self.default,
2596 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2597 expl=None if self._expl is None else tag_decode(self._expl),
2602 expl_offset=self.expl_offset if self.expled else None,
2603 expl_tlen=self.expl_tlen if self.expled else None,
2604 expl_llen=self.expl_llen if self.expled else None,
2605 expl_vlen=self.expl_vlen if self.expled else None,
2607 defined_by, defined = self.defined or (None, None)
2608 if defined_by is not None:
2610 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2615 """``NULL`` null object
2623 tag_default = tag_encode(5)
2624 asn1_type_name = "NULL"
2628 value=None, # unused, but Sequence passes it
2635 :param bytes impl: override default tag with ``IMPLICIT`` one
2636 :param bytes expl: override default tag with ``EXPLICIT`` one
2637 :param bool optional: is object ``OPTIONAL`` in sequence
2639 super(Null, self).__init__(impl, expl, None, optional, _decoded)
2647 obj = self.__class__()
2649 obj._expl = self._expl
2650 obj.default = self.default
2651 obj.optional = self.optional
2652 obj.offset = self.offset
2653 obj.llen = self.llen
2654 obj.vlen = self.vlen
2657 def __eq__(self, their):
2658 if not issubclass(their.__class__, Null):
2661 self.tag == their.tag and
2662 self._expl == their._expl
2672 return self.__class__(
2673 impl=self.tag if impl is None else impl,
2674 expl=self._expl if expl is None else expl,
2675 optional=self.optional if optional is None else optional,
2679 return self.tag + len_encode(0)
2681 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2683 t, _, lv = tag_strip(tlv)
2684 except DecodeError as err:
2685 raise err.__class__(
2687 klass=self.__class__,
2688 decode_path=decode_path,
2693 klass=self.__class__,
2694 decode_path=decode_path,
2700 l, _, v = len_decode(lv)
2701 except DecodeError as err:
2702 raise err.__class__(
2704 klass=self.__class__,
2705 decode_path=decode_path,
2709 raise InvalidLength(
2710 "Null must have zero length",
2711 klass=self.__class__,
2712 decode_path=decode_path,
2715 obj = self.__class__(
2718 optional=self.optional,
2719 _decoded=(offset, 1, 0),
2724 return pp_console_row(next(self.pps()))
2726 def pps(self, decode_path=()):
2728 asn1_type_name=self.asn1_type_name,
2729 obj_name=self.__class__.__name__,
2730 decode_path=decode_path,
2731 optional=self.optional,
2732 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2733 expl=None if self._expl is None else tag_decode(self._expl),
2738 expl_offset=self.expl_offset if self.expled else None,
2739 expl_tlen=self.expl_tlen if self.expled else None,
2740 expl_llen=self.expl_llen if self.expled else None,
2741 expl_vlen=self.expl_vlen if self.expled else None,
2745 class ObjectIdentifier(Obj):
2746 """``OBJECT IDENTIFIER`` OID type
2748 >>> oid = ObjectIdentifier((1, 2, 3))
2749 OBJECT IDENTIFIER 1.2.3
2750 >>> oid == ObjectIdentifier("1.2.3")
2756 >>> oid + (4, 5) + ObjectIdentifier("1.7")
2757 OBJECT IDENTIFIER 1.2.3.4.5.1.7
2759 >>> str(ObjectIdentifier((3, 1)))
2760 Traceback (most recent call last):
2761 pyderasn.InvalidOID: unacceptable first arc value
2763 __slots__ = ("defines",)
2764 tag_default = tag_encode(6)
2765 asn1_type_name = "OBJECT IDENTIFIER"
2778 :param value: set the value. Either tuples of integers,
2779 string of "."-concatenated integers, or
2780 :py:class:`pyderasn.ObjectIdentifier` object
2781 :param defines: sequence of tuples. Each tuple has two elements.
2782 First one is relative to current one decode
2783 path, aiming to the field defined by that OID.
2784 Read about relative path in
2785 :py:func:`pyderasn.abs_decode_path`. Second
2786 tuple element is ``{OID: pyderasn.Obj()}``
2787 dictionary, mapping between current OID value
2788 and structure applied to defined field.
2789 :ref:`Read about DEFINED BY <definedby>`
2790 :param bytes impl: override default tag with ``IMPLICIT`` one
2791 :param bytes expl: override default tag with ``EXPLICIT`` one
2792 :param default: set default value. Type same as in ``value``
2793 :param bool optional: is object ``OPTIONAL`` in sequence
2795 super(ObjectIdentifier, self).__init__(
2803 if value is not None:
2804 self._value = self._value_sanitize(value)
2805 if default is not None:
2806 default = self._value_sanitize(default)
2807 self.default = self.__class__(
2812 if self._value is None:
2813 self._value = default
2814 self.defines = defines
2816 def __add__(self, their):
2817 if isinstance(their, self.__class__):
2818 return self.__class__(self._value + their._value)
2819 if isinstance(their, tuple):
2820 return self.__class__(self._value + their)
2821 raise InvalidValueType((self.__class__, tuple))
2823 def _value_sanitize(self, value):
2824 if issubclass(value.__class__, ObjectIdentifier):
2826 if isinstance(value, string_types):
2828 value = tuple(int(arc) for arc in value.split("."))
2830 raise InvalidOID("unacceptable arcs values")
2831 if isinstance(value, tuple):
2833 raise InvalidOID("less than 2 arcs")
2834 first_arc = value[0]
2835 if first_arc in (0, 1):
2836 if not (0 <= value[1] <= 39):
2837 raise InvalidOID("second arc is too wide")
2838 elif first_arc == 2:
2841 raise InvalidOID("unacceptable first arc value")
2843 raise InvalidValueType((self.__class__, str, tuple))
2847 return self._value is not None
2850 obj = self.__class__()
2851 obj._value = self._value
2852 obj.defines = self.defines
2854 obj._expl = self._expl
2855 obj.default = self.default
2856 obj.optional = self.optional
2857 obj.offset = self.offset
2858 obj.llen = self.llen
2859 obj.vlen = self.vlen
2863 self._assert_ready()
2864 return iter(self._value)
2867 return ".".join(str(arc) for arc in self._value or ())
2870 self._assert_ready()
2873 bytes(self._expl or b"") +
2874 str(self._value).encode("ascii"),
2877 def __eq__(self, their):
2878 if isinstance(their, tuple):
2879 return self._value == their
2880 if not issubclass(their.__class__, ObjectIdentifier):
2883 self.tag == their.tag and
2884 self._expl == their._expl and
2885 self._value == their._value
2888 def __lt__(self, their):
2889 return self._value < their._value
2900 return self.__class__(
2902 defines=self.defines if defines is None else defines,
2903 impl=self.tag if impl is None else impl,
2904 expl=self._expl if expl is None else expl,
2905 default=self.default if default is None else default,
2906 optional=self.optional if optional is None else optional,
2910 self._assert_ready()
2912 first_value = value[1]
2913 first_arc = value[0]
2916 elif first_arc == 1:
2918 elif first_arc == 2:
2920 else: # pragma: no cover
2921 raise RuntimeError("invalid arc is stored")
2922 octets = [zero_ended_encode(first_value)]
2923 for arc in value[2:]:
2924 octets.append(zero_ended_encode(arc))
2925 v = b"".join(octets)
2926 return b"".join((self.tag, len_encode(len(v)), v))
2928 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2930 t, _, lv = tag_strip(tlv)
2931 except DecodeError as err:
2932 raise err.__class__(
2934 klass=self.__class__,
2935 decode_path=decode_path,
2940 klass=self.__class__,
2941 decode_path=decode_path,
2947 l, llen, v = len_decode(lv)
2948 except DecodeError as err:
2949 raise err.__class__(
2951 klass=self.__class__,
2952 decode_path=decode_path,
2956 raise NotEnoughData(
2957 "encoded length is longer than data",
2958 klass=self.__class__,
2959 decode_path=decode_path,
2963 raise NotEnoughData(
2965 klass=self.__class__,
2966 decode_path=decode_path,
2969 v, tail = v[:l], v[l:]
2975 octet = indexbytes(v, i)
2976 arc = (arc << 7) | (octet & 0x7F)
2977 if octet & 0x80 == 0:
2985 klass=self.__class__,
2986 decode_path=decode_path,
2990 second_arc = arcs[0]
2991 if 0 <= second_arc <= 39:
2993 elif 40 <= second_arc <= 79:
2999 obj = self.__class__(
3000 value=tuple([first_arc, second_arc] + arcs[1:]),
3003 default=self.default,
3004 optional=self.optional,
3005 _decoded=(offset, llen, l),
3010 return pp_console_row(next(self.pps()))
3012 def pps(self, decode_path=()):
3014 asn1_type_name=self.asn1_type_name,
3015 obj_name=self.__class__.__name__,
3016 decode_path=decode_path,
3017 value=str(self) if self.ready else None,
3018 optional=self.optional,
3019 default=self == self.default,
3020 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3021 expl=None if self._expl is None else tag_decode(self._expl),
3026 expl_offset=self.expl_offset if self.expled else None,
3027 expl_tlen=self.expl_tlen if self.expled else None,
3028 expl_llen=self.expl_llen if self.expled else None,
3029 expl_vlen=self.expl_vlen if self.expled else None,
3033 class Enumerated(Integer):
3034 """``ENUMERATED`` integer type
3036 This type is identical to :py:class:`pyderasn.Integer`, but requires
3037 schema to be specified and does not accept values missing from it.
3040 tag_default = tag_encode(10)
3041 asn1_type_name = "ENUMERATED"
3052 bounds=None, # dummy argument, workability for Integer.decode
3054 super(Enumerated, self).__init__(
3063 if len(self.specs) == 0:
3064 raise ValueError("schema must be specified")
3066 def _value_sanitize(self, value):
3067 if isinstance(value, self.__class__):
3068 value = value._value
3069 elif isinstance(value, integer_types):
3070 if value not in list(self.specs.values()):
3072 "unknown integer value: %s" % value,
3073 klass=self.__class__,
3075 elif isinstance(value, string_types):
3076 value = self.specs.get(value)
3078 raise ObjUnknown("integer value: %s" % value)
3080 raise InvalidValueType((self.__class__, int, str))
3084 obj = self.__class__(_specs=self.specs)
3085 obj._value = self._value
3086 obj._bound_min = self._bound_min
3087 obj._bound_max = self._bound_max
3089 obj._expl = self._expl
3090 obj.default = self.default
3091 obj.optional = self.optional
3092 obj.offset = self.offset
3093 obj.llen = self.llen
3094 obj.vlen = self.vlen
3106 return self.__class__(
3108 impl=self.tag if impl is None else impl,
3109 expl=self._expl if expl is None else expl,
3110 default=self.default if default is None else default,
3111 optional=self.optional if optional is None else optional,
3116 class CommonString(OctetString):
3117 """Common class for all strings
3119 Everything resembles :py:class:`pyderasn.OctetString`, except
3120 ability to deal with unicode text strings.
3122 >>> hexenc("привет мир".encode("utf-8"))
3123 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3124 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3126 >>> s = UTF8String("привет мир")
3127 UTF8String UTF8String привет мир
3129 'привет мир'
3130 >>> hexenc(bytes(s))
3131 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3133 >>> PrintableString("привет мир")
3134 Traceback (most recent call last):
3135 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3137 >>> BMPString("ада", bounds=(2, 2))
3138 Traceback (most recent call last):
3139 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3140 >>> s = BMPString("ад", bounds=(2, 2))
3143 >>> hexenc(bytes(s))
3151 * - :py:class:`pyderasn.UTF8String`
3153 * - :py:class:`pyderasn.NumericString`
3155 * - :py:class:`pyderasn.PrintableString`
3157 * - :py:class:`pyderasn.TeletexString`
3159 * - :py:class:`pyderasn.T61String`
3161 * - :py:class:`pyderasn.VideotexString`
3163 * - :py:class:`pyderasn.IA5String`
3165 * - :py:class:`pyderasn.GraphicString`
3167 * - :py:class:`pyderasn.VisibleString`
3169 * - :py:class:`pyderasn.ISO646String`
3171 * - :py:class:`pyderasn.GeneralString`
3173 * - :py:class:`pyderasn.UniversalString`
3175 * - :py:class:`pyderasn.BMPString`
3178 __slots__ = ("encoding",)
3180 def _value_sanitize(self, value):
3182 value_decoded = None
3183 if isinstance(value, self.__class__):
3184 value_raw = value._value
3185 elif isinstance(value, text_type):
3186 value_decoded = value
3187 elif isinstance(value, binary_type):
3190 raise InvalidValueType((self.__class__, text_type, binary_type))
3193 value_decoded.encode(self.encoding)
3194 if value_raw is None else value_raw
3197 value_raw.decode(self.encoding)
3198 if value_decoded is None else value_decoded
3200 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3201 raise DecodeError(str(err))
3202 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3210 def __eq__(self, their):
3211 if isinstance(their, binary_type):
3212 return self._value == their
3213 if isinstance(their, text_type):
3214 return self._value == their.encode(self.encoding)
3215 if not isinstance(their, self.__class__):
3218 self._value == their._value and
3219 self.tag == their.tag and
3220 self._expl == their._expl
3223 def __unicode__(self):
3225 return self._value.decode(self.encoding)
3226 return text_type(self._value)
3229 return pp_console_row(next(self.pps(no_unicode=PY2)))
3231 def pps(self, decode_path=(), no_unicode=False):
3234 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3236 asn1_type_name=self.asn1_type_name,
3237 obj_name=self.__class__.__name__,
3238 decode_path=decode_path,
3240 optional=self.optional,
3241 default=self == self.default,
3242 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3243 expl=None if self._expl is None else tag_decode(self._expl),
3248 expl_offset=self.expl_offset if self.expled else None,
3249 expl_tlen=self.expl_tlen if self.expled else None,
3250 expl_llen=self.expl_llen if self.expled else None,
3251 expl_vlen=self.expl_vlen if self.expled else None,
3255 class UTF8String(CommonString):
3257 tag_default = tag_encode(12)
3259 asn1_type_name = "UTF8String"
3262 class NumericString(CommonString):
3264 tag_default = tag_encode(18)
3266 asn1_type_name = "NumericString"
3267 allowable_chars = set(digits.encode("ascii"))
3269 def _value_sanitize(self, value):
3270 value = super(NumericString, self)._value_sanitize(value)
3271 if not set(value) <= self.allowable_chars:
3272 raise DecodeError("non-numeric value")
3276 class PrintableString(CommonString):
3278 tag_default = tag_encode(19)
3280 asn1_type_name = "PrintableString"
3283 class TeletexString(CommonString):
3285 tag_default = tag_encode(20)
3287 asn1_type_name = "TeletexString"
3290 class T61String(TeletexString):
3292 asn1_type_name = "T61String"
3295 class VideotexString(CommonString):
3297 tag_default = tag_encode(21)
3298 encoding = "iso-8859-1"
3299 asn1_type_name = "VideotexString"
3302 class IA5String(CommonString):
3304 tag_default = tag_encode(22)
3306 asn1_type_name = "IA5"
3309 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3310 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3311 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3314 class UTCTime(CommonString):
3315 """``UTCTime`` datetime type
3317 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3318 UTCTime UTCTime 2017-09-30T22:07:50
3324 datetime.datetime(2017, 9, 30, 22, 7, 50)
3325 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3326 datetime.datetime(1957, 9, 30, 22, 7, 50)
3329 tag_default = tag_encode(23)
3331 asn1_type_name = "UTCTime"
3333 fmt = "%y%m%d%H%M%SZ"
3343 bounds=None, # dummy argument, workability for OctetString.decode
3346 :param value: set the value. Either datetime type, or
3347 :py:class:`pyderasn.UTCTime` object
3348 :param bytes impl: override default tag with ``IMPLICIT`` one
3349 :param bytes expl: override default tag with ``EXPLICIT`` one
3350 :param default: set default value. Type same as in ``value``
3351 :param bool optional: is object ``OPTIONAL`` in sequence
3353 super(UTCTime, self).__init__(
3361 if value is not None:
3362 self._value = self._value_sanitize(value)
3363 if default is not None:
3364 default = self._value_sanitize(default)
3365 self.default = self.__class__(
3370 if self._value is None:
3371 self._value = default
3373 def _value_sanitize(self, value):
3374 if isinstance(value, self.__class__):
3376 if isinstance(value, datetime):
3377 return value.strftime(self.fmt).encode("ascii")
3378 if isinstance(value, binary_type):
3379 value_decoded = value.decode("ascii")
3380 if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
3382 datetime.strptime(value_decoded, self.fmt)
3384 raise DecodeError("invalid UTCTime format")
3387 raise DecodeError("invalid UTCTime length")
3388 raise InvalidValueType((self.__class__, datetime))
3390 def __eq__(self, their):
3391 if isinstance(their, binary_type):
3392 return self._value == their
3393 if isinstance(their, datetime):
3394 return self.todatetime() == their
3395 if not isinstance(their, self.__class__):
3398 self._value == their._value and
3399 self.tag == their.tag and
3400 self._expl == their._expl
3403 def todatetime(self):
3404 """Convert to datetime
3408 Pay attention that UTCTime can not hold full year, so all years
3409 having < 50 years are treated as 20xx, 19xx otherwise, according
3410 to X.509 recomendation.
3412 value = datetime.strptime(self._value.decode("ascii"), self.fmt)
3413 year = value.year % 100
3415 year=(2000 + year) if year < 50 else (1900 + year),
3419 minute=value.minute,
3420 second=value.second,
3424 return pp_console_row(next(self.pps()))
3426 def pps(self, decode_path=()):
3428 asn1_type_name=self.asn1_type_name,
3429 obj_name=self.__class__.__name__,
3430 decode_path=decode_path,
3431 value=self.todatetime().isoformat() if self.ready else None,
3432 optional=self.optional,
3433 default=self == self.default,
3434 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3435 expl=None if self._expl is None else tag_decode(self._expl),
3440 expl_offset=self.expl_offset if self.expled else None,
3441 expl_tlen=self.expl_tlen if self.expled else None,
3442 expl_llen=self.expl_llen if self.expled else None,
3443 expl_vlen=self.expl_vlen if self.expled else None,
3447 class GeneralizedTime(UTCTime):
3448 """``GeneralizedTime`` datetime type
3450 This type is similar to :py:class:`pyderasn.UTCTime`.
3452 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3453 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3455 '20170930220750.000123Z'
3456 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3457 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3460 tag_default = tag_encode(24)
3461 asn1_type_name = "GeneralizedTime"
3463 fmt = "%Y%m%d%H%M%SZ"
3464 fmt_ms = "%Y%m%d%H%M%S.%fZ"
3466 def _value_sanitize(self, value):
3467 if isinstance(value, self.__class__):
3469 if isinstance(value, datetime):
3470 return value.strftime(
3471 self.fmt_ms if value.microsecond > 0 else self.fmt
3473 if isinstance(value, binary_type):
3474 value_decoded = value.decode("ascii")
3475 if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
3477 datetime.strptime(value_decoded, self.fmt)
3480 "invalid GeneralizedTime (without ms) format",
3483 elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
3485 datetime.strptime(value_decoded, self.fmt_ms)
3488 "invalid GeneralizedTime (with ms) format",
3493 "invalid GeneralizedTime length",
3494 klass=self.__class__,
3496 raise InvalidValueType((self.__class__, datetime))
3498 def todatetime(self):
3499 value = self._value.decode("ascii")
3500 if len(value) == LEN_YYYYMMDDHHMMSSZ:
3501 return datetime.strptime(value, self.fmt)
3502 return datetime.strptime(value, self.fmt_ms)
3505 class GraphicString(CommonString):
3507 tag_default = tag_encode(25)
3508 encoding = "iso-8859-1"
3509 asn1_type_name = "GraphicString"
3512 class VisibleString(CommonString):
3514 tag_default = tag_encode(26)
3516 asn1_type_name = "VisibleString"
3519 class ISO646String(VisibleString):
3521 asn1_type_name = "ISO646String"
3524 class GeneralString(CommonString):
3526 tag_default = tag_encode(27)
3527 encoding = "iso-8859-1"
3528 asn1_type_name = "GeneralString"
3531 class UniversalString(CommonString):
3533 tag_default = tag_encode(28)
3534 encoding = "utf-32-be"
3535 asn1_type_name = "UniversalString"
3538 class BMPString(CommonString):
3540 tag_default = tag_encode(30)
3541 encoding = "utf-16-be"
3542 asn1_type_name = "BMPString"
3546 """``CHOICE`` special type
3550 class GeneralName(Choice):
3552 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
3553 ("dNSName", IA5String(impl=tag_ctxp(2))),
3556 >>> gn = GeneralName()
3558 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
3559 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3560 >>> gn["dNSName"] = IA5String("bar.baz")
3561 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
3562 >>> gn["rfc822Name"]
3565 [2] IA5String IA5 bar.baz
3568 >>> gn.value == gn["dNSName"]
3571 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
3573 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
3574 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
3576 __slots__ = ("specs",)
3578 asn1_type_name = "CHOICE"
3591 :param value: set the value. Either ``(choice, value)`` tuple, or
3592 :py:class:`pyderasn.Choice` object
3593 :param bytes impl: can not be set, do **not** use it
3594 :param bytes expl: override default tag with ``EXPLICIT`` one
3595 :param default: set default value. Type same as in ``value``
3596 :param bool optional: is object ``OPTIONAL`` in sequence
3598 if impl is not None:
3599 raise ValueError("no implicit tag allowed for CHOICE")
3600 super(Choice, self).__init__(None, expl, default, optional, _decoded)
3602 schema = getattr(self, "schema", ())
3603 if len(schema) == 0:
3604 raise ValueError("schema must be specified")
3606 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
3609 if value is not None:
3610 self._value = self._value_sanitize(value)
3611 if default is not None:
3612 default_value = self._value_sanitize(default)
3613 default_obj = self.__class__(impl=self.tag, expl=self._expl)
3614 default_obj.specs = self.specs
3615 default_obj._value = default_value
3616 self.default = default_obj
3618 self._value = default_obj.copy()._value
3620 def _value_sanitize(self, value):
3621 if isinstance(value, self.__class__):
3623 if isinstance(value, tuple) and len(value) == 2:
3625 spec = self.specs.get(choice)
3627 raise ObjUnknown(choice)
3628 if not isinstance(obj, spec.__class__):
3629 raise InvalidValueType((spec,))
3630 return (choice, spec(obj))
3631 raise InvalidValueType((self.__class__, tuple))
3635 return self._value is not None and self._value[1].ready
3638 obj = self.__class__(schema=self.specs)
3639 obj._expl = self._expl
3640 obj.default = self.default
3641 obj.optional = self.optional
3642 obj.offset = self.offset
3643 obj.llen = self.llen
3644 obj.vlen = self.vlen
3646 if value is not None:
3647 obj._value = (value[0], value[1].copy())
3650 def __eq__(self, their):
3651 if isinstance(their, tuple) and len(their) == 2:
3652 return self._value == their
3653 if not isinstance(their, self.__class__):
3656 self.specs == their.specs and
3657 self._value == their._value
3667 return self.__class__(
3670 expl=self._expl if expl is None else expl,
3671 default=self.default if default is None else default,
3672 optional=self.optional if optional is None else optional,
3677 self._assert_ready()
3678 return self._value[0]
3682 self._assert_ready()
3683 return self._value[1]
3685 def __getitem__(self, key):
3686 if key not in self.specs:
3687 raise ObjUnknown(key)
3688 if self._value is None:
3690 choice, value = self._value
3695 def __setitem__(self, key, value):
3696 spec = self.specs.get(key)
3698 raise ObjUnknown(key)
3699 if not isinstance(value, spec.__class__):
3700 raise InvalidValueType((spec.__class__,))
3701 self._value = (key, spec(value))
3709 return self._value[1].decoded if self.ready else False
3712 self._assert_ready()
3713 return self._value[1].encode()
3715 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3716 for choice, spec in self.specs.items():
3717 sub_decode_path = decode_path + (choice,)
3723 decode_path=sub_decode_path,
3732 klass=self.__class__,
3733 decode_path=decode_path,
3738 value, tail = spec.decode(
3742 decode_path=sub_decode_path,
3745 obj = self.__class__(
3748 default=self.default,
3749 optional=self.optional,
3750 _decoded=(offset, 0, value.tlvlen),
3752 obj._value = (choice, value)
3756 value = pp_console_row(next(self.pps()))
3758 value = "%s[%r]" % (value, self.value)
3761 def pps(self, decode_path=()):
3763 asn1_type_name=self.asn1_type_name,
3764 obj_name=self.__class__.__name__,
3765 decode_path=decode_path,
3766 value=self.choice if self.ready else None,
3767 optional=self.optional,
3768 default=self == self.default,
3769 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3770 expl=None if self._expl is None else tag_decode(self._expl),
3777 yield self.value.pps(decode_path=decode_path + (self.choice,))
3780 class PrimitiveTypes(Choice):
3781 """Predefined ``CHOICE`` for all generic primitive types
3783 It could be useful for general decoding of some unspecified values:
3785 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
3786 OCTET STRING 3 bytes 666f6f
3787 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
3791 schema = tuple((klass.__name__, klass()) for klass in (
3816 """``ANY`` special type
3818 >>> Any(Integer(-123))
3820 >>> a = Any(OctetString(b"hello world").encode())
3821 ANY 040b68656c6c6f20776f726c64
3822 >>> hexenc(bytes(a))
3823 b'0x040x0bhello world'
3825 __slots__ = ("defined",)
3826 tag_default = tag_encode(0)
3827 asn1_type_name = "ANY"
3837 :param value: set the value. Either any kind of pyderasn's
3838 **ready** object, or bytes. Pay attention that
3839 **no** validation is performed is raw binary value
3841 :param bytes expl: override default tag with ``EXPLICIT`` one
3842 :param bool optional: is object ``OPTIONAL`` in sequence
3844 super(Any, self).__init__(None, expl, None, optional, _decoded)
3845 self._value = None if value is None else self._value_sanitize(value)
3848 def _value_sanitize(self, value):
3849 if isinstance(value, self.__class__):
3851 if isinstance(value, Obj):
3852 return value.encode()
3853 if isinstance(value, binary_type):
3855 raise InvalidValueType((self.__class__, Obj, binary_type))
3859 return self._value is not None
3862 obj = self.__class__()
3863 obj._value = self._value
3865 obj._expl = self._expl
3866 obj.optional = self.optional
3867 obj.offset = self.offset
3868 obj.llen = self.llen
3869 obj.vlen = self.vlen
3872 def __eq__(self, their):
3873 if isinstance(their, binary_type):
3874 return self._value == their
3875 if issubclass(their.__class__, Any):
3876 return self._value == their._value
3885 return self.__class__(
3887 expl=self._expl if expl is None else expl,
3888 optional=self.optional if optional is None else optional,
3891 def __bytes__(self):
3892 self._assert_ready()
3900 self._assert_ready()
3903 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3905 t, tlen, lv = tag_strip(tlv)
3906 except DecodeError as err:
3907 raise err.__class__(
3909 klass=self.__class__,
3910 decode_path=decode_path,
3914 l, llen, v = len_decode(lv)
3915 except LenIndefForm as err:
3916 if not ctx.get("bered", False):
3917 raise err.__class__(
3919 klass=self.__class__,
3920 decode_path=decode_path,
3923 llen, vlen, v = 1, 0, lv[1:]
3924 sub_offset = offset + tlen + llen
3927 if v[:EOC_LEN].tobytes() == EOC:
3928 tlvlen = tlen + llen + vlen + EOC_LEN
3929 obj = self.__class__(
3930 value=tlv[:tlvlen].tobytes(),
3932 optional=self.optional,
3933 _decoded=(offset, 0, tlvlen),
3937 return obj, v[EOC_LEN:]
3939 chunk, v = Any().decode(
3942 decode_path=decode_path + (str(chunk_i),),
3946 vlen += chunk.tlvlen
3947 sub_offset += chunk.tlvlen
3949 except DecodeError as err:
3950 raise err.__class__(
3952 klass=self.__class__,
3953 decode_path=decode_path,
3957 raise NotEnoughData(
3958 "encoded length is longer than data",
3959 klass=self.__class__,
3960 decode_path=decode_path,
3963 tlvlen = tlen + llen + l
3964 v, tail = tlv[:tlvlen], v[l:]
3965 obj = self.__class__(
3968 optional=self.optional,
3969 _decoded=(offset, 0, tlvlen),
3975 return pp_console_row(next(self.pps()))
3977 def pps(self, decode_path=()):
3979 asn1_type_name=self.asn1_type_name,
3980 obj_name=self.__class__.__name__,
3981 decode_path=decode_path,
3982 blob=self._value if self.ready else None,
3983 optional=self.optional,
3984 default=self == self.default,
3985 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3986 expl=None if self._expl is None else tag_decode(self._expl),
3991 expl_offset=self.expl_offset if self.expled else None,
3992 expl_tlen=self.expl_tlen if self.expled else None,
3993 expl_llen=self.expl_llen if self.expled else None,
3994 expl_vlen=self.expl_vlen if self.expled else None,
3996 defined_by, defined = self.defined or (None, None)
3997 if defined_by is not None:
3999 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4003 ########################################################################
4004 # ASN.1 constructed types
4005 ########################################################################
4007 def get_def_by_path(defines_by_path, sub_decode_path):
4008 """Get define by decode path
4010 for path, define in defines_by_path:
4011 if len(path) != len(sub_decode_path):
4013 for p1, p2 in zip(path, sub_decode_path):
4014 if (p1 != any) and (p1 != p2):
4020 def abs_decode_path(decode_path, rel_path):
4021 """Create an absolute decode path from current and relative ones
4023 :param decode_path: current decode path, starting point.
4025 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4026 If first tuple's element is "/", then treat it as
4027 an absolute path, ignoring ``decode_path`` as
4028 starting point. Also this tuple can contain ".."
4029 elements, stripping the leading element from
4032 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4033 ("foo", "bar", "baz", "whatever")
4034 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4036 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4039 if rel_path[0] == "/":
4041 if rel_path[0] == "..":
4042 return abs_decode_path(decode_path[:-1], rel_path[1:])
4043 return decode_path + rel_path
4046 class Sequence(Obj):
4047 """``SEQUENCE`` structure type
4049 You have to make specification of sequence::
4051 class Extension(Sequence):
4053 ("extnID", ObjectIdentifier()),
4054 ("critical", Boolean(default=False)),
4055 ("extnValue", OctetString()),
4058 Then, you can work with it as with dictionary.
4060 >>> ext = Extension()
4061 >>> Extension().specs
4063 ('extnID', OBJECT IDENTIFIER),
4064 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4065 ('extnValue', OCTET STRING),
4067 >>> ext["extnID"] = "1.2.3"
4068 Traceback (most recent call last):
4069 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4070 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4072 You can determine if sequence is ready to be encoded:
4077 Traceback (most recent call last):
4078 pyderasn.ObjNotReady: object is not ready: extnValue
4079 >>> ext["extnValue"] = OctetString(b"foobar")
4083 Value you want to assign, must have the same **type** as in
4084 corresponding specification, but it can have different tags,
4085 optional/default attributes -- they will be taken from specification
4088 class TBSCertificate(Sequence):
4090 ("version", Version(expl=tag_ctxc(0), default="v1")),
4093 >>> tbs = TBSCertificate()
4094 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4096 Assign ``None`` to remove value from sequence.
4098 You can set values in Sequence during its initialization:
4100 >>> AlgorithmIdentifier((
4101 ("algorithm", ObjectIdentifier("1.2.3")),
4102 ("parameters", Any(Null()))
4104 AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
4106 You can determine if value exists/set in the sequence and take its value:
4108 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4111 OBJECT IDENTIFIER 1.2.3
4113 But pay attention that if value has default, then it won't be (not
4114 in) in the sequence (because ``DEFAULT`` must not be encoded in
4115 DER), but you can read its value:
4117 >>> "critical" in ext, ext["critical"]
4118 (False, BOOLEAN False)
4119 >>> ext["critical"] = Boolean(True)
4120 >>> "critical" in ext, ext["critical"]
4121 (True, BOOLEAN True)
4123 All defaulted values are always optional.
4125 .. _strict_default_existence_ctx:
4129 When decoded DER contains defaulted value inside, then
4130 technically this is not valid DER encoding. But we allow and pass
4131 it **by default**. Of course reencoding of that kind of DER will
4132 result in different binary representation (validly without
4133 defaulted value inside). You can enable strict defaulted values
4134 existence validation by setting ``"strict_default_existence":
4135 True`` :ref:`context <ctx>` option -- decoding process will raise
4136 an exception if defaulted value is met.
4138 Two sequences are equal if they have equal specification (schema),
4139 implicit/explicit tagging and the same values.
4141 __slots__ = ("specs",)
4142 tag_default = tag_encode(form=TagFormConstructed, num=16)
4143 asn1_type_name = "SEQUENCE"
4155 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4157 schema = getattr(self, "schema", ())
4159 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4162 if value is not None:
4163 if issubclass(value.__class__, Sequence):
4164 self._value = value._value
4165 elif hasattr(value, "__iter__"):
4166 for seq_key, seq_value in value:
4167 self[seq_key] = seq_value
4169 raise InvalidValueType((Sequence,))
4170 if default is not None:
4171 if not issubclass(default.__class__, Sequence):
4172 raise InvalidValueType((Sequence,))
4173 default_value = default._value
4174 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4175 default_obj.specs = self.specs
4176 default_obj._value = default_value
4177 self.default = default_obj
4179 self._value = default_obj.copy()._value
4183 for name, spec in self.specs.items():
4184 value = self._value.get(name)
4195 obj = self.__class__(schema=self.specs)
4197 obj._expl = self._expl
4198 obj.default = self.default
4199 obj.optional = self.optional
4200 obj.offset = self.offset
4201 obj.llen = self.llen
4202 obj.vlen = self.vlen
4203 obj._value = {k: v.copy() for k, v in self._value.items()}
4206 def __eq__(self, their):
4207 if not isinstance(their, self.__class__):
4210 self.specs == their.specs and
4211 self.tag == their.tag and
4212 self._expl == their._expl and
4213 self._value == their._value
4224 return self.__class__(
4227 impl=self.tag if impl is None else impl,
4228 expl=self._expl if expl is None else expl,
4229 default=self.default if default is None else default,
4230 optional=self.optional if optional is None else optional,
4233 def __contains__(self, key):
4234 return key in self._value
4236 def __setitem__(self, key, value):
4237 spec = self.specs.get(key)
4239 raise ObjUnknown(key)
4241 self._value.pop(key, None)
4243 if not isinstance(value, spec.__class__):
4244 raise InvalidValueType((spec.__class__,))
4245 value = spec(value=value)
4246 if spec.default is not None and value == spec.default:
4247 self._value.pop(key, None)
4249 self._value[key] = value
4251 def __getitem__(self, key):
4252 value = self._value.get(key)
4253 if value is not None:
4255 spec = self.specs.get(key)
4257 raise ObjUnknown(key)
4258 if spec.default is not None:
4262 def _encoded_values(self):
4264 for name, spec in self.specs.items():
4265 value = self._value.get(name)
4269 raise ObjNotReady(name)
4270 raws.append(value.encode())
4274 v = b"".join(self._encoded_values())
4275 return b"".join((self.tag, len_encode(len(v)), v))
4277 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4279 t, tlen, lv = tag_strip(tlv)
4280 except DecodeError as err:
4281 raise err.__class__(
4283 klass=self.__class__,
4284 decode_path=decode_path,
4289 klass=self.__class__,
4290 decode_path=decode_path,
4297 l, llen, v = len_decode(lv)
4298 except LenIndefForm as err:
4299 if not ctx.get("bered", False):
4300 raise err.__class__(
4302 klass=self.__class__,
4303 decode_path=decode_path,
4306 l, llen, v = 0, 1, lv[1:]
4308 except DecodeError as err:
4309 raise err.__class__(
4311 klass=self.__class__,
4312 decode_path=decode_path,
4316 raise NotEnoughData(
4317 "encoded length is longer than data",
4318 klass=self.__class__,
4319 decode_path=decode_path,
4323 v, tail = v[:l], v[l:]
4325 sub_offset = offset + tlen + llen
4327 for name, spec in self.specs.items():
4328 if spec.optional and (
4329 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4333 sub_decode_path = decode_path + (name,)
4335 value, v_tail = spec.decode(
4339 decode_path=sub_decode_path,
4347 defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
4348 if defined is not None:
4349 defined_by, defined_spec = defined
4350 if issubclass(value.__class__, SequenceOf):
4351 for i, _value in enumerate(value):
4352 sub_sub_decode_path = sub_decode_path + (
4354 DecodePathDefBy(defined_by),
4356 defined_value, defined_tail = defined_spec.decode(
4357 memoryview(bytes(_value)),
4359 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4360 if value.expled else (value.tlen + value.llen)
4363 decode_path=sub_sub_decode_path,
4366 if len(defined_tail) > 0:
4369 klass=self.__class__,
4370 decode_path=sub_sub_decode_path,
4373 _value.defined = (defined_by, defined_value)
4375 defined_value, defined_tail = defined_spec.decode(
4376 memoryview(bytes(value)),
4378 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4379 if value.expled else (value.tlen + value.llen)
4382 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4385 if len(defined_tail) > 0:
4388 klass=self.__class__,
4389 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4392 value.defined = (defined_by, defined_value)
4394 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4396 sub_offset += value_len
4398 if spec.default is not None and value == spec.default:
4399 if ctx.get("strict_default_existence", False):
4401 "DEFAULT value met",
4402 klass=self.__class__,
4403 decode_path=sub_decode_path,
4408 values[name] = value
4410 spec_defines = getattr(spec, "defines", ())
4411 if len(spec_defines) == 0:
4412 defines_by_path = ctx.get("defines_by_path", ())
4413 if len(defines_by_path) > 0:
4414 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4415 if spec_defines is not None and len(spec_defines) > 0:
4416 for rel_path, schema in spec_defines:
4417 defined = schema.get(value, None)
4418 if defined is not None:
4419 ctx.setdefault("defines", []).append((
4420 abs_decode_path(sub_decode_path[:-1], rel_path),
4424 if v[:EOC_LEN].tobytes() != EOC:
4427 klass=self.__class__,
4428 decode_path=decode_path,
4436 klass=self.__class__,
4437 decode_path=decode_path,
4440 obj = self.__class__(
4444 default=self.default,
4445 optional=self.optional,
4446 _decoded=(offset, llen, vlen),
4449 obj.lenindef = lenindef
4453 value = pp_console_row(next(self.pps()))
4455 for name in self.specs:
4456 _value = self._value.get(name)
4459 cols.append(repr(_value))
4460 return "%s[%s]" % (value, ", ".join(cols))
4462 def pps(self, decode_path=()):
4464 asn1_type_name=self.asn1_type_name,
4465 obj_name=self.__class__.__name__,
4466 decode_path=decode_path,
4467 optional=self.optional,
4468 default=self == self.default,
4469 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4470 expl=None if self._expl is None else tag_decode(self._expl),
4475 expl_offset=self.expl_offset if self.expled else None,
4476 expl_tlen=self.expl_tlen if self.expled else None,
4477 expl_llen=self.expl_llen if self.expled else None,
4478 expl_vlen=self.expl_vlen if self.expled else None,
4480 for name in self.specs:
4481 value = self._value.get(name)
4484 yield value.pps(decode_path=decode_path + (name,))
4487 class Set(Sequence):
4488 """``SET`` structure type
4490 Its usage is identical to :py:class:`pyderasn.Sequence`.
4493 tag_default = tag_encode(form=TagFormConstructed, num=17)
4494 asn1_type_name = "SET"
4497 raws = self._encoded_values()
4500 return b"".join((self.tag, len_encode(len(v)), v))
4502 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4504 t, tlen, lv = tag_strip(tlv)
4505 except DecodeError as err:
4506 raise err.__class__(
4508 klass=self.__class__,
4509 decode_path=decode_path,
4514 klass=self.__class__,
4515 decode_path=decode_path,
4522 l, llen, v = len_decode(lv)
4523 except LenIndefForm as err:
4524 if not ctx.get("bered", False):
4525 raise err.__class__(
4527 klass=self.__class__,
4528 decode_path=decode_path,
4531 l, llen, v = 0, 1, lv[1:]
4533 except DecodeError as err:
4534 raise err.__class__(
4536 klass=self.__class__,
4537 decode_path=decode_path,
4541 raise NotEnoughData(
4542 "encoded length is longer than data",
4543 klass=self.__class__,
4547 v, tail = v[:l], v[l:]
4549 sub_offset = offset + tlen + llen
4551 specs_items = self.specs.items
4553 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4555 for name, spec in specs_items():
4556 sub_decode_path = decode_path + (name,)
4562 decode_path=sub_decode_path,
4571 klass=self.__class__,
4572 decode_path=decode_path,
4575 value, v_tail = spec.decode(
4579 decode_path=sub_decode_path,
4582 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4583 sub_offset += value_len
4586 if spec.default is None or value != spec.default: # pragma: no cover
4587 # SeqMixing.test_encoded_default_accepted covers that place
4588 values[name] = value
4589 obj = self.__class__(
4593 default=self.default,
4594 optional=self.optional,
4595 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
4600 msg="not all values are ready",
4601 klass=self.__class__,
4602 decode_path=decode_path,
4605 obj.lenindef = lenindef
4606 return obj, (v[EOC_LEN:] if lenindef else tail)
4609 class SequenceOf(Obj):
4610 """``SEQUENCE OF`` sequence type
4612 For that kind of type you must specify the object it will carry on
4613 (bounds are for example here, not required)::
4615 class Ints(SequenceOf):
4620 >>> ints.append(Integer(123))
4621 >>> ints.append(Integer(234))
4623 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
4624 >>> [int(i) for i in ints]
4626 >>> ints.append(Integer(345))
4627 Traceback (most recent call last):
4628 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
4631 >>> ints[1] = Integer(345)
4633 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
4635 Also you can initialize sequence with preinitialized values:
4637 >>> ints = Ints([Integer(123), Integer(234)])
4639 __slots__ = ("spec", "_bound_min", "_bound_max")
4640 tag_default = tag_encode(form=TagFormConstructed, num=16)
4641 asn1_type_name = "SEQUENCE OF"
4654 super(SequenceOf, self).__init__(
4662 schema = getattr(self, "schema", None)
4664 raise ValueError("schema must be specified")
4666 self._bound_min, self._bound_max = getattr(
4670 ) if bounds is None else bounds
4672 if value is not None:
4673 self._value = self._value_sanitize(value)
4674 if default is not None:
4675 default_value = self._value_sanitize(default)
4676 default_obj = self.__class__(
4681 default_obj._value = default_value
4682 self.default = default_obj
4684 self._value = default_obj.copy()._value
4686 def _value_sanitize(self, value):
4687 if issubclass(value.__class__, SequenceOf):
4688 value = value._value
4689 elif hasattr(value, "__iter__"):
4692 raise InvalidValueType((self.__class__, iter))
4693 if not self._bound_min <= len(value) <= self._bound_max:
4694 raise BoundsError(self._bound_min, len(value), self._bound_max)
4696 if not isinstance(v, self.spec.__class__):
4697 raise InvalidValueType((self.spec.__class__,))
4702 return all(v.ready for v in self._value)
4705 obj = self.__class__(schema=self.spec)
4706 obj._bound_min = self._bound_min
4707 obj._bound_max = self._bound_max
4709 obj._expl = self._expl
4710 obj.default = self.default
4711 obj.optional = self.optional
4712 obj.offset = self.offset
4713 obj.llen = self.llen
4714 obj.vlen = self.vlen
4715 obj._value = [v.copy() for v in self._value]
4718 def __eq__(self, their):
4719 if isinstance(their, self.__class__):
4721 self.spec == their.spec and
4722 self.tag == their.tag and
4723 self._expl == their._expl and
4724 self._value == their._value
4726 if hasattr(their, "__iter__"):
4727 return self._value == list(their)
4739 return self.__class__(
4743 (self._bound_min, self._bound_max)
4744 if bounds is None else bounds
4746 impl=self.tag if impl is None else impl,
4747 expl=self._expl if expl is None else expl,
4748 default=self.default if default is None else default,
4749 optional=self.optional if optional is None else optional,
4752 def __contains__(self, key):
4753 return key in self._value
4755 def append(self, value):
4756 if not isinstance(value, self.spec.__class__):
4757 raise InvalidValueType((self.spec.__class__,))
4758 if len(self._value) + 1 > self._bound_max:
4761 len(self._value) + 1,
4764 self._value.append(value)
4767 self._assert_ready()
4768 return iter(self._value)
4771 self._assert_ready()
4772 return len(self._value)
4774 def __setitem__(self, key, value):
4775 if not isinstance(value, self.spec.__class__):
4776 raise InvalidValueType((self.spec.__class__,))
4777 self._value[key] = self.spec(value=value)
4779 def __getitem__(self, key):
4780 return self._value[key]
4782 def _encoded_values(self):
4783 return [v.encode() for v in self._value]
4786 v = b"".join(self._encoded_values())
4787 return b"".join((self.tag, len_encode(len(v)), v))
4789 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4791 t, tlen, lv = tag_strip(tlv)
4792 except DecodeError as err:
4793 raise err.__class__(
4795 klass=self.__class__,
4796 decode_path=decode_path,
4801 klass=self.__class__,
4802 decode_path=decode_path,
4809 l, llen, v = len_decode(lv)
4810 except LenIndefForm as err:
4811 if not ctx.get("bered", False):
4812 raise err.__class__(
4814 klass=self.__class__,
4815 decode_path=decode_path,
4818 l, llen, v = 0, 1, lv[1:]
4820 except DecodeError as err:
4821 raise err.__class__(
4823 klass=self.__class__,
4824 decode_path=decode_path,
4828 raise NotEnoughData(
4829 "encoded length is longer than data",
4830 klass=self.__class__,
4831 decode_path=decode_path,
4835 v, tail = v[:l], v[l:]
4837 sub_offset = offset + tlen + llen
4841 if lenindef and v[:EOC_LEN].tobytes() == EOC:
4843 value, v_tail = spec.decode(
4847 decode_path=decode_path + (str(len(_value)),),
4850 value_len = value.expl_tlvlen if value.expled else value.tlvlen
4851 sub_offset += value_len
4854 _value.append(value)
4855 obj = self.__class__(
4858 bounds=(self._bound_min, self._bound_max),
4861 default=self.default,
4862 optional=self.optional,
4863 _decoded=(offset, llen, vlen),
4865 obj.lenindef = lenindef
4866 return obj, (v[EOC_LEN:] if lenindef else tail)
4870 pp_console_row(next(self.pps())),
4871 ", ".join(repr(v) for v in self._value),
4874 def pps(self, decode_path=()):
4876 asn1_type_name=self.asn1_type_name,
4877 obj_name=self.__class__.__name__,
4878 decode_path=decode_path,
4879 optional=self.optional,
4880 default=self == self.default,
4881 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4882 expl=None if self._expl is None else tag_decode(self._expl),
4887 expl_offset=self.expl_offset if self.expled else None,
4888 expl_tlen=self.expl_tlen if self.expled else None,
4889 expl_llen=self.expl_llen if self.expled else None,
4890 expl_vlen=self.expl_vlen if self.expled else None,
4892 for i, value in enumerate(self._value):
4893 yield value.pps(decode_path=decode_path + (str(i),))
4896 class SetOf(SequenceOf):
4897 """``SET OF`` sequence type
4899 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
4902 tag_default = tag_encode(form=TagFormConstructed, num=17)
4903 asn1_type_name = "SET OF"
4906 raws = self._encoded_values()
4909 return b"".join((self.tag, len_encode(len(v)), v))
4912 def obj_by_path(pypath): # pragma: no cover
4913 """Import object specified as string Python path
4915 Modules must be separated from classes/functions with ``:``.
4917 >>> obj_by_path("foo.bar:Baz")
4918 <class 'foo.bar.Baz'>
4919 >>> obj_by_path("foo.bar:Baz.boo")
4920 <classmethod 'foo.bar.Baz.boo'>
4922 mod, objs = pypath.rsplit(":", 1)
4923 from importlib import import_module
4924 obj = import_module(mod)
4925 for obj_name in objs.split("."):
4926 obj = getattr(obj, obj_name)
4930 def generic_decoder(): # pragma: no cover
4931 # All of this below is a big hack with self references
4932 choice = PrimitiveTypes()
4933 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
4934 choice.specs["SetOf"] = SetOf(schema=choice)
4936 choice.specs["SequenceOf%d" % i] = SequenceOf(
4940 choice.specs["Any"] = Any()
4942 # Class name equals to type name, to omit it from output
4943 class SEQUENCEOF(SequenceOf):
4947 def pprint_any(obj, oids=None, with_colours=False):
4948 def _pprint_pps(pps):
4950 if hasattr(pp, "_fields"):
4951 if pp.asn1_type_name == Choice.asn1_type_name:
4953 pp_kwargs = pp._asdict()
4954 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
4955 pp = _pp(**pp_kwargs)
4956 yield pp_console_row(
4961 with_colours=with_colours,
4963 for row in pp_console_blob(pp):
4966 for row in _pprint_pps(pp):
4968 return "\n".join(_pprint_pps(obj.pps()))
4969 return SEQUENCEOF(), pprint_any
4972 def main(): # pragma: no cover
4974 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
4975 parser.add_argument(
4979 help="Skip that number of bytes from the beginning",
4981 parser.add_argument(
4983 help="Python path to dictionary with OIDs",
4985 parser.add_argument(
4987 help="Python path to schema definition to use",
4989 parser.add_argument(
4990 "--defines-by-path",
4991 help="Python path to decoder's defines_by_path",
4993 parser.add_argument(
4995 type=argparse.FileType("rb"),
4996 help="Path to DER file you want to decode",
4998 args = parser.parse_args()
4999 args.DERFile.seek(args.skip)
5000 der = memoryview(args.DERFile.read())
5001 args.DERFile.close()
5002 oids = obj_by_path(args.oids) if args.oids else {}
5004 schema = obj_by_path(args.schema)
5005 from functools import partial
5006 pprinter = partial(pprint, big_blobs=True)
5008 schema, pprinter = generic_decoder()
5009 ctx = {"bered": True}
5010 if args.defines_by_path is not None:
5011 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5012 obj, tail = schema().decode(der, ctx=ctx)
5016 with_colours=True if environ.get("NO_COLOR") is None else False,
5019 print("\nTrailing data: %s" % hexenc(tail))
5022 if __name__ == "__main__":